/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_ |