Coverage Report

Created: 2026-06-30 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/Wt/WWebSocketResource.h
Line
Count
Source
1
#ifndef WT_WWEBSOCKETRESOURCE_H_
2
#define WT_WWEBSOCKETRESOURCE_H_
3
4
#include "Wt/AsioWrapper/asio.hpp"
5
6
#include "Wt/Http/Request.h"
7
#include "Wt/Http/Response.h"
8
9
#include "Wt/WResource.h"
10
11
#include <mutex>
12
13
namespace http {
14
  namespace server {
15
    class Connection;
16
  }
17
}
18
19
namespace Wt {
20
class WebSocketConnection;
21
class WWebSocketConnection;
22
class WWebSocketResource;
23
24
/* Wt internal
25
 *
26
 * This resource is used to route the incoming request to the right
27
 * WWebSocketResource. When a new request comes in, that wishes to set up
28
 * a new WebSocket connection this will contain some websocket-specific
29
 * headers. This request is handled in the normal event loop of Wt. This
30
 * will ensure that WResource::handleRequest() is called.
31
 *
32
 * When a WResource is exposed by the WServer or WApplication, its path
33
 * can be used in requests, and the instance will correctly handle the
34
 * request to the resource. In this specific case, this will ensure that
35
 * the related WWebSocketResource is retrieved and can be used to set up
36
 * a new WWebSocketConnection.
37
 */
38
class WT_API WebSocketHandlerResource final : public WResource
39
{
40
public:
41
  explicit WebSocketHandlerResource(WWebSocketResource* resource);
42
  ~WebSocketHandlerResource();
43
44
  void moveSocket(const Http::Request& request, const std::shared_ptr<WebSocketConnection>& socketConnection);
45
46
protected:
47
  void handleRequest(const Http::Request& request, Http::Response& response) final;
48
49
private:
50
  WWebSocketResource* resource_ = nullptr;
51
};
52
53
/*! \class WWebSocketResource Wt/WWebSocketResource.h
54
 *  \brief A resource that manages a WebSocket endpoint
55
 *
56
 * This resource enables the creation of endpoints that can be accessed
57
 * to set up WebSocket connections. This implementation adheres to
58
 * <a href="https://datatracker.ietf.org/doc/html/rfc6455">RFC-6455</a>.
59
 * The implementation does not support Sec-WebSocket-Protocol or
60
 * Sec-WebSocket-Extension.
61
 *
62
 * Like WResource, a WWebSocketResource can be global (aka static) or
63
 * session-private (aka dynamic).
64
 * A global WWebSocketResource is deployed at a fixed URL by calling
65
 * WServer::addResource(). It is accessible by anybody that has access
66
 * to its URL. It is the responsibility of the developer to implement a
67
 * suitable access control mechanism if needed.
68
69
 * The private version of this is an endpoint unique for a session. It is
70
 * added to the widget tree of a WApplication instance (and
71
 * WServer::addResource() is thus not used). The URL is generated by %Wt
72
 * and access is protected to the session by using a session-specific
73
 * secret as configured by the 'tracking' configuration option in
74
 * wt_config.xml. Its path can still be managed, and set to any valid
75
 * name.
76
 *
77
 * This class enables an endpoint to be created (and moved, using
78
 * setInternalPath()).
79
 *
80
 * While it has <code>Resource</code> in its name, it is not actually a
81
 * WResource, and has no WResource::handleRequest() method.
82
 *
83
 * When a new connection comes in to the WWebSocketResource, that is a
84
 * valid WebSocket request, asking to be upgraded, handleConnect() will
85
 * be called. This creates a new instance of a WWebSocketConnection. This
86
 * connection can be used to send and receive data over. Upon creation,
87
 * all state that is configured on the resource is also put on the
88
 * connection, meaning the limits on frame and message size, the ping
89
 * delay and timeout, and whether the update lock is taken on updates.
90
 *
91
 * To use this resource, one has to implement the handleConnect()
92
 * method, which should create a WWebSocketConnection. Or any specialized
93
 * class that inherits from it.
94
 *
95
 * \if cpp
96
 * \code
97
 * class MyWebSocketResource final : public Wt::WWebSocketResource
98
 * {
99
 * public:
100
 *   std::unique_ptr<Wt::WWebSocketConnection> handleConnect(const Wt::Http::Request& request) final
101
 *   {
102
 *     auto connection = std::make_unique<Wt::WWebSocketConnection>(this, Wt::WServer::instance()->ioService());
103
 *     newConnectionMade_.emit(connection.get());
104
 *     return connection;
105
 *   }
106
 * };
107
 * \endcode
108
 * \endif
109
 *
110
 * \note This class is only supported when using the wthttp frontend.
111
 */
112
class WT_API WWebSocketResource : public WObject
113
{
114
public:
115
  //! Default constructor
116
  WWebSocketResource();
117
118
  //! Destructor
119
  virtual ~WWebSocketResource();
120
121
  /*! \brief Handles a new connection to the resource
122
   *
123
   * When a new connection is being made to the resource, this function
124
   * is called, to create a new WWebSocketConnection.
125
   *
126
   * The connection can be used to interact with the WebSocket stream.
127
   * The \p request that is passed to it is purely indicative. It allows
128
   * for inspection of the request and its headers, so that additional
129
   * information from it may be used further.
130
   */
131
  virtual std::unique_ptr<WWebSocketConnection> handleConnect(const Http::Request& request) = 0;
132
133
  /*! \brief Shuts down the resource
134
   *
135
   * The resource is closed down, meaning that all its active connections
136
   * are immediately closed. This is not a completely graceful shutdown,
137
   * in that clients (on the other side of the connection) are not
138
   * notified of the closing with a close frame. This could potentially
139
   * lead to the resource having to wait a while for an actual shutdown.
140
   *
141
   * Since this function is called in WServer::stop(), in case the %Wt
142
   * http library is used, we want an immediate shutdown here.
143
   */
144
  virtual void shutdown();
145
146
  /*! \brief Configures the internal path used for the resource
147
   *
148
   * This path is only useful for private resources. It defines the
149
   * internal path portion of the URL of the resource.
150
   *
151
   * If no path is configured, a URL will be generated by the library.
152
   */
153
  void setInternalPath(const std::string& path);
154
155
  /*! \brief Retrieves the current internal path
156
   *
157
   * \sa setInternalPath
158
   */
159
  std::string internalPath() const;
160
161
  /*! \brief Retrieves the current URL
162
   *
163
   * This method returns the full URL to be used to connect to this
164
   * resource.
165
   *
166
   * For global resources, this is the last URL at which the resource is
167
   * added to WServer. For private resources, this URL is influenced by
168
   * setInternalPath() and additional query parameters to provide session
169
   * secrecy.
170
   *
171
   * \sa setInternalPath
172
   */
173
  std::string url() const;
174
175
176
  /*! \brief Sets the maximum received frame and message sizes
177
   *
178
   * A WebSocket frame is a single transaction unit. It contains a header
179
   * and data. The header itself will contain information on the frame,
180
   * and specify how long it is. If this exceeds the limit, the received
181
   * frame is discarded.
182
   *
183
   * Likewise, when a received message consists of multiple frames, the
184
   * message as a whole is discarded if it exceeds the message limit.
185
   *
186
   * By default the maximum frame size is 10MB (1024 * 1024 * 10), and
187
   * the maxmimum message size is 5 times as big (50MB). This can be
188
   * increased to any arbitrary value, but the absolute maximum frame
189
   * size of a WebSocket is still enforced, which is 2 ^ 63 bytes.
190
   */
191
  void setMaximumReceivedSize(size_t frame, size_t message);
192
193
  /*! \brief Sets whether the resource takes the update lock on events
194
   *
195
   * Setting this to \p true will only affect private resources. By
196
   * default it is enabled.
197
   *
198
   * If enabled, handleConnect(), WWebSocketResource::handleMessage(),
199
   * WWebSocketResource::handleError(), WWebSocketResource::closed(), and
200
   * WWebSocketResource::done() are all called while holding the
201
   * WApplication::UpdateLock. The widget tree can thus safely be
202
   * accessed and modified from these functions.
203
   */
204
  void setTakesUpdateLock(bool canTake);
205
206
  /*! \brief Sets the ping-pong configuration.
207
   *
208
   * The ping-pong system is a way to ensure that a connection remains
209
   * active, and that both parties are still committed to the
210
   * interaction. Any of both parties can send a ping frame, to which
211
   * the other has to respond with a pong as soon as reasonable. They do
212
   * not have to drop current pending frames, but need to ensure that the
213
   * pong is sent out within a reasonable small amount of time.
214
   *
215
   * This function allows the managing of this functionality. The first
216
   * parameter \p intervalSeconds will set every how often a ping frame
217
   * is sent out from the server to a connected client. The second
218
   * parameter \p timeoutSeconds indicates how long the client has to
219
   * respond. If they do not respond within the set limit, the connection
220
   * will be considered dropped, and will be terminated. Both of these
221
   * parameters are in seconds.
222
   *
223
   * By default the used values are 30 seconds for the ping interval, and
224
   * 60 seconds for the timeout.
225
   *
226
   * This system can be disabled by setting \p intervalSeconds to 0.
227
   */
228
  void setPingTimeout(int intervalSeconds, int timeoutSeconds);
229
230
  /*! \brief Returns the maximum size of a single received frame.
231
   *
232
   * \sa setMaximumReceivedSize
233
   */
234
0
  size_t maximumReceivedFrameSize() const { return frameSize_; }
235
236
  /*! \brief Returns the maximum received size of a single message.
237
   *
238
   * \sa setMaximumReceivedSize
239
   *
240
   * \note A single message can span multiple frames.
241
   */
242
0
  size_t maximumReceivedMessageSize() const { return messageSize_; }
243
244
  /*! \brief Returns whether the application update lock is used.
245
   *
246
   * \sa setTakesUpdateLock
247
   */
248
0
  bool takesUpdateLock() const { return takesUpdateLock_; }
249
250
  /*! \brief Returns the interval (in seconds) between ping frames sent.
251
   *
252
   * \sa setPingTimeout
253
   */
254
0
  int pingInterval() const { return pingInterval_; }
255
256
  /*! \brief Returns the maximum duration (in second) between sent ping
257
   * frames, and received pong frames.
258
   *
259
   * \sa setPingTimeout
260
   */
261
0
  int pingTimeout() const { return pingTimeout_; }
262
263
  // Wt internal
264
0
  std::shared_ptr<WebSocketHandlerResource> handleResource() const { return resource_; }
265
266
private:
267
  std::shared_ptr<WebSocketHandlerResource> resource_;
268
269
  std::recursive_mutex clientsMutex_;
270
  std::vector<std::unique_ptr<WWebSocketConnection>> clients_;
271
272
  WWebSocketConnection* registerConnection(std::unique_ptr<WWebSocketConnection> connection);
273
  void removeConnection(WWebSocketConnection* connection);
274
275
  size_t frameSize_;
276
  size_t messageSize_;
277
278
  bool takesUpdateLock_;
279
280
  int pingInterval_;
281
  int pingTimeout_;
282
283
  WApplication* app_ = nullptr;
284
285
  friend class WebSocketHandlerResource;
286
  friend class WWebSocketConnection;
287
};
288
}
289
#endif // WT_WWEBSOCKETRESOURCE_H_