/src/uWebSockets/src/HttpContext.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Authored by Alex Hultman, 2018-2020. |
3 | | * Intellectual property of third-party. |
4 | | |
5 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | * you may not use this file except in compliance with the License. |
7 | | * You may obtain a copy of the License at |
8 | | |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | #ifndef UWS_HTTPCONTEXT_H |
19 | | #define UWS_HTTPCONTEXT_H |
20 | | |
21 | | /* This class defines the main behavior of HTTP and emits various events */ |
22 | | |
23 | | #include "Loop.h" |
24 | | #include "HttpContextData.h" |
25 | | #include "HttpResponseData.h" |
26 | | #include "AsyncSocket.h" |
27 | | #include "WebSocketData.h" |
28 | | |
29 | | #include <string_view> |
30 | | #include <iostream> |
31 | | #include "MoveOnlyFunction.h" |
32 | | |
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; |
45 | | |
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; |
48 | | |
49 | 106k | us_socket_context_t *getSocketContext() { |
50 | 106k | return (us_socket_context_t *) this; |
51 | 106k | } |
52 | | |
53 | 2.33M | static us_socket_context_t *getSocketContext(us_socket_t *s) { |
54 | 2.33M | return (us_socket_context_t *) us_socket_context(SSL, s); |
55 | 2.33M | } |
56 | | |
57 | 73.2k | HttpContextData<SSL> *getSocketContextData() { |
58 | 73.2k | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext()); |
59 | 73.2k | } |
60 | | |
61 | 2.33M | static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) { |
62 | 2.33M | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s)); |
63 | 2.33M | } |
64 | | |
65 | | /* Init the HttpContext by registering libusockets event handlers */ |
66 | 4.13k | HttpContext<SSL> *init() { |
67 | | /* Handle socket connections */ |
68 | 998k | us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) { |
69 | | /* Any connected socket should timeout until it has a request */ |
70 | 998k | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); |
71 | | |
72 | | /* Init socket ext */ |
73 | 998k | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; |
74 | | |
75 | | /* Call filter */ |
76 | 998k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
77 | 998k | for (auto &f : httpContextData->filterHandlers) { |
78 | 0 | f((HttpResponse<SSL> *) s, 1); |
79 | 0 | } |
80 | | |
81 | 998k | return s; |
82 | 998k | }); |
83 | | |
84 | | /* Handle socket disconnections */ |
85 | 933k | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { |
86 | | /* Get socket ext */ |
87 | 933k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); |
88 | | |
89 | | /* Call filter */ |
90 | 933k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
91 | 933k | for (auto &f : httpContextData->filterHandlers) { |
92 | 0 | f((HttpResponse<SSL> *) s, -1); |
93 | 0 | } |
94 | | |
95 | | /* Signal broken HTTP request only if we have a pending request */ |
96 | 933k | if (httpResponseData->onAborted) { |
97 | 0 | httpResponseData->onAborted(); |
98 | 0 | } |
99 | | |
100 | | /* Destruct socket ext */ |
101 | 933k | httpResponseData->~HttpResponseData<SSL>(); |
102 | | |
103 | 933k | return s; |
104 | 933k | }); |
105 | | |
106 | | /* Handle HTTP data streams */ |
107 | 403k | us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) { |
108 | | |
109 | | // total overhead is about 210k down to 180k |
110 | | // ~210k req/sec is the original perf with write in data |
111 | | // ~200k req/sec is with cork and formatting |
112 | | // ~190k req/sec is with http parsing |
113 | | // ~180k - 190k req/sec is with varying routing |
114 | | |
115 | 403k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
116 | | |
117 | | /* Do not accept any data while in shutdown state */ |
118 | 403k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
119 | 0 | return s; |
120 | 0 | } |
121 | | |
122 | 403k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); |
123 | | |
124 | | /* Cork this socket */ |
125 | 403k | ((AsyncSocket<SSL> *) s)->cork(); |
126 | | |
127 | | /* Mark that we are inside the parser now */ |
128 | 403k | httpContextData->isParsingHttp = true; |
129 | | |
130 | | // clients need to know the cursor after http parse, not servers! |
131 | | // how far did we read then? we need to know to continue with websocket parsing data? or? |
132 | | |
133 | 403k | void *proxyParser = nullptr; |
134 | | #ifdef UWS_WITH_PROXY |
135 | | proxyParser = &httpResponseData->proxyParser; |
136 | | #endif |
137 | | |
138 | | /* 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 */ |
139 | 403k | auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * { |
140 | | /* For every request we reset the timeout and hang until user makes action */ |
141 | | /* Warning: if we are in shutdown state, resetting the timer is a security issue! */ |
142 | 79.4k | us_socket_timeout(SSL, (us_socket_t *) s, 0); |
143 | | |
144 | | /* Reset httpResponse */ |
145 | 79.4k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); |
146 | 79.4k | httpResponseData->offset = 0; |
147 | | |
148 | | /* Are we not ready for another request yet? Terminate the connection. */ |
149 | 79.4k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { |
150 | 0 | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); |
151 | 0 | return nullptr; |
152 | 0 | } |
153 | | |
154 | | /* Mark pending request and emit it */ |
155 | 79.4k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; |
156 | | |
157 | | /* Mark this response as connectionClose if ancient or connection: close */ |
158 | 79.4k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { |
159 | 194 | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; |
160 | 194 | } |
161 | | |
162 | | /* Select the router based on SNI (only possible for SSL) */ |
163 | 79.4k | auto *selectedRouter = &httpContextData->router; |
164 | 79.4k | if constexpr (SSL) { |
165 | 79.4k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); |
166 | 79.4k | if (domainRouter) { |
167 | 0 | selectedRouter = (decltype(selectedRouter)) domainRouter; |
168 | 0 | } |
169 | 79.4k | } |
170 | | |
171 | | /* Route the method and URL */ |
172 | 79.4k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; |
173 | 79.4k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { |
174 | | /* We have to force close this socket as we have no handler for it */ |
175 | 14.5k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); |
176 | 14.5k | return nullptr; |
177 | 14.5k | } |
178 | | |
179 | | /* First of all we need to check if this socket was deleted due to upgrade */ |
180 | 64.9k | if (httpContextData->upgradedWebSocket) { |
181 | | /* We differ between closed and upgraded below */ |
182 | 64.9k | return nullptr; |
183 | 64.9k | } |
184 | | |
185 | | /* Was the socket closed? */ |
186 | 0 | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { |
187 | 0 | return nullptr; |
188 | 0 | } |
189 | | |
190 | | /* We absolutely have to terminate parsing if shutdown */ |
191 | 0 | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
192 | 0 | return nullptr; |
193 | 0 | } |
194 | | |
195 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ |
196 | 0 | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { |
197 | | /* Throw exception here? */ |
198 | 0 | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; |
199 | 0 | std::terminate(); |
200 | 0 | } |
201 | | |
202 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ |
203 | 0 | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { |
204 | 0 | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); |
205 | 0 | } |
206 | | |
207 | | /* Continue parsing */ |
208 | 0 | return s; |
209 | |
|
210 | 0 | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { |
211 | | /* We always get an empty chunk even if there is no data */ |
212 | 0 | if (httpResponseData->inStream) { |
213 | | |
214 | | /* Todo: can this handle timeout for non-post as well? */ |
215 | 0 | if (fin) { |
216 | | /* If we just got the last chunk (or empty chunk), disable timeout */ |
217 | 0 | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); |
218 | 0 | } else { |
219 | | /* We still have some more data coming in later, so reset timeout */ |
220 | | /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */ |
221 | 0 | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); |
222 | 0 | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { |
223 | 0 | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); |
224 | 0 | httpResponseData->received_bytes_per_timeout = 0; |
225 | 0 | } |
226 | 0 | } |
227 | | |
228 | | /* We might respond in the handler, so do not change timeout after this */ |
229 | 0 | httpResponseData->inStream(data, fin); |
230 | | |
231 | | /* Was the socket closed? */ |
232 | 0 | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { |
233 | 0 | return nullptr; |
234 | 0 | } |
235 | | |
236 | | /* We absolutely have to terminate parsing if shutdown */ |
237 | 0 | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { |
238 | 0 | return nullptr; |
239 | 0 | } |
240 | | |
241 | | /* If we were given the last data chunk, reset data handler to ensure following |
242 | | * requests on the same socket won't trigger any previously registered behavior */ |
243 | 0 | if (fin) { |
244 | 0 | httpResponseData->inStream = nullptr; |
245 | 0 | } |
246 | 0 | } |
247 | 0 | return user; |
248 | 0 | }); |
249 | | |
250 | | /* Mark that we are no longer parsing Http */ |
251 | 403k | httpContextData->isParsingHttp = false; |
252 | | |
253 | | /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */ |
254 | 403k | if (returnedSocket == FULLPTR) { |
255 | | /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */ |
256 | 303k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); |
257 | 303k | us_socket_shutdown(SSL, s); |
258 | | /* Close any socket on HTTP errors */ |
259 | 303k | us_socket_close(SSL, s, 0, nullptr); |
260 | | /* This just makes the following code act as if the socket was closed from error inside the parser. */ |
261 | 303k | returnedSocket = nullptr; |
262 | 303k | } |
263 | | |
264 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ |
265 | 403k | if (returnedSocket != nullptr) { |
266 | | /* Timeout on uncork failure */ |
267 | 20.2k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); |
268 | 20.2k | if (failed) { |
269 | | /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */ |
270 | | /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */ |
271 | 0 | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); |
272 | 0 | } |
273 | | |
274 | | /* We need to check if we should close this socket here now */ |
275 | 20.2k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { |
276 | 0 | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { |
277 | 0 | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { |
278 | 0 | ((AsyncSocket<SSL> *) s)->shutdown(); |
279 | | /* We need to force close after sending FIN since we want to hinder |
280 | | * clients from keeping to send their huge data */ |
281 | 0 | ((AsyncSocket<SSL> *) s)->close(); |
282 | 0 | } |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | 20.2k | return (us_socket_t *) returnedSocket; |
287 | 20.2k | } |
288 | | |
289 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ |
290 | 382k | if (httpContextData->upgradedWebSocket) { |
291 | | /* This path is only for upgraded websockets */ |
292 | 64.9k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket; |
293 | | |
294 | | /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */ |
295 | 64.9k | auto [written, failed] = asyncSocket->uncork(); |
296 | | |
297 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ |
298 | 64.9k | if (!failed) { |
299 | 951 | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); |
300 | 951 | if (webSocketData->isShuttingDown) { |
301 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ |
302 | 0 | asyncSocket->shutdown(); |
303 | 0 | } |
304 | 951 | } |
305 | | |
306 | | /* Reset upgradedWebSocket before we return */ |
307 | 64.9k | httpContextData->upgradedWebSocket = nullptr; |
308 | | |
309 | | /* Return the new upgraded websocket */ |
310 | 64.9k | return (us_socket_t *) asyncSocket; |
311 | 64.9k | } |
312 | | |
313 | | /* It is okay to uncork a closed socket and we need to */ |
314 | 317k | ((AsyncSocket<SSL> *) s)->uncork(); |
315 | | |
316 | | /* We cannot return nullptr to the underlying stack in any case */ |
317 | 317k | return s; |
318 | 382k | }); |
319 | | |
320 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ |
321 | 4.13k | us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { |
322 | |
|
323 | 0 | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
324 | 0 | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData(); |
325 | | |
326 | | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ |
327 | 0 | if (httpResponseData->onWritable) { |
328 | | /* 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 */ |
329 | 0 | us_socket_timeout(SSL, s, 0); |
330 | | |
331 | | /* We expect the developer to return whether or not write was successful (true). |
332 | | * If write was never called, the developer should still return true so that we may drain. */ |
333 | 0 | bool success = httpResponseData->callOnWritable(httpResponseData->offset); |
334 | | |
335 | | /* The developer indicated that their onWritable failed. */ |
336 | 0 | if (!success) { |
337 | | /* Skip testing if we can drain anything since that might perform an extra syscall */ |
338 | 0 | return s; |
339 | 0 | } |
340 | | |
341 | | /* We don't want to fall through since we don't want to mess with timeout. |
342 | | * It makes little sense to drain any backpressure when the user has registered onWritable. */ |
343 | 0 | return s; |
344 | 0 | } |
345 | | |
346 | | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ |
347 | 0 | /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0); |
348 | | |
349 | | /* Should we close this connection after a response - and is this response really done? */ |
350 | 0 | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { |
351 | 0 | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { |
352 | 0 | if (asyncSocket->getBufferedAmount() == 0) { |
353 | 0 | asyncSocket->shutdown(); |
354 | | /* We need to force close after sending FIN since we want to hinder |
355 | | * clients from keeping to send their huge data */ |
356 | 0 | asyncSocket->close(); |
357 | 0 | } |
358 | 0 | } |
359 | 0 | } |
360 | | |
361 | | /* Expect another writable event, or another request within the timeout */ |
362 | 0 | asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); |
363 | |
|
364 | 0 | return s; |
365 | 0 | }); |
366 | | |
367 | | /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ |
368 | 55.6k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { |
369 | | |
370 | | /* We do not care for half closed sockets */ |
371 | 55.6k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
372 | 55.6k | return asyncSocket->close(); |
373 | | |
374 | 55.6k | }); |
375 | | |
376 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ |
377 | 4.13k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { |
378 | | |
379 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ |
380 | 3.15k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
381 | 3.15k | return asyncSocket->close(); |
382 | | |
383 | 3.15k | }); |
384 | | |
385 | 4.13k | return this; |
386 | 4.13k | } |
387 | | |
388 | | public: |
389 | | /* Construct a new HttpContext using specified loop */ |
390 | 4.13k | static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { |
391 | 4.13k | HttpContext *httpContext; |
392 | | |
393 | 4.13k | httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options); |
394 | | |
395 | 4.13k | if (!httpContext) { |
396 | 0 | return nullptr; |
397 | 0 | } |
398 | | |
399 | | /* Init socket context data */ |
400 | 4.13k | new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>(); |
401 | 4.13k | return httpContext->init(); |
402 | 4.13k | } |
403 | | |
404 | | /* Destruct the HttpContext, it does not follow RAII */ |
405 | 4.13k | void free() { |
406 | | /* Destruct socket context data */ |
407 | 4.13k | HttpContextData<SSL> *httpContextData = getSocketContextData(); |
408 | 4.13k | httpContextData->~HttpContextData<SSL>(); |
409 | | |
410 | | /* Free the socket context in whole */ |
411 | 4.13k | us_socket_context_free(SSL, getSocketContext()); |
412 | 4.13k | } |
413 | | |
414 | | void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) { |
415 | | getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler)); |
416 | | } |
417 | | |
418 | | /* Register an HTTP route handler acording to URL pattern */ |
419 | 4.13k | void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) { |
420 | 4.13k | HttpContextData<SSL> *httpContextData = getSocketContextData(); |
421 | | |
422 | | /* Todo: This is ugly, fix */ |
423 | 4.13k | std::vector<std::string> methods; |
424 | 4.13k | if (method == "*") { |
425 | 0 | methods = httpContextData->currentRouter->upperCasedMethods; |
426 | 4.13k | } else { |
427 | 4.13k | methods = {method}; |
428 | 4.13k | } |
429 | | |
430 | 4.13k | uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY); |
431 | | |
432 | | /* If we are passed nullptr then remove this */ |
433 | 4.13k | if (!handler) { |
434 | 0 | httpContextData->currentRouter->remove(methods[0], pattern, priority); |
435 | 0 | return; |
436 | 0 | } |
437 | | |
438 | 66.5k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { |
439 | 66.5k | auto user = r->getUserData(); |
440 | 66.5k | user.httpRequest->setYield(false); |
441 | 66.5k | user.httpRequest->setParameters(r->getParameters()); |
442 | | |
443 | | /* Middleware? Automatically respond to expectations */ |
444 | 66.5k | std::string_view expect = user.httpRequest->getHeader("expect"); |
445 | 66.5k | if (expect.length() && expect == "100-continue") { |
446 | 227 | user.httpResponse->writeContinue(); |
447 | 227 | } |
448 | | |
449 | 66.5k | handler(user.httpResponse, user.httpRequest); |
450 | | |
451 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ |
452 | 66.5k | if (user.httpRequest->getYield()) { |
453 | 1.57k | return false; |
454 | 1.57k | } |
455 | 64.9k | return true; |
456 | 66.5k | }, priority); |
457 | 4.13k | } |
458 | | |
459 | | /* Listen to port using this HttpContext */ |
460 | 4.13k | us_listen_socket_t *listen(const char *host, int port, int options) { |
461 | 4.13k | return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>)); |
462 | 4.13k | } |
463 | | |
464 | | /* Listen to unix domain socket using this HttpContext */ |
465 | | us_listen_socket_t *listen(const char *path, int options) { |
466 | | return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>)); |
467 | | } |
468 | | }; |
469 | | |
470 | | } |
471 | | |
472 | | #endif // UWS_HTTPCONTEXT_H |