opal-up 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -20,483 +20,578 @@
20
20
 
21
21
  /* This class defines the main behavior of HTTP and emits various events */
22
22
 
23
- #include "Loop.h"
23
+ #include "AsyncSocket.h"
24
24
  #include "HttpContextData.h"
25
25
  #include "HttpResponseData.h"
26
- #include "AsyncSocket.h"
26
+ #include "Loop.h"
27
27
  #include "WebSocketData.h"
28
28
 
29
- #include <string_view>
30
- #include <iostream>
31
29
  #include "MoveOnlyFunction.h"
30
+ #include <iostream>
31
+ #include <string_view>
32
32
 
33
33
  namespace uWS {
34
- template<bool> struct HttpResponse;
35
-
36
- template <bool SSL>
37
- struct HttpContext {
38
- template<bool> friend struct TemplatedApp;
39
- template<bool> friend struct HttpResponse;
40
- private:
41
- HttpContext() = delete;
42
-
43
- /* Maximum delay allowed until an HTTP connection is terminated due to outstanding request or rejected data (slow loris protection) */
44
- static const int HTTP_IDLE_TIMEOUT_S = 10;
34
+ template <bool> struct HttpResponse;
45
35
 
46
- /* Minimum allowed receive throughput per second (clients uploading less than 16kB/sec get dropped) */
47
- static const int HTTP_RECEIVE_THROUGHPUT_BYTES = 16 * 1024;
36
+ template <bool SSL> struct HttpContext {
37
+ template <bool> friend struct TemplatedApp;
38
+ template <bool> friend struct HttpResponse;
48
39
 
49
- us_loop_t *getLoop() {
50
- return us_socket_context_loop(SSL, getSocketContext());
51
- }
52
-
53
- us_socket_context_t *getSocketContext() {
54
- return (us_socket_context_t *) this;
55
- }
56
-
57
- static us_socket_context_t *getSocketContext(us_socket_t *s) {
58
- return (us_socket_context_t *) us_socket_context(SSL, s);
59
- }
60
-
61
- HttpContextData<SSL> *getSocketContextData() {
62
- return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
63
- }
64
-
65
- static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
66
- return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
67
- }
68
-
69
- /* Init the HttpContext by registering libusockets event handlers */
70
- HttpContext<SSL> *init() {
71
- /* Handle socket connections */
72
- us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) {
73
- /* Any connected socket should timeout until it has a request */
74
- us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
-
76
- /* Init socket ext */
77
- new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
-
79
- /* Call filter */
80
- HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
81
- for (auto &f : httpContextData->filterHandlers) {
82
- f((HttpResponse<SSL> *) s, 1);
83
- }
84
-
85
- return s;
40
+ private:
41
+ HttpContext() = delete;
42
+
43
+ /* Maximum delay allowed until an HTTP connection is terminated due to
44
+ * outstanding request or rejected data (slow loris protection) */
45
+ static const int HTTP_IDLE_TIMEOUT_S = 10;
46
+
47
+ /* Minimum allowed receive throughput per second (clients uploading less than
48
+ * 16kB/sec get dropped) */
49
+ static const int HTTP_RECEIVE_THROUGHPUT_BYTES = 16 * 1024;
50
+
51
+ us_loop_t *getLoop() {
52
+ return us_socket_context_loop(SSL, getSocketContext());
53
+ }
54
+
55
+ us_socket_context_t *getSocketContext() {
56
+ return (us_socket_context_t *)this;
57
+ }
58
+
59
+ static us_socket_context_t *getSocketContext(us_socket_t *s) {
60
+ return (us_socket_context_t *)us_socket_context(SSL, s);
61
+ }
62
+
63
+ HttpContextData<SSL> *getSocketContextData() {
64
+ return (HttpContextData<SSL> *)us_socket_context_ext(SSL,
65
+ getSocketContext());
66
+ }
67
+
68
+ static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
69
+ return (HttpContextData<SSL> *)us_socket_context_ext(SSL,
70
+ getSocketContext(s));
71
+ }
72
+
73
+ /* Init the HttpContext by registering libusockets event handlers */
74
+ HttpContext<SSL> *init() {
75
+ /* Handle socket connections */
76
+ us_socket_context_on_open(
77
+ SSL, getSocketContext(),
78
+ [](us_socket_t *s, int /*is_client*/, char * /*ip*/,
79
+ int /*ip_length*/) {
80
+ /* Any connected socket should timeout until it has a request */
81
+ us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
82
+
83
+ /* Init socket ext */
84
+ new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
85
+
86
+ /* Call filter */
87
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
88
+ for (auto &f : httpContextData->filterHandlers) {
89
+ f((HttpResponse<SSL> *)s, 1);
90
+ }
91
+
92
+ return s;
86
93
  });
87
94
 
88
- /* Handle socket disconnections */
89
- us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
90
- /* Get socket ext */
91
- HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
92
-
93
- /* Call filter */
94
- HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
- for (auto &f : httpContextData->filterHandlers) {
96
- f((HttpResponse<SSL> *) s, -1);
97
- }
98
-
99
- /* Signal broken HTTP request only if we have a pending request */
100
- if (httpResponseData->onAborted) {
101
- httpResponseData->onAborted();
102
- }
103
-
104
- /* Destruct socket ext */
105
- httpResponseData->~HttpResponseData<SSL>();
106
-
107
- return s;
95
+ /* Handle socket disconnections */
96
+ us_socket_context_on_close(
97
+ SSL, getSocketContext(),
98
+ [](us_socket_t *s, int /*code*/, void * /*reason*/) {
99
+ /* Get socket ext */
100
+ HttpResponseData<SSL> *httpResponseData =
101
+ (HttpResponseData<SSL> *)us_socket_ext(SSL, s);
102
+
103
+ /* Call filter */
104
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
105
+ for (auto &f : httpContextData->filterHandlers) {
106
+ f((HttpResponse<SSL> *)s, -1);
107
+ }
108
+
109
+ /* Signal broken HTTP request only if we have a pending request */
110
+ if (httpResponseData->onAborted) {
111
+ httpResponseData->onAborted();
112
+ }
113
+
114
+ /* Destruct socket ext */
115
+ httpResponseData->~HttpResponseData<SSL>();
116
+
117
+ return s;
108
118
  });
109
119
 
110
- /* Handle HTTP data streams */
111
- us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
120
+ /* Handle HTTP data streams */
121
+ us_socket_context_on_data(
122
+ SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
123
+ // total overhead is about 210k down to 180k
124
+ // ~210k req/sec is the original perf with write in data
125
+ // ~200k req/sec is with cork and formatting
126
+ // ~190k req/sec is with http parsing
127
+ // ~180k - 190k req/sec is with varying routing
112
128
 
113
- // total overhead is about 210k down to 180k
114
- // ~210k req/sec is the original perf with write in data
115
- // ~200k req/sec is with cork and formatting
116
- // ~190k req/sec is with http parsing
117
- // ~180k - 190k req/sec is with varying routing
129
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
118
130
 
119
- HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
120
-
121
- /* Do not accept any data while in shutdown state */
122
- if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
123
- return s;
124
- }
131
+ /* Do not accept any data while in shutdown state */
132
+ if (us_socket_is_shut_down(SSL, (us_socket_t *)s)) {
133
+ return s;
134
+ }
125
135
 
126
- HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
136
+ HttpResponseData<SSL> *httpResponseData =
137
+ (HttpResponseData<SSL> *)us_socket_ext(SSL, s);
127
138
 
128
- /* Cork this socket */
129
- ((AsyncSocket<SSL> *) s)->cork();
139
+ /* Cork this socket */
140
+ ((AsyncSocket<SSL> *)s)->cork();
130
141
 
131
- /* Mark that we are inside the parser now */
132
- httpContextData->isParsingHttp = true;
142
+ /* Mark that we are inside the parser now */
143
+ httpContextData->isParsingHttp = true;
133
144
 
134
- // clients need to know the cursor after http parse, not servers!
135
- // how far did we read then? we need to know to continue with websocket parsing data? or?
145
+ // clients need to know the cursor after http parse, not servers!
146
+ // how far did we read then? we need to know to continue with
147
+ // websocket parsing data? or?
136
148
 
137
- void *proxyParser = nullptr;
149
+ void *proxyParser = nullptr;
138
150
  #ifdef UWS_WITH_PROXY
139
- proxyParser = &httpResponseData->proxyParser;
151
+ proxyParser = &httpResponseData->proxyParser;
140
152
  #endif
141
153
 
142
- /* The return value is entirely up to us to interpret. The HttpParser only care for whether the returned value is DIFFERENT or not from passed user */
143
- auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
144
- /* For every request we reset the timeout and hang until user makes action */
145
- /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
146
- us_socket_timeout(SSL, (us_socket_t *) s, 0);
154
+ /* The return value is entirely up to us to interpret. The HttpParser
155
+ * only care for whether the returned value is DIFFERENT or not from
156
+ * passed user */
157
+ auto [err, returnedSocket] = httpResponseData->consumePostPadded(
158
+ data, (unsigned int)length, s, proxyParser,
159
+ [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
160
+ /* For every request we reset the timeout and hang until user
161
+ * makes action */
162
+ /* Warning: if we are in shutdown state, resetting the timer is
163
+ * a security issue! */
164
+ us_socket_timeout(SSL, (us_socket_t *)s, 0);
147
165
 
148
166
  /* Reset httpResponse */
149
- HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
167
+ HttpResponseData<SSL> *httpResponseData =
168
+ (HttpResponseData<SSL> *)us_socket_ext(SSL,
169
+ (us_socket_t *)s);
150
170
  httpResponseData->offset = 0;
151
171
 
152
- /* Are we not ready for another request yet? Terminate the connection. */
153
- if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
154
- us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
155
- return nullptr;
172
+ /* Are we not ready for another request yet? Terminate the
173
+ * connection. */
174
+ if (httpResponseData->state &
175
+ HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
176
+ us_socket_close(SSL, (us_socket_t *)s, 0, nullptr);
177
+ return nullptr;
156
178
  }
157
179
 
158
180
  /* Mark pending request and emit it */
159
- httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
160
-
161
- /* Mark this response as connectionClose if ancient or connection: close */
162
- if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
163
- httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
181
+ httpResponseData->state =
182
+ HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
183
+
184
+ /* Mark this response as connectionClose if ancient or
185
+ * connection: close */
186
+ if (httpRequest->isAncient() ||
187
+ httpRequest->getHeader("connection").length() == 5) {
188
+ httpResponseData->state |=
189
+ HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
164
190
  }
165
191
 
166
192
  /* Select the router based on SNI (only possible for SSL) */
167
193
  auto *selectedRouter = &httpContextData->router;
168
194
  if constexpr (SSL) {
169
- void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
170
- if (domainRouter) {
171
- selectedRouter = (decltype(selectedRouter)) domainRouter;
172
- }
195
+ void *domainRouter = us_socket_server_name_userdata(
196
+ SSL, (struct us_socket_t *)s);
197
+ if (domainRouter) {
198
+ selectedRouter = (decltype(selectedRouter))domainRouter;
199
+ }
173
200
  }
174
201
 
175
202
  /* Route the method and URL */
176
- selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
177
- if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
178
- /* We have to force close this socket as we have no handler for it */
179
- us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
180
- return nullptr;
203
+ selectedRouter->getUserData() = {(HttpResponse<SSL> *)s,
204
+ httpRequest};
205
+ if (!selectedRouter->route(
206
+ httpRequest->getCaseSensitiveMethod(),
207
+ httpRequest->getUrl())) {
208
+ /* We have to force close this socket as we have no handler
209
+ * for it */
210
+ us_socket_close(SSL, (us_socket_t *)s, 0, nullptr);
211
+ return nullptr;
181
212
  }
182
213
 
183
- /* First of all we need to check if this socket was deleted due to upgrade */
214
+ /* First of all we need to check if this socket was deleted due
215
+ * to upgrade */
184
216
  if (httpContextData->upgradedWebSocket) {
185
- /* We differ between closed and upgraded below */
186
- return nullptr;
217
+ /* We differ between closed and upgraded below */
218
+ return nullptr;
187
219
  }
188
220
 
189
221
  /* Was the socket closed? */
190
- if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
191
- return nullptr;
222
+ if (us_socket_is_closed(SSL, (struct us_socket_t *)s)) {
223
+ return nullptr;
192
224
  }
193
225
 
194
226
  /* We absolutely have to terminate parsing if shutdown */
195
- if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
196
- return nullptr;
227
+ if (us_socket_is_shut_down(SSL, (us_socket_t *)s)) {
228
+ return nullptr;
197
229
  }
198
230
 
199
- /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
200
- if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
201
- /* Throw exception here? */
202
- std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl;
203
- std::terminate();
231
+ /* Returning from a request handler without responding or
232
+ * attaching an onAborted handler is ill-use */
233
+ if (!((HttpResponse<SSL> *)s)->hasResponded() &&
234
+ !httpResponseData->onAborted) {
235
+ /* Throw exception here? */
236
+ std::cerr << "Error: Returning from a request handler "
237
+ "without responding or attaching an abort "
238
+ "handler is forbidden!"
239
+ << std::endl;
240
+ std::terminate();
204
241
  }
205
242
 
206
- /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
207
- if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
208
- us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
243
+ /* If we have not responded and we have a data handler, we need
244
+ * to timeout to enfore client sending the data */
245
+ if (!((HttpResponse<SSL> *)s)->hasResponded() &&
246
+ httpResponseData->inStream) {
247
+ us_socket_timeout(SSL, (us_socket_t *)s, HTTP_IDLE_TIMEOUT_S);
209
248
  }
210
249
 
211
250
  /* Continue parsing */
212
251
  return s;
213
-
214
- }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * {
252
+ },
253
+ [httpResponseData](void *user, std::string_view data,
254
+ bool fin) -> void * {
215
255
  /* We always get an empty chunk even if there is no data */
216
256
  if (httpResponseData->inStream) {
217
257
 
218
- /* Todo: can this handle timeout for non-post as well? */
219
- if (fin) {
220
- /* If we just got the last chunk (or empty chunk), disable timeout */
221
- us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
222
- } else {
223
- /* We still have some more data coming in later, so reset timeout */
224
- /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
225
- httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
226
- if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
227
- us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
228
- httpResponseData->received_bytes_per_timeout = 0;
229
- }
258
+ /* Todo: can this handle timeout for non-post as well? */
259
+ if (fin) {
260
+ /* If we just got the last chunk (or empty chunk), disable
261
+ * timeout */
262
+ us_socket_timeout(SSL, (struct us_socket_t *)user, 0);
263
+ } else {
264
+ /* We still have some more data coming in later, so reset
265
+ * timeout */
266
+ /* Only reset timeout if we got enough bytes (16kb/sec)
267
+ * since last time we reset here */
268
+ httpResponseData->received_bytes_per_timeout +=
269
+ (unsigned int)data.length();
270
+ if (httpResponseData->received_bytes_per_timeout >=
271
+ HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
272
+ us_socket_timeout(SSL, (struct us_socket_t *)user,
273
+ HTTP_IDLE_TIMEOUT_S);
274
+ httpResponseData->received_bytes_per_timeout = 0;
230
275
  }
276
+ }
231
277
 
232
- /* We might respond in the handler, so do not change timeout after this */
233
- httpResponseData->inStream(data, fin);
234
-
235
- /* Was the socket closed? */
236
- if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
237
- return nullptr;
238
- }
278
+ /* We might respond in the handler, so do not change timeout
279
+ * after this */
280
+ httpResponseData->inStream(data, fin);
239
281
 
240
- /* We absolutely have to terminate parsing if shutdown */
241
- if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
242
- return nullptr;
243
- }
282
+ /* Was the socket closed? */
283
+ if (us_socket_is_closed(SSL, (struct us_socket_t *)user)) {
284
+ return nullptr;
285
+ }
244
286
 
245
- /* If we were given the last data chunk, reset data handler to ensure following
246
- * requests on the same socket won't trigger any previously registered behavior */
247
- if (fin) {
248
- httpResponseData->inStream = nullptr;
249
- }
287
+ /* We absolutely have to terminate parsing if shutdown */
288
+ if (us_socket_is_shut_down(SSL, (us_socket_t *)user)) {
289
+ return nullptr;
290
+ }
291
+
292
+ /* If we were given the last data chunk, reset data handler to
293
+ * ensure following requests on the same socket won't trigger
294
+ * any previously registered behavior */
295
+ if (fin) {
296
+ httpResponseData->inStream = nullptr;
297
+ }
250
298
  }
251
299
  return user;
252
- });
253
-
254
- /* Mark that we are no longer parsing Http */
255
- httpContextData->isParsingHttp = false;
256
-
257
- /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
258
- if (returnedSocket == FULLPTR) {
259
- /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
260
- us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
261
- us_socket_shutdown(SSL, s);
262
- /* Close any socket on HTTP errors */
263
- us_socket_close(SSL, s, 0, nullptr);
264
- /* This just makes the following code act as if the socket was closed from error inside the parser. */
265
- returnedSocket = nullptr;
300
+ });
301
+
302
+ /* Mark that we are no longer parsing Http */
303
+ httpContextData->isParsingHttp = false;
304
+
305
+ /* If we got fullptr that means the parser wants us to close the
306
+ * socket from error (same as calling the errorHandler) */
307
+ if (returnedSocket == FULLPTR) {
308
+ /* For errors, we only deliver them "at most once". We don't care if
309
+ * they get halfways delivered or not. */
310
+ us_socket_write(SSL, s, httpErrorResponses[err].data(),
311
+ (int)httpErrorResponses[err].length(), false);
312
+ us_socket_shutdown(SSL, s);
313
+ /* Close any socket on HTTP errors */
314
+ us_socket_close(SSL, s, 0, nullptr);
315
+ /* This just makes the following code act as if the socket was
316
+ * closed from error inside the parser. */
317
+ returnedSocket = nullptr;
318
+ }
319
+
320
+ /* We need to uncork in all cases, except for nullptr (closed socket,
321
+ * or upgraded socket) */
322
+ if (returnedSocket != nullptr) {
323
+ /* Timeout on uncork failure */
324
+ auto [written, failed] =
325
+ ((AsyncSocket<SSL> *)returnedSocket)->uncork();
326
+ if (failed) {
327
+ /* All Http sockets timeout by this, and this behavior match the
328
+ * one in HttpResponse::cork */
329
+ /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10
330
+ * seconds and both are used the same */
331
+ ((AsyncSocket<SSL> *)s)->timeout(HTTP_IDLE_TIMEOUT_S);
266
332
  }
267
333
 
268
- /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
269
- if (returnedSocket != nullptr) {
270
- /* Timeout on uncork failure */
271
- auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
272
- if (failed) {
273
- /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
274
- /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
275
- ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
276
- }
277
-
278
- /* We need to check if we should close this socket here now */
279
- if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
280
- if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
281
- if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
282
- ((AsyncSocket<SSL> *) s)->shutdown();
283
- /* We need to force close after sending FIN since we want to hinder
284
- * clients from keeping to send their huge data */
285
- ((AsyncSocket<SSL> *) s)->close();
286
- }
287
- }
334
+ /* We need to check if we should close this socket here now */
335
+ if (httpResponseData->state &
336
+ HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
337
+ if ((httpResponseData->state &
338
+ HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
339
+ if (((AsyncSocket<SSL> *)s)->getBufferedAmount() == 0) {
340
+ ((AsyncSocket<SSL> *)s)->shutdown();
341
+ /* We need to force close after sending FIN since we want to
342
+ * hinder clients from keeping to send their huge data */
343
+ ((AsyncSocket<SSL> *)s)->close();
288
344
  }
289
-
290
- return (us_socket_t *) returnedSocket;
345
+ }
291
346
  }
292
347
 
293
- /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
294
- if (httpContextData->upgradedWebSocket) {
295
- /* This path is only for upgraded websockets */
296
- AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
297
-
298
- /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
299
- auto [written, failed] = asyncSocket->uncork();
300
-
301
- /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
302
- if (!failed) {
303
- WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
304
- if (webSocketData->isShuttingDown) {
305
- /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
306
- asyncSocket->shutdown();
307
- }
308
- }
309
-
310
- /* Reset upgradedWebSocket before we return */
311
- httpContextData->upgradedWebSocket = nullptr;
312
-
313
- /* Return the new upgraded websocket */
314
- return (us_socket_t *) asyncSocket;
348
+ return (us_socket_t *)returnedSocket;
349
+ }
350
+
351
+ /* If we upgraded, check here (differ between nullptr close and
352
+ * nullptr upgrade) */
353
+ if (httpContextData->upgradedWebSocket) {
354
+ /* This path is only for upgraded websockets */
355
+ AsyncSocket<SSL> *asyncSocket =
356
+ (AsyncSocket<SSL> *)httpContextData->upgradedWebSocket;
357
+
358
+ /* Uncork here as well (note: what if we failed to uncork and we
359
+ * then pub/sub before we even upgraded?) */
360
+ auto [written, failed] = asyncSocket->uncork();
361
+
362
+ /* If we succeeded in uncorking, check if we have sent WebSocket FIN
363
+ */
364
+ if (!failed) {
365
+ WebSocketData *webSocketData =
366
+ (WebSocketData *)asyncSocket->getAsyncSocketData();
367
+ if (webSocketData->isShuttingDown) {
368
+ /* In that case, also send TCP FIN (this is similar to what we
369
+ * have in ws drain handler) */
370
+ asyncSocket->shutdown();
371
+ }
315
372
  }
316
373
 
317
- /* It is okay to uncork a closed socket and we need to */
318
- ((AsyncSocket<SSL> *) s)->uncork();
319
-
320
- /* We cannot return nullptr to the underlying stack in any case */
321
- return s;
322
- });
374
+ /* Reset upgradedWebSocket before we return */
375
+ httpContextData->upgradedWebSocket = nullptr;
323
376
 
324
- /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */
325
- us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
377
+ /* Return the new upgraded websocket */
378
+ return (us_socket_t *)asyncSocket;
379
+ }
326
380
 
327
- AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
328
- HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
381
+ /* It is okay to uncork a closed socket and we need to */
382
+ ((AsyncSocket<SSL> *)s)->uncork();
329
383
 
330
- /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
331
- if (httpResponseData->onWritable) {
332
- /* We are now writable, so hang timeout again, the user does not have to do anything so we should hang until end or tryEnd rearms timeout */
333
- us_socket_timeout(SSL, s, 0);
334
-
335
- /* We expect the developer to return whether or not write was successful (true).
336
- * If write was never called, the developer should still return true so that we may drain. */
337
- bool success = httpResponseData->callOnWritable(httpResponseData->offset);
338
-
339
- /* The developer indicated that their onWritable failed. */
340
- if (!success) {
341
- /* Skip testing if we can drain anything since that might perform an extra syscall */
342
- return s;
343
- }
344
-
345
- /* We don't want to fall through since we don't want to mess with timeout.
346
- * It makes little sense to drain any backpressure when the user has registered onWritable. */
347
- return s;
348
- }
349
-
350
- /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
351
- /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
352
-
353
- /* Should we close this connection after a response - and is this response really done? */
354
- if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
355
- if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
356
- if (asyncSocket->getBufferedAmount() == 0) {
357
- asyncSocket->shutdown();
358
- /* We need to force close after sending FIN since we want to hinder
359
- * clients from keeping to send their huge data */
360
- asyncSocket->close();
361
- }
362
- }
363
- }
364
-
365
- /* Expect another writable event, or another request within the timeout */
366
- asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
367
-
368
- return s;
384
+ /* We cannot return nullptr to the underlying stack in any case */
385
+ return s;
369
386
  });
370
387
 
371
- /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
372
- us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
388
+ /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the
389
+ * app need to handle spurious calls) */
390
+ us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
391
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *)s;
392
+ HttpResponseData<SSL> *httpResponseData =
393
+ (HttpResponseData<SSL> *)asyncSocket->getAsyncSocketData();
394
+
395
+ /* Ask the developer to write data and return success (true) or failure
396
+ * (false), OR skip sending anything and return success (true). */
397
+ if (httpResponseData->onWritable) {
398
+ /* We are now writable, so hang timeout again, the user does not have to
399
+ * do anything so we should hang until end or tryEnd rearms timeout */
400
+ us_socket_timeout(SSL, s, 0);
401
+
402
+ /* We expect the developer to return whether or not write was successful
403
+ * (true). If write was never called, the developer should still return
404
+ * true so that we may drain. */
405
+ bool success =
406
+ httpResponseData->callOnWritable(httpResponseData->offset);
407
+
408
+ /* The developer indicated that their onWritable failed. */
409
+ if (!success) {
410
+ /* Skip testing if we can drain anything since that might perform an
411
+ * extra syscall */
412
+ return s;
413
+ }
373
414
 
374
- /* We do not care for half closed sockets */
375
- AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
376
- return asyncSocket->close();
415
+ /* We don't want to fall through since we don't want to mess with
416
+ * timeout. It makes little sense to drain any backpressure when the
417
+ * user has registered onWritable. */
418
+ return s;
419
+ }
420
+
421
+ /* Drain any socket buffer, this might empty our backpressure and thus
422
+ * finish the request */
423
+ /*auto [written, failed] = */ asyncSocket->write(nullptr, 0, true, 0);
424
+
425
+ /* Should we close this connection after a response - and is this response
426
+ * really done? */
427
+ if (httpResponseData->state &
428
+ HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
429
+ if ((httpResponseData->state &
430
+ HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
431
+ if (asyncSocket->getBufferedAmount() == 0) {
432
+ asyncSocket->shutdown();
433
+ /* We need to force close after sending FIN since we want to hinder
434
+ * clients from keeping to send their huge data */
435
+ asyncSocket->close();
436
+ }
437
+ }
438
+ }
377
439
 
378
- });
440
+ /* Expect another writable event, or another request within the timeout */
441
+ asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
379
442
 
380
- /* Handle socket timeouts, simply close them so to not confuse client with FIN */
381
- us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
443
+ return s;
444
+ });
382
445
 
383
- /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
384
- AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
385
- return asyncSocket->close();
446
+ /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
447
+ us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
448
+ /* We do not care for half closed sockets */
449
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *)s;
450
+ return asyncSocket->close();
451
+ });
386
452
 
387
- });
453
+ /* Handle socket timeouts, simply close them so to not confuse client with
454
+ * FIN */
455
+ us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
456
+ /* Force close rather than gracefully shutdown and risk confusing the
457
+ * client with a complete download */
458
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *)s;
459
+ return asyncSocket->close();
460
+ });
388
461
 
389
- return this;
390
- }
462
+ return this;
463
+ }
391
464
 
392
465
  public:
393
- /* Construct a new HttpContext using specified loop */
394
- static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) {
395
- HttpContext *httpContext;
396
-
397
- httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options);
398
-
399
- if (!httpContext) {
400
- return nullptr;
401
- }
402
-
403
- /* Init socket context data */
404
- new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
405
- return httpContext->init();
406
- }
466
+ /* Construct a new HttpContext using specified loop */
467
+ static HttpContext *create(Loop *loop,
468
+ us_socket_context_options_t options = {}) {
469
+ HttpContext *httpContext;
407
470
 
408
- /* Destruct the HttpContext, it does not follow RAII */
409
- void free() {
410
- /* Destruct socket context data */
411
- HttpContextData<SSL> *httpContextData = getSocketContextData();
412
- httpContextData->~HttpContextData<SSL>();
471
+ httpContext = (HttpContext *)us_create_socket_context(
472
+ SSL, (us_loop_t *)loop, sizeof(HttpContextData<SSL>), options);
413
473
 
414
- /* Free the socket context in whole */
415
- us_socket_context_free(SSL, getSocketContext());
474
+ if (!httpContext) {
475
+ return nullptr;
416
476
  }
417
477
 
418
- void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
419
- getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler));
478
+ /* Init socket context data */
479
+ new ((HttpContextData<SSL> *)us_socket_context_ext(
480
+ SSL, (us_socket_context_t *)httpContext)) HttpContextData<SSL>();
481
+ return httpContext->init();
482
+ }
483
+
484
+ /* Destruct the HttpContext, it does not follow RAII */
485
+ void free() {
486
+ /* Destruct socket context data */
487
+ HttpContextData<SSL> *httpContextData = getSocketContextData();
488
+ httpContextData->~HttpContextData<SSL>();
489
+
490
+ /* Free the socket context in whole */
491
+ us_socket_context_free(SSL, getSocketContext());
492
+ }
493
+
494
+ void
495
+ filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
496
+ getSocketContextData()->filterHandlers.emplace_back(
497
+ std::move(filterHandler));
498
+ }
499
+
500
+ /* Register an HTTP route handler acording to URL pattern */
501
+ void
502
+ onHttp(std::string method, std::string pattern,
503
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler,
504
+ bool upgrade = false) {
505
+ HttpContextData<SSL> *httpContextData = getSocketContextData();
506
+
507
+ /* Todo: This is ugly, fix */
508
+ std::vector<std::string> methods;
509
+ if (method == "*") {
510
+ methods = {"*"};
511
+ } else {
512
+ methods = {method};
420
513
  }
421
514
 
422
- /* Register an HTTP route handler acording to URL pattern */
423
- void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
424
- HttpContextData<SSL> *httpContextData = getSocketContextData();
515
+ uint32_t priority =
516
+ method == "*"
517
+ ? httpContextData->currentRouter->LOW_PRIORITY
518
+ : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY
519
+ : httpContextData->currentRouter->MEDIUM_PRIORITY);
425
520
 
426
- /* Todo: This is ugly, fix */
427
- std::vector<std::string> methods;
428
- if (method == "*") {
429
- methods = {"*"};
430
- } else {
431
- methods = {method};
432
- }
433
-
434
- uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
435
-
436
- /* If we are passed nullptr then remove this */
437
- if (!handler) {
438
- httpContextData->currentRouter->remove(methods[0], pattern, priority);
439
- return;
440
- }
441
-
442
- /* Record this route's parameter offsets */
443
- std::map<std::string, unsigned short, std::less<>> parameterOffsets;
444
- unsigned short offset = 0;
445
- for (unsigned int i = 0; i < pattern.length(); i++) {
446
- if (pattern[i] == ':') {
447
- i++;
448
- unsigned int start = i;
449
- while (i < pattern.length() && pattern[i] != '/') {
450
- i++;
451
- }
452
- parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
453
- //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
454
- offset++;
455
- }
456
- }
457
-
458
- httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
459
- auto user = r->getUserData();
460
- user.httpRequest->setYield(false);
461
- user.httpRequest->setParameters(r->getParameters());
462
- user.httpRequest->setParameterOffsets(&parameterOffsets);
463
-
464
- /* Middleware? Automatically respond to expectations */
465
- std::string_view expect = user.httpRequest->getHeader("expect");
466
- if (expect.length() && expect == "100-continue") {
467
- user.httpResponse->writeContinue();
468
- }
469
-
470
- handler(user.httpResponse, user.httpRequest);
471
-
472
- /* If any handler yielded, the router will keep looking for a suitable handler. */
473
- if (user.httpRequest->getYield()) {
474
- return false;
475
- }
476
- return true;
477
- }, priority);
521
+ /* If we are passed nullptr then remove this */
522
+ if (!handler) {
523
+ httpContextData->currentRouter->remove(methods[0], pattern, priority);
524
+ return;
478
525
  }
479
526
 
480
- /* Listen to port using this HttpContext */
481
- us_listen_socket_t *listen(const char *host, int port, int options) {
482
- return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>));
483
- }
484
-
485
- /* Listen to unix domain socket using this HttpContext */
486
- us_listen_socket_t *listen(const char *path, int options) {
487
- return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>));
488
- }
489
-
490
- void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
491
- us_socket_context_on_pre_open(SSL, getSocketContext(), handler);
527
+ /* Record this route's parameter offsets */
528
+ std::map<std::string, unsigned short, std::less<>> parameterOffsets;
529
+ unsigned short offset = 0;
530
+ for (unsigned int i = 0; i < pattern.length(); i++) {
531
+ if (pattern[i] == ':') {
532
+ i++;
533
+ unsigned int start = i;
534
+ while (i < pattern.length() && pattern[i] != '/') {
535
+ i++;
536
+ }
537
+ parameterOffsets[std::string(pattern.data() + start, i - start)] =
538
+ offset;
539
+ // std::cout << "<" << std::string(pattern.data() + start, i - start) <<
540
+ // "> is offset " << offset;
541
+ offset++;
542
+ }
492
543
  }
493
544
 
494
- /* Adopt an externally accepted socket into this HttpContext */
495
- us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
496
- return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd, sizeof(HttpResponseData<SSL>), 0, 0);
497
- }
545
+ httpContextData->currentRouter->add(
546
+ methods, pattern,
547
+ [handler = std::move(handler),
548
+ parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
549
+ auto user = r->getUserData();
550
+ user.httpRequest->setYield(false);
551
+ user.httpRequest->setParameters(r->getParameters());
552
+ user.httpRequest->setParameterOffsets(&parameterOffsets);
553
+
554
+ /* Middleware? Automatically respond to expectations */
555
+ std::string_view expect = user.httpRequest->getHeader("expect");
556
+ if (expect.length() && expect == "100-continue") {
557
+ user.httpResponse->writeContinue();
558
+ }
559
+
560
+ handler(user.httpResponse, user.httpRequest);
561
+
562
+ /* If any handler yielded, the router will keep looking for a suitable
563
+ * handler. */
564
+ if (user.httpRequest->getYield()) {
565
+ return false;
566
+ }
567
+ return true;
568
+ },
569
+ priority);
570
+ }
571
+
572
+ /* Listen to port using this HttpContext */
573
+ us_listen_socket_t *listen(const char *host, int port, int options) {
574
+ return us_socket_context_listen(SSL, getSocketContext(), host, port,
575
+ options, sizeof(HttpResponseData<SSL>));
576
+ }
577
+
578
+ /* Listen to unix domain socket using this HttpContext */
579
+ us_listen_socket_t *listen(const char *path, int options) {
580
+ return us_socket_context_listen_unix(SSL, getSocketContext(), path, options,
581
+ sizeof(HttpResponseData<SSL>));
582
+ }
583
+
584
+ void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
585
+ us_socket_context_on_pre_open(SSL, getSocketContext(), handler);
586
+ }
587
+
588
+ /* Adopt an externally accepted socket into this HttpContext */
589
+ us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
590
+ return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd,
591
+ sizeof(HttpResponseData<SSL>), 0, 0);
592
+ }
498
593
  };
499
594
 
500
- }
595
+ } // namespace uWS
501
596
 
502
597
  #endif // UWS_HTTPCONTEXT_H