/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 | 472k | us_socket_context_t *getSocketContext() { |
50 | 472k | return (us_socket_context_t *) this; |
51 | 472k | } uWS::HttpContext<true>::getSocketContext() Line | Count | Source | 49 | 121k | us_socket_context_t *getSocketContext() { | 50 | 121k | return (us_socket_context_t *) this; | 51 | 121k | } |
uWS::HttpContext<false>::getSocketContext() Line | Count | Source | 49 | 351k | us_socket_context_t *getSocketContext() { | 50 | 351k | return (us_socket_context_t *) this; | 51 | 351k | } |
|
52 | | |
53 | 12.5M | static us_socket_context_t *getSocketContext(us_socket_t *s) { |
54 | 12.5M | return (us_socket_context_t *) us_socket_context(SSL, s); |
55 | 12.5M | } uWS::HttpContext<true>::getSocketContext(us_socket_t*) Line | Count | Source | 53 | 2.46M | static us_socket_context_t *getSocketContext(us_socket_t *s) { | 54 | 2.46M | return (us_socket_context_t *) us_socket_context(SSL, s); | 55 | 2.46M | } |
uWS::HttpContext<false>::getSocketContext(us_socket_t*) Line | Count | Source | 53 | 10.0M | static us_socket_context_t *getSocketContext(us_socket_t *s) { | 54 | 10.0M | return (us_socket_context_t *) us_socket_context(SSL, s); | 55 | 10.0M | } |
|
56 | | |
57 | 332k | HttpContextData<SSL> *getSocketContextData() { |
58 | 332k | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext()); |
59 | 332k | } uWS::HttpContext<true>::getSocketContextData() Line | Count | Source | 57 | 87.9k | HttpContextData<SSL> *getSocketContextData() { | 58 | 87.9k | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext()); | 59 | 87.9k | } |
uWS::HttpContext<false>::getSocketContextData() Line | Count | Source | 57 | 244k | HttpContextData<SSL> *getSocketContextData() { | 58 | 244k | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext()); | 59 | 244k | } |
|
60 | | |
61 | 12.5M | static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) { |
62 | 12.5M | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s)); |
63 | 12.5M | } uWS::HttpContext<true>::getSocketContextDataS(us_socket_t*) Line | Count | Source | 61 | 2.46M | static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) { | 62 | 2.46M | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s)); | 63 | 2.46M | } |
uWS::HttpContext<false>::getSocketContextDataS(us_socket_t*) Line | Count | Source | 61 | 10.0M | static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) { | 62 | 10.0M | return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s)); | 63 | 10.0M | } |
|
64 | | |
65 | | /* Init the HttpContext by registering libusockets event handlers */ |
66 | 17.5k | HttpContext<SSL> *init() { |
67 | | /* Handle socket connections */ |
68 | 5.74M | 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 | 5.74M | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); |
71 | | |
72 | | /* Init socket ext */ |
73 | 5.74M | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; |
74 | | |
75 | | /* Call filter */ |
76 | 5.74M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
77 | 5.74M | for (auto &f : httpContextData->filterHandlers) { |
78 | 0 | f((HttpResponse<SSL> *) s, 1); |
79 | 0 | } |
80 | | |
81 | 5.74M | return s; |
82 | 5.74M | }); uWS::HttpContext<true>::init()::{lambda(us_socket_t*, int, char*, int)#1}::operator()(us_socket_t*, int, char*, int) const Line | Count | Source | 68 | 1.04M | 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 | 1.04M | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); | 71 | | | 72 | | /* Init socket ext */ | 73 | 1.04M | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; | 74 | | | 75 | | /* Call filter */ | 76 | 1.04M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 77 | 1.04M | for (auto &f : httpContextData->filterHandlers) { | 78 | 0 | f((HttpResponse<SSL> *) s, 1); | 79 | 0 | } | 80 | | | 81 | 1.04M | return s; | 82 | 1.04M | }); |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, int, char*, int)#1}::operator()(us_socket_t*, int, char*, int) const Line | Count | Source | 68 | 4.69M | 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 | 4.69M | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); | 71 | | | 72 | | /* Init socket ext */ | 73 | 4.69M | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; | 74 | | | 75 | | /* Call filter */ | 76 | 4.69M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 77 | 4.69M | for (auto &f : httpContextData->filterHandlers) { | 78 | 0 | f((HttpResponse<SSL> *) s, 1); | 79 | 0 | } | 80 | | | 81 | 4.69M | return s; | 82 | 4.69M | }); |
|
83 | | |
84 | | /* Handle socket disconnections */ |
85 | 5.48M | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { |
86 | | /* Get socket ext */ |
87 | 5.48M | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); |
88 | | |
89 | | /* Call filter */ |
90 | 5.48M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
91 | 5.48M | 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 | 5.48M | if (httpResponseData->onAborted) { |
97 | 11.8k | httpResponseData->onAborted(); |
98 | 11.8k | } |
99 | | |
100 | | /* Destruct socket ext */ |
101 | 5.48M | httpResponseData->~HttpResponseData<SSL>(); |
102 | | |
103 | 5.48M | return s; |
104 | 5.48M | }); uWS::HttpContext<true>::init()::{lambda(us_socket_t*, int, void*)#1}::operator()(us_socket_t*, int, void*) const Line | Count | Source | 85 | 970k | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { | 86 | | /* Get socket ext */ | 87 | 970k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 88 | | | 89 | | /* Call filter */ | 90 | 970k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 91 | 970k | 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 | 970k | if (httpResponseData->onAborted) { | 97 | 0 | httpResponseData->onAborted(); | 98 | 0 | } | 99 | | | 100 | | /* Destruct socket ext */ | 101 | 970k | httpResponseData->~HttpResponseData<SSL>(); | 102 | | | 103 | 970k | return s; | 104 | 970k | }); |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, int, void*)#1}::operator()(us_socket_t*, int, void*) const Line | Count | Source | 85 | 4.51M | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { | 86 | | /* Get socket ext */ | 87 | 4.51M | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 88 | | | 89 | | /* Call filter */ | 90 | 4.51M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 91 | 4.51M | 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 | 4.51M | if (httpResponseData->onAborted) { | 97 | 11.8k | httpResponseData->onAborted(); | 98 | 11.8k | } | 99 | | | 100 | | /* Destruct socket ext */ | 101 | 4.51M | httpResponseData->~HttpResponseData<SSL>(); | 102 | | | 103 | 4.51M | return s; | 104 | 4.51M | }); |
|
105 | | |
106 | | /* Handle HTTP data streams */ |
107 | 1.31M | 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 | 1.31M | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); |
116 | | |
117 | | /* Do not accept any data while in shutdown state */ |
118 | 1.31M | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
119 | 0 | return s; |
120 | 0 | } |
121 | | |
122 | 1.31M | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); |
123 | | |
124 | | /* Cork this socket */ |
125 | 1.31M | ((AsyncSocket<SSL> *) s)->cork(); |
126 | | |
127 | | /* Mark that we are inside the parser now */ |
128 | 1.31M | 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 | 1.31M | 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 | 1.31M | 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 | 324k | us_socket_timeout(SSL, (us_socket_t *) s, 0); |
143 | | |
144 | | /* Reset httpResponse */ |
145 | 324k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); |
146 | 324k | httpResponseData->offset = 0; |
147 | | |
148 | | /* Are we not ready for another request yet? Terminate the connection. */ |
149 | 324k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { |
150 | 1.31k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); |
151 | 1.31k | return nullptr; |
152 | 1.31k | } |
153 | | |
154 | | /* Mark pending request and emit it */ |
155 | 322k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; |
156 | | |
157 | | /* Mark this response as connectionClose if ancient or connection: close */ |
158 | 322k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { |
159 | 4.86k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; |
160 | 4.86k | } |
161 | | |
162 | | /* Select the router based on SNI (only possible for SSL) */ |
163 | 322k | auto *selectedRouter = &httpContextData->router; |
164 | 322k | if constexpr (SSL) { |
165 | 90.5k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); |
166 | 90.5k | if (domainRouter) { |
167 | 0 | selectedRouter = (decltype(selectedRouter)) domainRouter; |
168 | 0 | } |
169 | 90.5k | } |
170 | | |
171 | | /* Route the method and URL */ |
172 | 322k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; |
173 | 322k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { |
174 | | /* We don't care if it reaches the client or not */ |
175 | 21.3k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); |
176 | 21.3k | us_socket_shutdown(SSL, (us_socket_t *) s); |
177 | | |
178 | | /* We have to force close this socket as we have no handler for it */ |
179 | 21.3k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); |
180 | 21.3k | return nullptr; |
181 | 21.3k | } |
182 | | |
183 | | /* First of all we need to check if this socket was deleted due to upgrade */ |
184 | 301k | if (httpContextData->upgradedWebSocket) { |
185 | | /* We differ between closed and upgraded below */ |
186 | 261k | return nullptr; |
187 | 261k | } |
188 | | |
189 | | /* Was the socket closed? */ |
190 | 40.0k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { |
191 | 910 | return nullptr; |
192 | 910 | } |
193 | | |
194 | | /* We absolutely have to terminate parsing if shutdown */ |
195 | 39.1k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
196 | 0 | return nullptr; |
197 | 0 | } |
198 | | |
199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ |
200 | 39.1k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { |
201 | | /* Throw exception here? */ |
202 | 0 | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; |
203 | 0 | std::terminate(); |
204 | 0 | } |
205 | | |
206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ |
207 | 39.1k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { |
208 | 2.73k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); |
209 | 2.73k | } |
210 | | |
211 | | /* Continue parsing */ |
212 | 39.1k | return s; |
213 | | |
214 | 71.3k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { uWS::HttpContext<true>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const::{lambda(void*, uWS::HttpRequest*)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, uWS::HttpRequest) const Line | Count | Source | 139 | 90.5k | 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 | 90.5k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 90.5k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 90.5k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 90.5k | 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 | 90.5k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 90.5k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 196 | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 196 | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 90.5k | auto *selectedRouter = &httpContextData->router; | 164 | 90.5k | if constexpr (SSL) { | 165 | 90.5k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 90.5k | if (domainRouter) { | 167 | 0 | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 0 | } | 169 | 90.5k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 90.5k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 90.5k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 11.0k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 11.0k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 11.0k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 11.0k | return nullptr; | 181 | 11.0k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 79.5k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 79.5k | return nullptr; | 187 | 79.5k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 0 | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 0 | return nullptr; | 192 | 0 | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 0 | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 0 | return nullptr; | 197 | 0 | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 0 | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 0 | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 0 | std::terminate(); | 204 | 0 | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 0 | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 0 | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 0 | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 0 | return s; | 213 | |
| 214 | 0 | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const::{lambda(void*, uWS::HttpRequest*)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, uWS::HttpRequest) const Line | Count | Source | 139 | 233k | 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 | 233k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 233k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 233k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 233k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { | 150 | 1.31k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 151 | 1.31k | return nullptr; | 152 | 1.31k | } | 153 | | | 154 | | /* Mark pending request and emit it */ | 155 | 232k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 232k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 4.67k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 4.67k | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 232k | auto *selectedRouter = &httpContextData->router; | 164 | 232k | if constexpr (SSL) { | 165 | 232k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 232k | if (domainRouter) { | 167 | 232k | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 232k | } | 169 | 232k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 232k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 232k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 10.3k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 10.3k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 10.3k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 10.3k | return nullptr; | 181 | 10.3k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 222k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 182k | return nullptr; | 187 | 182k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 40.0k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 910 | return nullptr; | 192 | 910 | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 39.1k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 0 | return nullptr; | 197 | 0 | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 39.1k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 0 | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 0 | std::terminate(); | 204 | 0 | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 39.1k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 2.73k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 2.73k | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 39.1k | return s; | 213 | | | 214 | 39.1k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { |
|
215 | | /* We always get an empty chunk even if there is no data */ |
216 | 71.3k | if (httpResponseData->inStream) { |
217 | | |
218 | | /* Todo: can this handle timeout for non-post as well? */ |
219 | 35.1k | if (fin) { |
220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ |
221 | 1.59k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); |
222 | 33.5k | } 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 | 33.5k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); |
226 | 33.5k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { |
227 | 10 | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); |
228 | 10 | httpResponseData->received_bytes_per_timeout = 0; |
229 | 10 | } |
230 | 33.5k | } |
231 | | |
232 | | /* We might respond in the handler, so do not change timeout after this */ |
233 | 35.1k | httpResponseData->inStream(data, fin); |
234 | | |
235 | | /* Was the socket closed? */ |
236 | 35.1k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { |
237 | 0 | return nullptr; |
238 | 0 | } |
239 | | |
240 | | /* We absolutely have to terminate parsing if shutdown */ |
241 | 35.1k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { |
242 | 0 | return nullptr; |
243 | 0 | } |
244 | | |
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 | 35.1k | if (fin) { |
248 | 1.59k | httpResponseData->inStream = nullptr; |
249 | 1.59k | } |
250 | 35.1k | } |
251 | 71.3k | return user; |
252 | 71.3k | }); Unexecuted instantiation: uWS::HttpContext<true>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const::{lambda(void*, std::__1::basic_string_view<char, void*::char_traits<char> >, bool)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, void*::char_traits<char>, bool) const uWS::HttpContext<false>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const::{lambda(void*, std::__1::basic_string_view<char, void*::char_traits<char> >, bool)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, void*::char_traits<char>, bool) const Line | Count | Source | 214 | 71.3k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { | 215 | | /* We always get an empty chunk even if there is no data */ | 216 | 71.3k | if (httpResponseData->inStream) { | 217 | | | 218 | | /* Todo: can this handle timeout for non-post as well? */ | 219 | 35.1k | if (fin) { | 220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ | 221 | 1.59k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); | 222 | 33.5k | } 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 | 33.5k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); | 226 | 33.5k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { | 227 | 10 | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); | 228 | 10 | httpResponseData->received_bytes_per_timeout = 0; | 229 | 10 | } | 230 | 33.5k | } | 231 | | | 232 | | /* We might respond in the handler, so do not change timeout after this */ | 233 | 35.1k | httpResponseData->inStream(data, fin); | 234 | | | 235 | | /* Was the socket closed? */ | 236 | 35.1k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { | 237 | 0 | return nullptr; | 238 | 0 | } | 239 | | | 240 | | /* We absolutely have to terminate parsing if shutdown */ | 241 | 35.1k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { | 242 | 0 | return nullptr; | 243 | 0 | } | 244 | | | 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 | 35.1k | if (fin) { | 248 | 1.59k | httpResponseData->inStream = nullptr; | 249 | 1.59k | } | 250 | 35.1k | } | 251 | 71.3k | return user; | 252 | 71.3k | }); |
|
253 | | |
254 | | /* Mark that we are no longer parsing Http */ |
255 | 1.31M | 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 | 1.31M | 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 | 882k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); |
261 | 882k | us_socket_shutdown(SSL, s); |
262 | | /* Close any socket on HTTP errors */ |
263 | 882k | 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 | 882k | returnedSocket = nullptr; |
266 | 882k | } |
267 | | |
268 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ |
269 | 1.31M | if (returnedSocket != nullptr) { |
270 | | /* Timeout on uncork failure */ |
271 | 143k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); |
272 | 143k | 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 | 53.8k | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); |
276 | 53.8k | } |
277 | | |
278 | | /* We need to check if we should close this socket here now */ |
279 | 143k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { |
280 | 4.07k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { |
281 | 1.61k | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { |
282 | 536 | ((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 | 536 | ((AsyncSocket<SSL> *) s)->close(); |
286 | 536 | } |
287 | 1.61k | } |
288 | 4.07k | } |
289 | | |
290 | 143k | return (us_socket_t *) returnedSocket; |
291 | 143k | } |
292 | | |
293 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ |
294 | 1.16M | if (httpContextData->upgradedWebSocket) { |
295 | | /* This path is only for upgraded websockets */ |
296 | 261k | 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 | 261k | auto [written, failed] = asyncSocket->uncork(); |
300 | | |
301 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ |
302 | 261k | if (!failed) { |
303 | 7.82k | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); |
304 | 7.82k | if (webSocketData->isShuttingDown) { |
305 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ |
306 | 0 | asyncSocket->shutdown(); |
307 | 0 | } |
308 | 7.82k | } |
309 | | |
310 | | /* Reset upgradedWebSocket before we return */ |
311 | 261k | httpContextData->upgradedWebSocket = nullptr; |
312 | | |
313 | | /* Return the new upgraded websocket */ |
314 | 261k | return (us_socket_t *) asyncSocket; |
315 | 261k | } |
316 | | |
317 | | /* It is okay to uncork a closed socket and we need to */ |
318 | 905k | ((AsyncSocket<SSL> *) s)->uncork(); |
319 | | |
320 | | /* We cannot return nullptr to the underlying stack in any case */ |
321 | 905k | return s; |
322 | 1.16M | }); uWS::HttpContext<true>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const Line | Count | Source | 107 | 443k | 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 | 443k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 116 | | | 117 | | /* Do not accept any data while in shutdown state */ | 118 | 443k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 119 | 0 | return s; | 120 | 0 | } | 121 | | | 122 | 443k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 123 | | | 124 | | /* Cork this socket */ | 125 | 443k | ((AsyncSocket<SSL> *) s)->cork(); | 126 | | | 127 | | /* Mark that we are inside the parser now */ | 128 | 443k | 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 | 443k | 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 | 443k | 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 | 443k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 443k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 443k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 443k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { | 150 | 443k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 151 | 443k | return nullptr; | 152 | 443k | } | 153 | | | 154 | | /* Mark pending request and emit it */ | 155 | 443k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 443k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 443k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 443k | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 443k | auto *selectedRouter = &httpContextData->router; | 164 | 443k | if constexpr (SSL) { | 165 | 443k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 443k | if (domainRouter) { | 167 | 443k | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 443k | } | 169 | 443k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 443k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 443k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 443k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 443k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 443k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 443k | return nullptr; | 181 | 443k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 443k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 443k | return nullptr; | 187 | 443k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 443k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 443k | return nullptr; | 192 | 443k | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 443k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 443k | return nullptr; | 197 | 443k | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 443k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 443k | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 443k | std::terminate(); | 204 | 443k | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 443k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 443k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 443k | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 443k | return s; | 213 | | | 214 | 443k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { | 215 | | /* We always get an empty chunk even if there is no data */ | 216 | 443k | if (httpResponseData->inStream) { | 217 | | | 218 | | /* Todo: can this handle timeout for non-post as well? */ | 219 | 443k | if (fin) { | 220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ | 221 | 443k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); | 222 | 443k | } 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 | 443k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); | 226 | 443k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { | 227 | 443k | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); | 228 | 443k | httpResponseData->received_bytes_per_timeout = 0; | 229 | 443k | } | 230 | 443k | } | 231 | | | 232 | | /* We might respond in the handler, so do not change timeout after this */ | 233 | 443k | httpResponseData->inStream(data, fin); | 234 | | | 235 | | /* Was the socket closed? */ | 236 | 443k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { | 237 | 443k | return nullptr; | 238 | 443k | } | 239 | | | 240 | | /* We absolutely have to terminate parsing if shutdown */ | 241 | 443k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { | 242 | 443k | return nullptr; | 243 | 443k | } | 244 | | | 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 | 443k | if (fin) { | 248 | 443k | httpResponseData->inStream = nullptr; | 249 | 443k | } | 250 | 443k | } | 251 | 443k | return user; | 252 | 443k | }); | 253 | | | 254 | | /* Mark that we are no longer parsing Http */ | 255 | 443k | 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 | 443k | 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 | 325k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); | 261 | 325k | us_socket_shutdown(SSL, s); | 262 | | /* Close any socket on HTTP errors */ | 263 | 325k | 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 | 325k | returnedSocket = nullptr; | 266 | 325k | } | 267 | | | 268 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ | 269 | 443k | if (returnedSocket != nullptr) { | 270 | | /* Timeout on uncork failure */ | 271 | 27.7k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); | 272 | 27.7k | 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 | 0 | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); | 276 | 0 | } | 277 | | | 278 | | /* We need to check if we should close this socket here now */ | 279 | 27.7k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 280 | 0 | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 281 | 0 | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { | 282 | 0 | ((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 | 0 | ((AsyncSocket<SSL> *) s)->close(); | 286 | 0 | } | 287 | 0 | } | 288 | 0 | } | 289 | | | 290 | 27.7k | return (us_socket_t *) returnedSocket; | 291 | 27.7k | } | 292 | | | 293 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ | 294 | 416k | if (httpContextData->upgradedWebSocket) { | 295 | | /* This path is only for upgraded websockets */ | 296 | 79.5k | 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 | 79.5k | auto [written, failed] = asyncSocket->uncork(); | 300 | | | 301 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ | 302 | 79.5k | if (!failed) { | 303 | 572 | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); | 304 | 572 | if (webSocketData->isShuttingDown) { | 305 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ | 306 | 0 | asyncSocket->shutdown(); | 307 | 0 | } | 308 | 572 | } | 309 | | | 310 | | /* Reset upgradedWebSocket before we return */ | 311 | 79.5k | httpContextData->upgradedWebSocket = nullptr; | 312 | | | 313 | | /* Return the new upgraded websocket */ | 314 | 79.5k | return (us_socket_t *) asyncSocket; | 315 | 79.5k | } | 316 | | | 317 | | /* It is okay to uncork a closed socket and we need to */ | 318 | 336k | ((AsyncSocket<SSL> *) s)->uncork(); | 319 | | | 320 | | /* We cannot return nullptr to the underlying stack in any case */ | 321 | 336k | return s; | 322 | 416k | }); |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const Line | Count | Source | 107 | 866k | 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 | 866k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 116 | | | 117 | | /* Do not accept any data while in shutdown state */ | 118 | 866k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 119 | 0 | return s; | 120 | 0 | } | 121 | | | 122 | 866k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 123 | | | 124 | | /* Cork this socket */ | 125 | 866k | ((AsyncSocket<SSL> *) s)->cork(); | 126 | | | 127 | | /* Mark that we are inside the parser now */ | 128 | 866k | 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 | 866k | 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 | 866k | 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 | 866k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 866k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 866k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 866k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { | 150 | 866k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 151 | 866k | return nullptr; | 152 | 866k | } | 153 | | | 154 | | /* Mark pending request and emit it */ | 155 | 866k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 866k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 866k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 866k | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 866k | auto *selectedRouter = &httpContextData->router; | 164 | 866k | if constexpr (SSL) { | 165 | 866k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 866k | if (domainRouter) { | 167 | 866k | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 866k | } | 169 | 866k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 866k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 866k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 866k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 866k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 866k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 866k | return nullptr; | 181 | 866k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 866k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 866k | return nullptr; | 187 | 866k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 866k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 866k | return nullptr; | 192 | 866k | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 866k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 866k | return nullptr; | 197 | 866k | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 866k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 866k | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 866k | std::terminate(); | 204 | 866k | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 866k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 866k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 866k | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 866k | return s; | 213 | | | 214 | 866k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { | 215 | | /* We always get an empty chunk even if there is no data */ | 216 | 866k | if (httpResponseData->inStream) { | 217 | | | 218 | | /* Todo: can this handle timeout for non-post as well? */ | 219 | 866k | if (fin) { | 220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ | 221 | 866k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); | 222 | 866k | } 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 | 866k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); | 226 | 866k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { | 227 | 866k | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); | 228 | 866k | httpResponseData->received_bytes_per_timeout = 0; | 229 | 866k | } | 230 | 866k | } | 231 | | | 232 | | /* We might respond in the handler, so do not change timeout after this */ | 233 | 866k | httpResponseData->inStream(data, fin); | 234 | | | 235 | | /* Was the socket closed? */ | 236 | 866k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { | 237 | 866k | return nullptr; | 238 | 866k | } | 239 | | | 240 | | /* We absolutely have to terminate parsing if shutdown */ | 241 | 866k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { | 242 | 866k | return nullptr; | 243 | 866k | } | 244 | | | 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 | 866k | if (fin) { | 248 | 866k | httpResponseData->inStream = nullptr; | 249 | 866k | } | 250 | 866k | } | 251 | 866k | return user; | 252 | 866k | }); | 253 | | | 254 | | /* Mark that we are no longer parsing Http */ | 255 | 866k | 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 | 866k | 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 | 556k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); | 261 | 556k | us_socket_shutdown(SSL, s); | 262 | | /* Close any socket on HTTP errors */ | 263 | 556k | 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 | 556k | returnedSocket = nullptr; | 266 | 556k | } | 267 | | | 268 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ | 269 | 866k | if (returnedSocket != nullptr) { | 270 | | /* Timeout on uncork failure */ | 271 | 115k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); | 272 | 115k | 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 | 53.8k | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); | 276 | 53.8k | } | 277 | | | 278 | | /* We need to check if we should close this socket here now */ | 279 | 115k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 280 | 4.07k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 281 | 1.61k | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { | 282 | 536 | ((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 | 536 | ((AsyncSocket<SSL> *) s)->close(); | 286 | 536 | } | 287 | 1.61k | } | 288 | 4.07k | } | 289 | | | 290 | 115k | return (us_socket_t *) returnedSocket; | 291 | 115k | } | 292 | | | 293 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ | 294 | 751k | if (httpContextData->upgradedWebSocket) { | 295 | | /* This path is only for upgraded websockets */ | 296 | 182k | 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 | 182k | auto [written, failed] = asyncSocket->uncork(); | 300 | | | 301 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ | 302 | 182k | if (!failed) { | 303 | 7.25k | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); | 304 | 7.25k | if (webSocketData->isShuttingDown) { | 305 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ | 306 | 0 | asyncSocket->shutdown(); | 307 | 0 | } | 308 | 7.25k | } | 309 | | | 310 | | /* Reset upgradedWebSocket before we return */ | 311 | 182k | httpContextData->upgradedWebSocket = nullptr; | 312 | | | 313 | | /* Return the new upgraded websocket */ | 314 | 182k | return (us_socket_t *) asyncSocket; | 315 | 182k | } | 316 | | | 317 | | /* It is okay to uncork a closed socket and we need to */ | 318 | 569k | ((AsyncSocket<SSL> *) s)->uncork(); | 319 | | | 320 | | /* We cannot return nullptr to the underlying stack in any case */ | 321 | 569k | return s; | 322 | 751k | }); |
|
323 | | |
324 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ |
325 | 17.5k | us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { |
326 | | |
327 | 11.1k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
328 | 11.1k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData(); |
329 | | |
330 | | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ |
331 | 11.1k | 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 | 0 | 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 | 0 | bool success = httpResponseData->callOnWritable(httpResponseData->offset); |
338 | | |
339 | | /* The developer indicated that their onWritable failed. */ |
340 | 0 | if (!success) { |
341 | | /* Skip testing if we can drain anything since that might perform an extra syscall */ |
342 | 0 | return s; |
343 | 0 | } |
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 | 0 | return s; |
348 | 0 | } |
349 | | |
350 | | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ |
351 | 11.1k | /*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 | 11.1k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { |
355 | 2.77k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { |
356 | 2.38k | if (asyncSocket->getBufferedAmount() == 0) { |
357 | 1.42k | 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 | 1.42k | asyncSocket->close(); |
361 | 1.42k | } |
362 | 2.38k | } |
363 | 2.77k | } |
364 | | |
365 | | /* Expect another writable event, or another request within the timeout */ |
366 | 11.1k | asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); |
367 | | |
368 | 11.1k | return s; |
369 | 11.1k | }); Unexecuted instantiation: uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#1}::operator()(us_socket_t*) const uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#1}::operator()(us_socket_t*) const Line | Count | Source | 325 | 11.1k | us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { | 326 | | | 327 | 11.1k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 328 | 11.1k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData(); | 329 | | | 330 | | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ | 331 | 11.1k | 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 | 0 | 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 | 0 | bool success = httpResponseData->callOnWritable(httpResponseData->offset); | 338 | | | 339 | | /* The developer indicated that their onWritable failed. */ | 340 | 0 | if (!success) { | 341 | | /* Skip testing if we can drain anything since that might perform an extra syscall */ | 342 | 0 | return s; | 343 | 0 | } | 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 | 0 | return s; | 348 | 0 | } | 349 | | | 350 | | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ | 351 | 11.1k | /*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 | 11.1k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 355 | 2.77k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 356 | 2.38k | if (asyncSocket->getBufferedAmount() == 0) { | 357 | 1.42k | 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 | 1.42k | asyncSocket->close(); | 361 | 1.42k | } | 362 | 2.38k | } | 363 | 2.77k | } | 364 | | | 365 | | /* Expect another writable event, or another request within the timeout */ | 366 | 11.1k | asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); | 367 | | | 368 | 11.1k | return s; | 369 | 11.1k | }); |
|
370 | | |
371 | | /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ |
372 | 220k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { |
373 | | |
374 | | /* We do not care for half closed sockets */ |
375 | 220k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
376 | 220k | return asyncSocket->close(); |
377 | | |
378 | 220k | }); uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#2}::operator()(us_socket_t*) const Line | Count | Source | 372 | 83.7k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { | 373 | | | 374 | | /* We do not care for half closed sockets */ | 375 | 83.7k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 376 | 83.7k | return asyncSocket->close(); | 377 | | | 378 | 83.7k | }); |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#2}::operator()(us_socket_t*) const Line | Count | Source | 372 | 137k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { | 373 | | | 374 | | /* We do not care for half closed sockets */ | 375 | 137k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 376 | 137k | return asyncSocket->close(); | 377 | | | 378 | 137k | }); |
|
379 | | |
380 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ |
381 | 17.5k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { |
382 | | |
383 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ |
384 | 13.6k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
385 | 13.6k | return asyncSocket->close(); |
386 | | |
387 | 13.6k | }); uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#3}::operator()(us_socket_t*) const Line | Count | Source | 381 | 3.96k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { | 382 | | | 383 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ | 384 | 3.96k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 385 | 3.96k | return asyncSocket->close(); | 386 | | | 387 | 3.96k | }); |
uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#3}::operator()(us_socket_t*) const Line | Count | Source | 381 | 9.67k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { | 382 | | | 383 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ | 384 | 9.67k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 385 | 9.67k | return asyncSocket->close(); | 386 | | | 387 | 9.67k | }); |
|
388 | | |
389 | 17.5k | return this; |
390 | 17.5k | } uWS::HttpContext<true>::init() Line | Count | Source | 66 | 4.21k | HttpContext<SSL> *init() { | 67 | | /* Handle socket connections */ | 68 | 4.21k | 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 | 4.21k | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); | 71 | | | 72 | | /* Init socket ext */ | 73 | 4.21k | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; | 74 | | | 75 | | /* Call filter */ | 76 | 4.21k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 77 | 4.21k | for (auto &f : httpContextData->filterHandlers) { | 78 | 4.21k | f((HttpResponse<SSL> *) s, 1); | 79 | 4.21k | } | 80 | | | 81 | 4.21k | return s; | 82 | 4.21k | }); | 83 | | | 84 | | /* Handle socket disconnections */ | 85 | 4.21k | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { | 86 | | /* Get socket ext */ | 87 | 4.21k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 88 | | | 89 | | /* Call filter */ | 90 | 4.21k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 91 | 4.21k | for (auto &f : httpContextData->filterHandlers) { | 92 | 4.21k | f((HttpResponse<SSL> *) s, -1); | 93 | 4.21k | } | 94 | | | 95 | | /* Signal broken HTTP request only if we have a pending request */ | 96 | 4.21k | if (httpResponseData->onAborted) { | 97 | 4.21k | httpResponseData->onAborted(); | 98 | 4.21k | } | 99 | | | 100 | | /* Destruct socket ext */ | 101 | 4.21k | httpResponseData->~HttpResponseData<SSL>(); | 102 | | | 103 | 4.21k | return s; | 104 | 4.21k | }); | 105 | | | 106 | | /* Handle HTTP data streams */ | 107 | 4.21k | 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 | 4.21k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 116 | | | 117 | | /* Do not accept any data while in shutdown state */ | 118 | 4.21k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 119 | 4.21k | return s; | 120 | 4.21k | } | 121 | | | 122 | 4.21k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 123 | | | 124 | | /* Cork this socket */ | 125 | 4.21k | ((AsyncSocket<SSL> *) s)->cork(); | 126 | | | 127 | | /* Mark that we are inside the parser now */ | 128 | 4.21k | 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 | 4.21k | 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 | 4.21k | 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 | 4.21k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 4.21k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 4.21k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 4.21k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { | 150 | 4.21k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 151 | 4.21k | return nullptr; | 152 | 4.21k | } | 153 | | | 154 | | /* Mark pending request and emit it */ | 155 | 4.21k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 4.21k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 4.21k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 4.21k | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 4.21k | auto *selectedRouter = &httpContextData->router; | 164 | 4.21k | if constexpr (SSL) { | 165 | 4.21k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 4.21k | if (domainRouter) { | 167 | 4.21k | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 4.21k | } | 169 | 4.21k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 4.21k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 4.21k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 4.21k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 4.21k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 4.21k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 4.21k | return nullptr; | 181 | 4.21k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 4.21k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 4.21k | return nullptr; | 187 | 4.21k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 4.21k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 4.21k | return nullptr; | 192 | 4.21k | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 4.21k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 4.21k | return nullptr; | 197 | 4.21k | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 4.21k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 4.21k | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 4.21k | std::terminate(); | 204 | 4.21k | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 4.21k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 4.21k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 4.21k | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 4.21k | return s; | 213 | | | 214 | 4.21k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { | 215 | | /* We always get an empty chunk even if there is no data */ | 216 | 4.21k | if (httpResponseData->inStream) { | 217 | | | 218 | | /* Todo: can this handle timeout for non-post as well? */ | 219 | 4.21k | if (fin) { | 220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ | 221 | 4.21k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); | 222 | 4.21k | } 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 | 4.21k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); | 226 | 4.21k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { | 227 | 4.21k | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); | 228 | 4.21k | httpResponseData->received_bytes_per_timeout = 0; | 229 | 4.21k | } | 230 | 4.21k | } | 231 | | | 232 | | /* We might respond in the handler, so do not change timeout after this */ | 233 | 4.21k | httpResponseData->inStream(data, fin); | 234 | | | 235 | | /* Was the socket closed? */ | 236 | 4.21k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { | 237 | 4.21k | return nullptr; | 238 | 4.21k | } | 239 | | | 240 | | /* We absolutely have to terminate parsing if shutdown */ | 241 | 4.21k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { | 242 | 4.21k | return nullptr; | 243 | 4.21k | } | 244 | | | 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 | 4.21k | if (fin) { | 248 | 4.21k | httpResponseData->inStream = nullptr; | 249 | 4.21k | } | 250 | 4.21k | } | 251 | 4.21k | return user; | 252 | 4.21k | }); | 253 | | | 254 | | /* Mark that we are no longer parsing Http */ | 255 | 4.21k | 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 | 4.21k | 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 | 4.21k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); | 261 | 4.21k | us_socket_shutdown(SSL, s); | 262 | | /* Close any socket on HTTP errors */ | 263 | 4.21k | 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 | 4.21k | returnedSocket = nullptr; | 266 | 4.21k | } | 267 | | | 268 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ | 269 | 4.21k | if (returnedSocket != nullptr) { | 270 | | /* Timeout on uncork failure */ | 271 | 4.21k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); | 272 | 4.21k | 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 | 4.21k | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); | 276 | 4.21k | } | 277 | | | 278 | | /* We need to check if we should close this socket here now */ | 279 | 4.21k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 280 | 4.21k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 281 | 4.21k | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { | 282 | 4.21k | ((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 | 4.21k | ((AsyncSocket<SSL> *) s)->close(); | 286 | 4.21k | } | 287 | 4.21k | } | 288 | 4.21k | } | 289 | | | 290 | 4.21k | return (us_socket_t *) returnedSocket; | 291 | 4.21k | } | 292 | | | 293 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ | 294 | 4.21k | if (httpContextData->upgradedWebSocket) { | 295 | | /* This path is only for upgraded websockets */ | 296 | 4.21k | 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 | 4.21k | auto [written, failed] = asyncSocket->uncork(); | 300 | | | 301 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ | 302 | 4.21k | if (!failed) { | 303 | 4.21k | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); | 304 | 4.21k | if (webSocketData->isShuttingDown) { | 305 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ | 306 | 4.21k | asyncSocket->shutdown(); | 307 | 4.21k | } | 308 | 4.21k | } | 309 | | | 310 | | /* Reset upgradedWebSocket before we return */ | 311 | 4.21k | httpContextData->upgradedWebSocket = nullptr; | 312 | | | 313 | | /* Return the new upgraded websocket */ | 314 | 4.21k | return (us_socket_t *) asyncSocket; | 315 | 4.21k | } | 316 | | | 317 | | /* It is okay to uncork a closed socket and we need to */ | 318 | 4.21k | ((AsyncSocket<SSL> *) s)->uncork(); | 319 | | | 320 | | /* We cannot return nullptr to the underlying stack in any case */ | 321 | 4.21k | return s; | 322 | 4.21k | }); | 323 | | | 324 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 325 | 4.21k | us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { | 326 | | | 327 | 4.21k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 328 | 4.21k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData(); | 329 | | | 330 | | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ | 331 | 4.21k | 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 | 4.21k | 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 | 4.21k | bool success = httpResponseData->callOnWritable(httpResponseData->offset); | 338 | | | 339 | | /* The developer indicated that their onWritable failed. */ | 340 | 4.21k | if (!success) { | 341 | | /* Skip testing if we can drain anything since that might perform an extra syscall */ | 342 | 4.21k | return s; | 343 | 4.21k | } | 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 | 4.21k | return s; | 348 | 4.21k | } | 349 | | | 350 | | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ | 351 | 4.21k | /*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 | 4.21k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 355 | 4.21k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 356 | 4.21k | if (asyncSocket->getBufferedAmount() == 0) { | 357 | 4.21k | 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 | 4.21k | asyncSocket->close(); | 361 | 4.21k | } | 362 | 4.21k | } | 363 | 4.21k | } | 364 | | | 365 | | /* Expect another writable event, or another request within the timeout */ | 366 | 4.21k | asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); | 367 | | | 368 | 4.21k | return s; | 369 | 4.21k | }); | 370 | | | 371 | | /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ | 372 | 4.21k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { | 373 | | | 374 | | /* We do not care for half closed sockets */ | 375 | 4.21k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 376 | 4.21k | return asyncSocket->close(); | 377 | | | 378 | 4.21k | }); | 379 | | | 380 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 381 | 4.21k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { | 382 | | | 383 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ | 384 | 4.21k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 385 | 4.21k | return asyncSocket->close(); | 386 | | | 387 | 4.21k | }); | 388 | | | 389 | 4.21k | return this; | 390 | 4.21k | } |
uWS::HttpContext<false>::init() Line | Count | Source | 66 | 13.3k | HttpContext<SSL> *init() { | 67 | | /* Handle socket connections */ | 68 | 13.3k | 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 | 13.3k | us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S); | 71 | | | 72 | | /* Init socket ext */ | 73 | 13.3k | new (us_socket_ext(SSL, s)) HttpResponseData<SSL>; | 74 | | | 75 | | /* Call filter */ | 76 | 13.3k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 77 | 13.3k | for (auto &f : httpContextData->filterHandlers) { | 78 | 13.3k | f((HttpResponse<SSL> *) s, 1); | 79 | 13.3k | } | 80 | | | 81 | 13.3k | return s; | 82 | 13.3k | }); | 83 | | | 84 | | /* Handle socket disconnections */ | 85 | 13.3k | us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) { | 86 | | /* Get socket ext */ | 87 | 13.3k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 88 | | | 89 | | /* Call filter */ | 90 | 13.3k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 91 | 13.3k | for (auto &f : httpContextData->filterHandlers) { | 92 | 13.3k | f((HttpResponse<SSL> *) s, -1); | 93 | 13.3k | } | 94 | | | 95 | | /* Signal broken HTTP request only if we have a pending request */ | 96 | 13.3k | if (httpResponseData->onAborted) { | 97 | 13.3k | httpResponseData->onAborted(); | 98 | 13.3k | } | 99 | | | 100 | | /* Destruct socket ext */ | 101 | 13.3k | httpResponseData->~HttpResponseData<SSL>(); | 102 | | | 103 | 13.3k | return s; | 104 | 13.3k | }); | 105 | | | 106 | | /* Handle HTTP data streams */ | 107 | 13.3k | 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 | 13.3k | HttpContextData<SSL> *httpContextData = getSocketContextDataS(s); | 116 | | | 117 | | /* Do not accept any data while in shutdown state */ | 118 | 13.3k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 119 | 13.3k | return s; | 120 | 13.3k | } | 121 | | | 122 | 13.3k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s); | 123 | | | 124 | | /* Cork this socket */ | 125 | 13.3k | ((AsyncSocket<SSL> *) s)->cork(); | 126 | | | 127 | | /* Mark that we are inside the parser now */ | 128 | 13.3k | 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 | 13.3k | 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 | 13.3k | 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 | 13.3k | us_socket_timeout(SSL, (us_socket_t *) s, 0); | 143 | | | 144 | | /* Reset httpResponse */ | 145 | 13.3k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s); | 146 | 13.3k | httpResponseData->offset = 0; | 147 | | | 148 | | /* Are we not ready for another request yet? Terminate the connection. */ | 149 | 13.3k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) { | 150 | 13.3k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 151 | 13.3k | return nullptr; | 152 | 13.3k | } | 153 | | | 154 | | /* Mark pending request and emit it */ | 155 | 13.3k | httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING; | 156 | | | 157 | | /* Mark this response as connectionClose if ancient or connection: close */ | 158 | 13.3k | if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) { | 159 | 13.3k | httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE; | 160 | 13.3k | } | 161 | | | 162 | | /* Select the router based on SNI (only possible for SSL) */ | 163 | 13.3k | auto *selectedRouter = &httpContextData->router; | 164 | 13.3k | if constexpr (SSL) { | 165 | 13.3k | void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s); | 166 | 13.3k | if (domainRouter) { | 167 | 13.3k | selectedRouter = (decltype(selectedRouter)) domainRouter; | 168 | 13.3k | } | 169 | 13.3k | } | 170 | | | 171 | | /* Route the method and URL */ | 172 | 13.3k | selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest}; | 173 | 13.3k | if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) { | 174 | | /* We don't care if it reaches the client or not */ | 175 | 13.3k | us_socket_write(SSL, (us_socket_t *) s, httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].data(), (int) httpErrorResponses[HTTP_ERROR_404_FILE_NOT_FOUND].length(), false); | 176 | 13.3k | us_socket_shutdown(SSL, (us_socket_t *) s); | 177 | | | 178 | | /* We have to force close this socket as we have no handler for it */ | 179 | 13.3k | us_socket_close(SSL, (us_socket_t *) s, 0, nullptr); | 180 | 13.3k | return nullptr; | 181 | 13.3k | } | 182 | | | 183 | | /* First of all we need to check if this socket was deleted due to upgrade */ | 184 | 13.3k | if (httpContextData->upgradedWebSocket) { | 185 | | /* We differ between closed and upgraded below */ | 186 | 13.3k | return nullptr; | 187 | 13.3k | } | 188 | | | 189 | | /* Was the socket closed? */ | 190 | 13.3k | if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) { | 191 | 13.3k | return nullptr; | 192 | 13.3k | } | 193 | | | 194 | | /* We absolutely have to terminate parsing if shutdown */ | 195 | 13.3k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 196 | 13.3k | return nullptr; | 197 | 13.3k | } | 198 | | | 199 | | /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */ | 200 | 13.3k | if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) { | 201 | | /* Throw exception here? */ | 202 | 13.3k | std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl; | 203 | 13.3k | std::terminate(); | 204 | 13.3k | } | 205 | | | 206 | | /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */ | 207 | 13.3k | if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) { | 208 | 13.3k | us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S); | 209 | 13.3k | } | 210 | | | 211 | | /* Continue parsing */ | 212 | 13.3k | return s; | 213 | | | 214 | 13.3k | }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * { | 215 | | /* We always get an empty chunk even if there is no data */ | 216 | 13.3k | if (httpResponseData->inStream) { | 217 | | | 218 | | /* Todo: can this handle timeout for non-post as well? */ | 219 | 13.3k | if (fin) { | 220 | | /* If we just got the last chunk (or empty chunk), disable timeout */ | 221 | 13.3k | us_socket_timeout(SSL, (struct us_socket_t *) user, 0); | 222 | 13.3k | } 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 | 13.3k | httpResponseData->received_bytes_per_timeout += (unsigned int) data.length(); | 226 | 13.3k | if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) { | 227 | 13.3k | us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S); | 228 | 13.3k | httpResponseData->received_bytes_per_timeout = 0; | 229 | 13.3k | } | 230 | 13.3k | } | 231 | | | 232 | | /* We might respond in the handler, so do not change timeout after this */ | 233 | 13.3k | httpResponseData->inStream(data, fin); | 234 | | | 235 | | /* Was the socket closed? */ | 236 | 13.3k | if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) { | 237 | 13.3k | return nullptr; | 238 | 13.3k | } | 239 | | | 240 | | /* We absolutely have to terminate parsing if shutdown */ | 241 | 13.3k | if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) { | 242 | 13.3k | return nullptr; | 243 | 13.3k | } | 244 | | | 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 | 13.3k | if (fin) { | 248 | 13.3k | httpResponseData->inStream = nullptr; | 249 | 13.3k | } | 250 | 13.3k | } | 251 | 13.3k | return user; | 252 | 13.3k | }); | 253 | | | 254 | | /* Mark that we are no longer parsing Http */ | 255 | 13.3k | 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 | 13.3k | 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 | 13.3k | us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false); | 261 | 13.3k | us_socket_shutdown(SSL, s); | 262 | | /* Close any socket on HTTP errors */ | 263 | 13.3k | 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 | 13.3k | returnedSocket = nullptr; | 266 | 13.3k | } | 267 | | | 268 | | /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */ | 269 | 13.3k | if (returnedSocket != nullptr) { | 270 | | /* Timeout on uncork failure */ | 271 | 13.3k | auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork(); | 272 | 13.3k | 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 | 13.3k | ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S); | 276 | 13.3k | } | 277 | | | 278 | | /* We need to check if we should close this socket here now */ | 279 | 13.3k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 280 | 13.3k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 281 | 13.3k | if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) { | 282 | 13.3k | ((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 | 13.3k | ((AsyncSocket<SSL> *) s)->close(); | 286 | 13.3k | } | 287 | 13.3k | } | 288 | 13.3k | } | 289 | | | 290 | 13.3k | return (us_socket_t *) returnedSocket; | 291 | 13.3k | } | 292 | | | 293 | | /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */ | 294 | 13.3k | if (httpContextData->upgradedWebSocket) { | 295 | | /* This path is only for upgraded websockets */ | 296 | 13.3k | 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 | 13.3k | auto [written, failed] = asyncSocket->uncork(); | 300 | | | 301 | | /* If we succeeded in uncorking, check if we have sent WebSocket FIN */ | 302 | 13.3k | if (!failed) { | 303 | 13.3k | WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData(); | 304 | 13.3k | if (webSocketData->isShuttingDown) { | 305 | | /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */ | 306 | 13.3k | asyncSocket->shutdown(); | 307 | 13.3k | } | 308 | 13.3k | } | 309 | | | 310 | | /* Reset upgradedWebSocket before we return */ | 311 | 13.3k | httpContextData->upgradedWebSocket = nullptr; | 312 | | | 313 | | /* Return the new upgraded websocket */ | 314 | 13.3k | return (us_socket_t *) asyncSocket; | 315 | 13.3k | } | 316 | | | 317 | | /* It is okay to uncork a closed socket and we need to */ | 318 | 13.3k | ((AsyncSocket<SSL> *) s)->uncork(); | 319 | | | 320 | | /* We cannot return nullptr to the underlying stack in any case */ | 321 | 13.3k | return s; | 322 | 13.3k | }); | 323 | | | 324 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 325 | 13.3k | us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) { | 326 | | | 327 | 13.3k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 328 | 13.3k | HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData(); | 329 | | | 330 | | /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */ | 331 | 13.3k | 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 | 13.3k | 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 | 13.3k | bool success = httpResponseData->callOnWritable(httpResponseData->offset); | 338 | | | 339 | | /* The developer indicated that their onWritable failed. */ | 340 | 13.3k | if (!success) { | 341 | | /* Skip testing if we can drain anything since that might perform an extra syscall */ | 342 | 13.3k | return s; | 343 | 13.3k | } | 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 | 13.3k | return s; | 348 | 13.3k | } | 349 | | | 350 | | /* Drain any socket buffer, this might empty our backpressure and thus finish the request */ | 351 | 13.3k | /*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 | 13.3k | if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) { | 355 | 13.3k | if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) { | 356 | 13.3k | if (asyncSocket->getBufferedAmount() == 0) { | 357 | 13.3k | 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 | 13.3k | asyncSocket->close(); | 361 | 13.3k | } | 362 | 13.3k | } | 363 | 13.3k | } | 364 | | | 365 | | /* Expect another writable event, or another request within the timeout */ | 366 | 13.3k | asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S); | 367 | | | 368 | 13.3k | return s; | 369 | 13.3k | }); | 370 | | | 371 | | /* Handle FIN, HTTP does not support half-closed sockets, so simply close */ | 372 | 13.3k | us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) { | 373 | | | 374 | | /* We do not care for half closed sockets */ | 375 | 13.3k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 376 | 13.3k | return asyncSocket->close(); | 377 | | | 378 | 13.3k | }); | 379 | | | 380 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 381 | 13.3k | us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) { | 382 | | | 383 | | /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */ | 384 | 13.3k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 385 | 13.3k | return asyncSocket->close(); | 386 | | | 387 | 13.3k | }); | 388 | | | 389 | 13.3k | return this; | 390 | 13.3k | } |
|
391 | | |
392 | | public: |
393 | | /* Construct a new HttpContext using specified loop */ |
394 | 17.5k | static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { |
395 | 17.5k | HttpContext *httpContext; |
396 | | |
397 | 17.5k | httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options); |
398 | | |
399 | 17.5k | if (!httpContext) { |
400 | 0 | return nullptr; |
401 | 0 | } |
402 | | |
403 | | /* Init socket context data */ |
404 | 17.5k | new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>(); |
405 | 17.5k | return httpContext->init(); |
406 | 17.5k | } uWS::HttpContext<true>::create(uWS::Loop*, us_socket_context_options_t) Line | Count | Source | 394 | 4.21k | static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { | 395 | 4.21k | HttpContext *httpContext; | 396 | | | 397 | 4.21k | httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options); | 398 | | | 399 | 4.21k | if (!httpContext) { | 400 | 0 | return nullptr; | 401 | 0 | } | 402 | | | 403 | | /* Init socket context data */ | 404 | 4.21k | new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>(); | 405 | 4.21k | return httpContext->init(); | 406 | 4.21k | } |
uWS::HttpContext<false>::create(uWS::Loop*, us_socket_context_options_t) Line | Count | Source | 394 | 13.3k | static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { | 395 | 13.3k | HttpContext *httpContext; | 396 | | | 397 | 13.3k | httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options); | 398 | | | 399 | 13.3k | if (!httpContext) { | 400 | 0 | return nullptr; | 401 | 0 | } | 402 | | | 403 | | /* Init socket context data */ | 404 | 13.3k | new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>(); | 405 | 13.3k | return httpContext->init(); | 406 | 13.3k | } |
|
407 | | |
408 | | /* Destruct the HttpContext, it does not follow RAII */ |
409 | 17.5k | void free() { |
410 | | /* Destruct socket context data */ |
411 | 17.5k | HttpContextData<SSL> *httpContextData = getSocketContextData(); |
412 | 17.5k | httpContextData->~HttpContextData<SSL>(); |
413 | | |
414 | | /* Free the socket context in whole */ |
415 | 17.5k | us_socket_context_free(SSL, getSocketContext()); |
416 | 17.5k | } uWS::HttpContext<true>::free() Line | Count | Source | 409 | 4.21k | void free() { | 410 | | /* Destruct socket context data */ | 411 | 4.21k | HttpContextData<SSL> *httpContextData = getSocketContextData(); | 412 | 4.21k | httpContextData->~HttpContextData<SSL>(); | 413 | | | 414 | | /* Free the socket context in whole */ | 415 | 4.21k | us_socket_context_free(SSL, getSocketContext()); | 416 | 4.21k | } |
uWS::HttpContext<false>::free() Line | Count | Source | 409 | 13.3k | void free() { | 410 | | /* Destruct socket context data */ | 411 | 13.3k | HttpContextData<SSL> *httpContextData = getSocketContextData(); | 412 | 13.3k | httpContextData->~HttpContextData<SSL>(); | 413 | | | 414 | | /* Free the socket context in whole */ | 415 | 13.3k | us_socket_context_free(SSL, getSocketContext()); | 416 | 13.3k | } |
|
417 | | |
418 | | void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) { |
419 | | getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler)); |
420 | | } |
421 | | |
422 | | /* Register an HTTP route handler acording to URL pattern */ |
423 | 47.2k | void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) { |
424 | 47.2k | HttpContextData<SSL> *httpContextData = getSocketContextData(); |
425 | | |
426 | | /* Todo: This is ugly, fix */ |
427 | 47.2k | std::vector<std::string> methods; |
428 | 47.2k | if (method == "*") { |
429 | 6.03k | methods = httpContextData->currentRouter->upperCasedMethods; |
430 | 41.1k | } else { |
431 | 41.1k | methods = {method}; |
432 | 41.1k | } |
433 | | |
434 | 47.2k | 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 | 47.2k | if (!handler) { |
438 | 0 | httpContextData->currentRouter->remove(methods[0], pattern, priority); |
439 | 0 | return; |
440 | 0 | } |
441 | | |
442 | 323k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { |
443 | 323k | auto user = r->getUserData(); |
444 | 323k | user.httpRequest->setYield(false); |
445 | 323k | user.httpRequest->setParameters(r->getParameters()); |
446 | | |
447 | | /* Middleware? Automatically respond to expectations */ |
448 | 323k | std::string_view expect = user.httpRequest->getHeader("expect"); |
449 | 323k | if (expect.length() && expect == "100-continue") { |
450 | 1.42k | user.httpResponse->writeContinue(); |
451 | 1.42k | } |
452 | | |
453 | 323k | handler(user.httpResponse, user.httpRequest); |
454 | | |
455 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ |
456 | 323k | if (user.httpRequest->getYield()) { |
457 | 22.0k | return false; |
458 | 22.0k | } |
459 | 301k | return true; |
460 | 323k | }, priority); auto uWS::HttpContext<true>::onHttp(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<true>*, uWS::HttpRequest*)>&&, bool)::{lambda(auto:1*)#1}::operator()<uWS::HttpRouter<uWS::HttpContextData<true>::RouterData> >(uWS::HttpRouter<uWS::HttpContextData<true>::RouterData>*) Line | Count | Source | 442 | 82.7k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { | 443 | 82.7k | auto user = r->getUserData(); | 444 | 82.7k | user.httpRequest->setYield(false); | 445 | 82.7k | user.httpRequest->setParameters(r->getParameters()); | 446 | | | 447 | | /* Middleware? Automatically respond to expectations */ | 448 | 82.7k | std::string_view expect = user.httpRequest->getHeader("expect"); | 449 | 82.7k | if (expect.length() && expect == "100-continue") { | 450 | 1.01k | user.httpResponse->writeContinue(); | 451 | 1.01k | } | 452 | | | 453 | 82.7k | handler(user.httpResponse, user.httpRequest); | 454 | | | 455 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ | 456 | 82.7k | if (user.httpRequest->getYield()) { | 457 | 3.26k | return false; | 458 | 3.26k | } | 459 | 79.5k | return true; | 460 | 82.7k | }, priority); |
auto uWS::HttpContext<false>::onHttp(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<false>*, uWS::HttpRequest*)>&&, bool)::{lambda(auto:1*)#1}::operator()<uWS::HttpRouter<uWS::HttpContextData<false>::RouterData> >(uWS::HttpRouter<uWS::HttpContextData<false>::RouterData>*) Line | Count | Source | 442 | 240k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { | 443 | 240k | auto user = r->getUserData(); | 444 | 240k | user.httpRequest->setYield(false); | 445 | 240k | user.httpRequest->setParameters(r->getParameters()); | 446 | | | 447 | | /* Middleware? Automatically respond to expectations */ | 448 | 240k | std::string_view expect = user.httpRequest->getHeader("expect"); | 449 | 240k | if (expect.length() && expect == "100-continue") { | 450 | 411 | user.httpResponse->writeContinue(); | 451 | 411 | } | 452 | | | 453 | 240k | handler(user.httpResponse, user.httpRequest); | 454 | | | 455 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ | 456 | 240k | if (user.httpRequest->getYield()) { | 457 | 18.8k | return false; | 458 | 18.8k | } | 459 | 222k | return true; | 460 | 240k | }, priority); |
|
461 | 47.2k | } uWS::HttpContext<true>::onHttp(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<true>*, uWS::HttpRequest*)>&&, bool) Line | Count | Source | 423 | 4.21k | void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) { | 424 | 4.21k | HttpContextData<SSL> *httpContextData = getSocketContextData(); | 425 | | | 426 | | /* Todo: This is ugly, fix */ | 427 | 4.21k | std::vector<std::string> methods; | 428 | 4.21k | if (method == "*") { | 429 | 0 | methods = httpContextData->currentRouter->upperCasedMethods; | 430 | 4.21k | } else { | 431 | 4.21k | methods = {method}; | 432 | 4.21k | } | 433 | | | 434 | 4.21k | 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 | 4.21k | if (!handler) { | 438 | 0 | httpContextData->currentRouter->remove(methods[0], pattern, priority); | 439 | 0 | return; | 440 | 0 | } | 441 | | | 442 | 4.21k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { | 443 | 4.21k | auto user = r->getUserData(); | 444 | 4.21k | user.httpRequest->setYield(false); | 445 | 4.21k | user.httpRequest->setParameters(r->getParameters()); | 446 | | | 447 | | /* Middleware? Automatically respond to expectations */ | 448 | 4.21k | std::string_view expect = user.httpRequest->getHeader("expect"); | 449 | 4.21k | if (expect.length() && expect == "100-continue") { | 450 | 4.21k | user.httpResponse->writeContinue(); | 451 | 4.21k | } | 452 | | | 453 | 4.21k | handler(user.httpResponse, user.httpRequest); | 454 | | | 455 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ | 456 | 4.21k | if (user.httpRequest->getYield()) { | 457 | 4.21k | return false; | 458 | 4.21k | } | 459 | 4.21k | return true; | 460 | 4.21k | }, priority); | 461 | 4.21k | } |
uWS::HttpContext<false>::onHttp(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<false>*, uWS::HttpRequest*)>&&, bool) Line | Count | Source | 423 | 43.0k | void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) { | 424 | 43.0k | HttpContextData<SSL> *httpContextData = getSocketContextData(); | 425 | | | 426 | | /* Todo: This is ugly, fix */ | 427 | 43.0k | std::vector<std::string> methods; | 428 | 43.0k | if (method == "*") { | 429 | 6.03k | methods = httpContextData->currentRouter->upperCasedMethods; | 430 | 36.9k | } else { | 431 | 36.9k | methods = {method}; | 432 | 36.9k | } | 433 | | | 434 | 43.0k | 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 | 43.0k | if (!handler) { | 438 | 0 | httpContextData->currentRouter->remove(methods[0], pattern, priority); | 439 | 0 | return; | 440 | 0 | } | 441 | | | 442 | 43.0k | httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler)](auto *r) mutable { | 443 | 43.0k | auto user = r->getUserData(); | 444 | 43.0k | user.httpRequest->setYield(false); | 445 | 43.0k | user.httpRequest->setParameters(r->getParameters()); | 446 | | | 447 | | /* Middleware? Automatically respond to expectations */ | 448 | 43.0k | std::string_view expect = user.httpRequest->getHeader("expect"); | 449 | 43.0k | if (expect.length() && expect == "100-continue") { | 450 | 43.0k | user.httpResponse->writeContinue(); | 451 | 43.0k | } | 452 | | | 453 | 43.0k | handler(user.httpResponse, user.httpRequest); | 454 | | | 455 | | /* If any handler yielded, the router will keep looking for a suitable handler. */ | 456 | 43.0k | if (user.httpRequest->getYield()) { | 457 | 43.0k | return false; | 458 | 43.0k | } | 459 | 43.0k | return true; | 460 | 43.0k | }, priority); | 461 | 43.0k | } |
|
462 | | |
463 | | /* Listen to port using this HttpContext */ |
464 | 17.5k | us_listen_socket_t *listen(const char *host, int port, int options) { |
465 | 17.5k | return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>)); |
466 | 17.5k | } uWS::HttpContext<true>::listen(char const*, int, int) Line | Count | Source | 464 | 4.21k | us_listen_socket_t *listen(const char *host, int port, int options) { | 465 | 4.21k | return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>)); | 466 | 4.21k | } |
uWS::HttpContext<false>::listen(char const*, int, int) Line | Count | Source | 464 | 13.3k | us_listen_socket_t *listen(const char *host, int port, int options) { | 465 | 13.3k | return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>)); | 466 | 13.3k | } |
|
467 | | |
468 | | /* Listen to unix domain socket using this HttpContext */ |
469 | | us_listen_socket_t *listen(const char *path, int options) { |
470 | | return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>)); |
471 | | } |
472 | | }; |
473 | | |
474 | | } |
475 | | |
476 | | #endif // UWS_HTTPCONTEXT_H |