Coverage Report

Created: 2026-09-14 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/uWebSockets/src/HttpContext.h
Line
Count
Source
1
/*
2
 * Authored by Alex Hultman, 2018-2026.
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
    us_loop_t *getLoop() {
50
        return us_socket_context_loop(SSL, getSocketContext());
51
    }
52
53
679k
    us_socket_context_t *getSocketContext() {
54
679k
        return (us_socket_context_t *) this;
55
679k
    }
uWS::HttpContext<true>::getSocketContext()
Line
Count
Source
53
165k
    us_socket_context_t *getSocketContext() {
54
165k
        return (us_socket_context_t *) this;
55
165k
    }
uWS::HttpContext<false>::getSocketContext()
Line
Count
Source
53
514k
    us_socket_context_t *getSocketContext() {
54
514k
        return (us_socket_context_t *) this;
55
514k
    }
56
57
11.8M
    static us_socket_context_t *getSocketContext(us_socket_t *s) {
58
11.8M
        return (us_socket_context_t *) us_socket_context(SSL, s);
59
11.8M
    }
uWS::HttpContext<true>::getSocketContext(us_socket_t*)
Line
Count
Source
57
3.20M
    static us_socket_context_t *getSocketContext(us_socket_t *s) {
58
3.20M
        return (us_socket_context_t *) us_socket_context(SSL, s);
59
3.20M
    }
uWS::HttpContext<false>::getSocketContext(us_socket_t*)
Line
Count
Source
57
8.61M
    static us_socket_context_t *getSocketContext(us_socket_t *s) {
58
8.61M
        return (us_socket_context_t *) us_socket_context(SSL, s);
59
8.61M
    }
60
61
481k
    HttpContextData<SSL> *getSocketContextData() {
62
481k
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
63
481k
    }
uWS::HttpContext<true>::getSocketContextData()
Line
Count
Source
61
114k
    HttpContextData<SSL> *getSocketContextData() {
62
114k
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
63
114k
    }
uWS::HttpContext<false>::getSocketContextData()
Line
Count
Source
61
366k
    HttpContextData<SSL> *getSocketContextData() {
62
366k
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
63
366k
    }
64
65
11.8M
    static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
66
11.8M
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
67
11.8M
    }
uWS::HttpContext<true>::getSocketContextDataS(us_socket_t*)
Line
Count
Source
65
3.20M
    static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
66
3.20M
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
67
3.20M
    }
uWS::HttpContext<false>::getSocketContextDataS(us_socket_t*)
Line
Count
Source
65
8.61M
    static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
66
8.61M
        return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
67
8.61M
    }
68
69
    /* Init the HttpContext by registering libusockets event handlers */
70
24.7k
    HttpContext<SSL> *init() {
71
        /* Handle socket connections */
72
4.67M
        us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) {
73
            /* Any connected socket should timeout until it has a request */
74
4.67M
            us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
76
            /* Init socket ext */
77
4.67M
            new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
79
#ifdef UWS_REMOTE_ADDRESS_USERSPACE
80
            /* Copy remote address into per-socket cache for later retrieval */
81
            AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) us_socket_ext(SSL, s);
82
            if (ip_length > 0 && ip_length <= 16) {
83
                memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length);
84
                asyncSocketData->remoteAddressLength = ip_length;
85
            } else {
86
                asyncSocketData->remoteAddressLength = 0;
87
            }
88
#else
89
4.67M
            (void) ip;
90
4.67M
            (void) ip_length;
91
4.67M
#endif
92
93
            /* Call filter */
94
4.67M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
4.67M
            for (auto &f : httpContextData->filterHandlers) {
96
0
                f((HttpResponse<SSL> *) s, 1);
97
0
            }
98
99
4.67M
            return s;
100
4.67M
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*, int, char*, int)#1}::operator()(us_socket_t*, int, char*, int) const
Line
Count
Source
72
1.13M
        us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) {
73
            /* Any connected socket should timeout until it has a request */
74
1.13M
            us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
76
            /* Init socket ext */
77
1.13M
            new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
79
#ifdef UWS_REMOTE_ADDRESS_USERSPACE
80
            /* Copy remote address into per-socket cache for later retrieval */
81
            AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) us_socket_ext(SSL, s);
82
            if (ip_length > 0 && ip_length <= 16) {
83
                memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length);
84
                asyncSocketData->remoteAddressLength = ip_length;
85
            } else {
86
                asyncSocketData->remoteAddressLength = 0;
87
            }
88
#else
89
1.13M
            (void) ip;
90
1.13M
            (void) ip_length;
91
1.13M
#endif
92
93
            /* Call filter */
94
1.13M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
1.13M
            for (auto &f : httpContextData->filterHandlers) {
96
0
                f((HttpResponse<SSL> *) s, 1);
97
0
            }
98
99
1.13M
            return s;
100
1.13M
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, int, char*, int)#1}::operator()(us_socket_t*, int, char*, int) const
Line
Count
Source
72
3.53M
        us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) {
73
            /* Any connected socket should timeout until it has a request */
74
3.53M
            us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
76
            /* Init socket ext */
77
3.53M
            new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
79
#ifdef UWS_REMOTE_ADDRESS_USERSPACE
80
            /* Copy remote address into per-socket cache for later retrieval */
81
            AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) us_socket_ext(SSL, s);
82
            if (ip_length > 0 && ip_length <= 16) {
83
                memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length);
84
                asyncSocketData->remoteAddressLength = ip_length;
85
            } else {
86
                asyncSocketData->remoteAddressLength = 0;
87
            }
88
#else
89
3.53M
            (void) ip;
90
3.53M
            (void) ip_length;
91
3.53M
#endif
92
93
            /* Call filter */
94
3.53M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
3.53M
            for (auto &f : httpContextData->filterHandlers) {
96
0
                f((HttpResponse<SSL> *) s, 1);
97
0
            }
98
99
3.53M
            return s;
100
3.53M
        });
101
102
        /* Handle socket disconnections */
103
4.30M
        us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
104
            /* Get socket ext */
105
4.30M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
106
107
            /* Call filter */
108
4.30M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
109
4.30M
            for (auto &f : httpContextData->filterHandlers) {
110
0
                f((HttpResponse<SSL> *) s, -1);
111
0
            }
112
113
            /* Signal broken HTTP request only if we have a pending request */
114
4.30M
            if (httpResponseData->onAborted) {
115
8.70k
                httpResponseData->onAborted();
116
8.70k
            }
117
118
            /* Destruct socket ext */
119
4.30M
            httpResponseData->~HttpResponseData<SSL>();
120
121
4.30M
            return s;
122
4.30M
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*, int, void*)#1}::operator()(us_socket_t*, int, void*) const
Line
Count
Source
103
1.03M
        us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
104
            /* Get socket ext */
105
1.03M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
106
107
            /* Call filter */
108
1.03M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
109
1.03M
            for (auto &f : httpContextData->filterHandlers) {
110
0
                f((HttpResponse<SSL> *) s, -1);
111
0
            }
112
113
            /* Signal broken HTTP request only if we have a pending request */
114
1.03M
            if (httpResponseData->onAborted) {
115
0
                httpResponseData->onAborted();
116
0
            }
117
118
            /* Destruct socket ext */
119
1.03M
            httpResponseData->~HttpResponseData<SSL>();
120
121
1.03M
            return s;
122
1.03M
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, int, void*)#1}::operator()(us_socket_t*, int, void*) const
Line
Count
Source
103
3.27M
        us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
104
            /* Get socket ext */
105
3.27M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
106
107
            /* Call filter */
108
3.27M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
109
3.27M
            for (auto &f : httpContextData->filterHandlers) {
110
0
                f((HttpResponse<SSL> *) s, -1);
111
0
            }
112
113
            /* Signal broken HTTP request only if we have a pending request */
114
3.27M
            if (httpResponseData->onAborted) {
115
8.70k
                httpResponseData->onAborted();
116
8.70k
            }
117
118
            /* Destruct socket ext */
119
3.27M
            httpResponseData->~HttpResponseData<SSL>();
120
121
3.27M
            return s;
122
3.27M
        });
123
124
        /* Handle HTTP data streams */
125
2.83M
        us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
126
127
            // total overhead is about 210k down to 180k
128
            // ~210k req/sec is the original perf with write in data
129
            // ~200k req/sec is with cork and formatting
130
            // ~190k req/sec is with http parsing
131
            // ~180k - 190k req/sec is with varying routing
132
133
2.83M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
134
135
            /* Do not accept any data while in shutdown state */
136
2.83M
            if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
137
0
                return s;
138
0
            }
139
140
2.83M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
141
142
            /* Cork this socket */
143
2.83M
            ((AsyncSocket<SSL> *) s)->cork();
144
145
            /* Mark that we are inside the parser now */
146
2.83M
            httpContextData->isParsingHttp = true;
147
148
            // clients need to know the cursor after http parse, not servers!
149
            // how far did we read then? we need to know to continue with websocket parsing data? or?
150
151
2.83M
            void *proxyParser = nullptr;
152
#ifdef UWS_WITH_PROXY
153
            proxyParser = &httpResponseData->proxyParser;
154
#endif
155
156
            /* 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 */
157
2.83M
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
480k
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
480k
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
480k
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
480k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
610
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
610
                    return nullptr;
172
610
                }
173
174
                /* Mark pending request and emit it */
175
480k
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
480k
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
8.21k
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
8.21k
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
480k
                auto *selectedRouter = &httpContextData->router;
184
480k
                if constexpr (SSL) {
185
126k
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
126k
                    if (domainRouter) {
187
0
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
0
                    }
189
126k
                }
190
191
                /* Route the method and URL */
192
480k
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
480k
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
0
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
0
                    return nullptr;
197
0
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
480k
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
364k
                    return nullptr;
203
364k
                }
204
205
                /* Was the socket closed? */
206
116k
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
646
                    return nullptr;
208
646
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
115k
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
0
                    return nullptr;
213
0
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
115k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
0
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
0
                              << std::endl
220
0
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
0
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
0
                    std::terminate();
223
0
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
115k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
6.96k
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
6.96k
                }
229
230
                /* Continue parsing */
231
115k
                return s;
232
233
115k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> 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
157
126k
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
126k
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
126k
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
126k
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
126k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
0
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
0
                    return nullptr;
172
0
                }
173
174
                /* Mark pending request and emit it */
175
126k
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
126k
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
1.21k
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
1.21k
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
126k
                auto *selectedRouter = &httpContextData->router;
184
126k
                if constexpr (SSL) {
185
126k
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
126k
                    if (domainRouter) {
187
0
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
0
                    }
189
126k
                }
190
191
                /* Route the method and URL */
192
126k
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
126k
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
0
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
0
                    return nullptr;
197
0
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
126k
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
96.1k
                    return nullptr;
203
96.1k
                }
204
205
                /* Was the socket closed? */
206
30.0k
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
0
                    return nullptr;
208
0
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
30.0k
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
0
                    return nullptr;
213
0
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
30.0k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
0
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
0
                              << std::endl
220
0
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
0
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
0
                    std::terminate();
223
0
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
30.0k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
0
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
0
                }
229
230
                /* Continue parsing */
231
30.0k
                return s;
232
233
30.0k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> 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
157
354k
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
354k
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
354k
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
354k
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
354k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
610
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
610
                    return nullptr;
172
610
                }
173
174
                /* Mark pending request and emit it */
175
354k
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
354k
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
6.99k
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
6.99k
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
354k
                auto *selectedRouter = &httpContextData->router;
184
                if constexpr (SSL) {
185
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
                    if (domainRouter) {
187
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
                    }
189
                }
190
191
                /* Route the method and URL */
192
354k
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
354k
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
0
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
0
                    return nullptr;
197
0
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
354k
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
268k
                    return nullptr;
203
268k
                }
204
205
                /* Was the socket closed? */
206
86.1k
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
646
                    return nullptr;
208
646
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
85.4k
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
0
                    return nullptr;
213
0
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
85.4k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
0
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
0
                              << std::endl
220
0
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
0
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
0
                    std::terminate();
223
0
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
85.4k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
6.96k
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
6.96k
                }
229
230
                /* Continue parsing */
231
85.4k
                return s;
232
233
85.4k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
84.8k
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
12.0k
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
1.30k
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
10.7k
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
10.7k
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
10.7k
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
0
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
0
                            httpResponseData->received_bytes_per_timeout = 0;
248
0
                        }
249
10.7k
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
12.0k
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
12.0k
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
0
                        return nullptr;
257
0
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
12.0k
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
0
                        return nullptr;
262
0
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
12.0k
                    if (maxRemainingBodyLength == 0) {
267
1.30k
                        httpResponseData->inStream = nullptr;
268
1.30k
                    }
269
12.0k
                }
270
84.8k
                return user;
271
84.8k
            });
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> >, unsigned long)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, void*::char_traits<char>, unsigned long) const
Line
Count
Source
233
20.1k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
20.1k
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
0
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
0
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
0
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
0
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
0
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
0
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
0
                            httpResponseData->received_bytes_per_timeout = 0;
248
0
                        }
249
0
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
0
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
0
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
0
                        return nullptr;
257
0
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
0
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
0
                        return nullptr;
262
0
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
0
                    if (maxRemainingBodyLength == 0) {
267
0
                        httpResponseData->inStream = nullptr;
268
0
                    }
269
0
                }
270
20.1k
                return user;
271
20.1k
            });
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> >, unsigned long)#1}::operator()({lambda(us_socket_t*, char*, int)#1}, void*::char_traits<char>, unsigned long) const
Line
Count
Source
233
64.7k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
64.7k
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
12.0k
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
1.30k
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
10.7k
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
10.7k
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
10.7k
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
0
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
0
                            httpResponseData->received_bytes_per_timeout = 0;
248
0
                        }
249
10.7k
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
12.0k
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
12.0k
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
0
                        return nullptr;
257
0
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
12.0k
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
0
                        return nullptr;
262
0
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
12.0k
                    if (maxRemainingBodyLength == 0) {
267
1.30k
                        httpResponseData->inStream = nullptr;
268
1.30k
                    }
269
12.0k
                }
270
64.7k
                return user;
271
64.7k
            });
272
273
            /* Mark that we are no longer parsing Http */
274
2.83M
            httpContextData->isParsingHttp = false;
275
276
            /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
277
2.83M
            if (returnedSocket == FULLPTR) {
278
                /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
279
1.31M
                us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
280
1.31M
                us_socket_shutdown(SSL, s);
281
                /* Close any socket on HTTP errors */
282
1.31M
                us_socket_close(SSL, s, 0, nullptr);
283
                /* This just makes the following code act as if the socket was closed from error inside the parser. */
284
1.31M
                returnedSocket = nullptr;
285
1.31M
            }
286
287
            /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
288
2.83M
            if (returnedSocket != nullptr) {
289
                /* Timeout on uncork failure */
290
1.15M
                auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
291
1.15M
                if (failed) {
292
                    /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
293
                    /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
294
59.0k
                    ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
295
59.0k
                }
296
297
                /* We need to check if we should close this socket here now */
298
1.15M
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
299
22.6k
                    if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
300
8.90k
                        if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
301
1.16k
                            ((AsyncSocket<SSL> *) s)->shutdown();
302
                            /* We need to force close after sending FIN since we want to hinder
303
                             * clients from keeping to send their huge data */
304
1.16k
                            ((AsyncSocket<SSL> *) s)->close();
305
1.16k
                        }
306
8.90k
                    }
307
22.6k
                }
308
309
1.15M
                return (us_socket_t *) returnedSocket;
310
1.15M
            }
311
312
            /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
313
1.67M
            if (httpContextData->upgradedWebSocket) {
314
                /* This path is only for upgraded websockets */
315
364k
                AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
316
317
                /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
318
364k
                auto [written, failed] = asyncSocket->uncork();
319
320
                /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
321
364k
                if (!failed) {
322
10.3k
                    WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
323
10.3k
                    if (webSocketData->isShuttingDown) {
324
                        /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
325
0
                        asyncSocket->shutdown();
326
0
                    }
327
10.3k
                }
328
329
                /* Reset upgradedWebSocket before we return */
330
364k
                httpContextData->upgradedWebSocket = nullptr;
331
332
                /* Return the new upgraded websocket */
333
364k
                return (us_socket_t *) asyncSocket;
334
364k
            }
335
336
            /* It is okay to uncork a closed socket and we need to */
337
1.31M
            ((AsyncSocket<SSL> *) s)->uncork();
338
339
            /* We cannot return nullptr to the underlying stack in any case */
340
1.31M
            return s;
341
1.67M
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const
Line
Count
Source
125
1.03M
        us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
126
127
            // total overhead is about 210k down to 180k
128
            // ~210k req/sec is the original perf with write in data
129
            // ~200k req/sec is with cork and formatting
130
            // ~190k req/sec is with http parsing
131
            // ~180k - 190k req/sec is with varying routing
132
133
1.03M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
134
135
            /* Do not accept any data while in shutdown state */
136
1.03M
            if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
137
0
                return s;
138
0
            }
139
140
1.03M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
141
142
            /* Cork this socket */
143
1.03M
            ((AsyncSocket<SSL> *) s)->cork();
144
145
            /* Mark that we are inside the parser now */
146
1.03M
            httpContextData->isParsingHttp = true;
147
148
            // clients need to know the cursor after http parse, not servers!
149
            // how far did we read then? we need to know to continue with websocket parsing data? or?
150
151
1.03M
            void *proxyParser = nullptr;
152
#ifdef UWS_WITH_PROXY
153
            proxyParser = &httpResponseData->proxyParser;
154
#endif
155
156
            /* 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 */
157
1.03M
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
1.03M
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
1.03M
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
1.03M
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
1.03M
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
1.03M
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
1.03M
                    return nullptr;
172
1.03M
                }
173
174
                /* Mark pending request and emit it */
175
1.03M
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
1.03M
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
1.03M
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
1.03M
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
1.03M
                auto *selectedRouter = &httpContextData->router;
184
1.03M
                if constexpr (SSL) {
185
1.03M
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
1.03M
                    if (domainRouter) {
187
1.03M
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
1.03M
                    }
189
1.03M
                }
190
191
                /* Route the method and URL */
192
1.03M
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
1.03M
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
1.03M
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
1.03M
                    return nullptr;
197
1.03M
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
1.03M
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
1.03M
                    return nullptr;
203
1.03M
                }
204
205
                /* Was the socket closed? */
206
1.03M
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
1.03M
                    return nullptr;
208
1.03M
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
1.03M
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
1.03M
                    return nullptr;
213
1.03M
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
1.03M
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
1.03M
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
1.03M
                              << std::endl
220
1.03M
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
1.03M
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
1.03M
                    std::terminate();
223
1.03M
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
1.03M
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
1.03M
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
1.03M
                }
229
230
                /* Continue parsing */
231
1.03M
                return s;
232
233
1.03M
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
1.03M
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
1.03M
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
1.03M
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
1.03M
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
1.03M
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
1.03M
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
1.03M
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
1.03M
                            httpResponseData->received_bytes_per_timeout = 0;
248
1.03M
                        }
249
1.03M
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
1.03M
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
1.03M
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
1.03M
                        return nullptr;
257
1.03M
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
1.03M
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
1.03M
                        return nullptr;
262
1.03M
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
1.03M
                    if (maxRemainingBodyLength == 0) {
267
1.03M
                        httpResponseData->inStream = nullptr;
268
1.03M
                    }
269
1.03M
                }
270
1.03M
                return user;
271
1.03M
            });
272
273
            /* Mark that we are no longer parsing Http */
274
1.03M
            httpContextData->isParsingHttp = false;
275
276
            /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
277
1.03M
            if (returnedSocket == FULLPTR) {
278
                /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
279
477k
                us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
280
477k
                us_socket_shutdown(SSL, s);
281
                /* Close any socket on HTTP errors */
282
477k
                us_socket_close(SSL, s, 0, nullptr);
283
                /* This just makes the following code act as if the socket was closed from error inside the parser. */
284
477k
                returnedSocket = nullptr;
285
477k
            }
286
287
            /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
288
1.03M
            if (returnedSocket != nullptr) {
289
                /* Timeout on uncork failure */
290
462k
                auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
291
462k
                if (failed) {
292
                    /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
293
                    /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
294
22.2k
                    ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
295
22.2k
                }
296
297
                /* We need to check if we should close this socket here now */
298
462k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
299
1.58k
                    if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
300
1.58k
                        if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
301
579
                            ((AsyncSocket<SSL> *) s)->shutdown();
302
                            /* We need to force close after sending FIN since we want to hinder
303
                             * clients from keeping to send their huge data */
304
579
                            ((AsyncSocket<SSL> *) s)->close();
305
579
                        }
306
1.58k
                    }
307
1.58k
                }
308
309
462k
                return (us_socket_t *) returnedSocket;
310
462k
            }
311
312
            /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
313
573k
            if (httpContextData->upgradedWebSocket) {
314
                /* This path is only for upgraded websockets */
315
96.1k
                AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
316
317
                /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
318
96.1k
                auto [written, failed] = asyncSocket->uncork();
319
320
                /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
321
96.1k
                if (!failed) {
322
524
                    WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
323
524
                    if (webSocketData->isShuttingDown) {
324
                        /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
325
0
                        asyncSocket->shutdown();
326
0
                    }
327
524
                }
328
329
                /* Reset upgradedWebSocket before we return */
330
96.1k
                httpContextData->upgradedWebSocket = nullptr;
331
332
                /* Return the new upgraded websocket */
333
96.1k
                return (us_socket_t *) asyncSocket;
334
96.1k
            }
335
336
            /* It is okay to uncork a closed socket and we need to */
337
477k
            ((AsyncSocket<SSL> *) s)->uncork();
338
339
            /* We cannot return nullptr to the underlying stack in any case */
340
477k
            return s;
341
573k
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*, char*, int)#1}::operator()(us_socket_t*, char*, int) const
Line
Count
Source
125
1.80M
        us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
126
127
            // total overhead is about 210k down to 180k
128
            // ~210k req/sec is the original perf with write in data
129
            // ~200k req/sec is with cork and formatting
130
            // ~190k req/sec is with http parsing
131
            // ~180k - 190k req/sec is with varying routing
132
133
1.80M
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
134
135
            /* Do not accept any data while in shutdown state */
136
1.80M
            if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
137
0
                return s;
138
0
            }
139
140
1.80M
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
141
142
            /* Cork this socket */
143
1.80M
            ((AsyncSocket<SSL> *) s)->cork();
144
145
            /* Mark that we are inside the parser now */
146
1.80M
            httpContextData->isParsingHttp = true;
147
148
            // clients need to know the cursor after http parse, not servers!
149
            // how far did we read then? we need to know to continue with websocket parsing data? or?
150
151
1.80M
            void *proxyParser = nullptr;
152
#ifdef UWS_WITH_PROXY
153
            proxyParser = &httpResponseData->proxyParser;
154
#endif
155
156
            /* 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 */
157
1.80M
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
1.80M
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
1.80M
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
1.80M
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
1.80M
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
1.80M
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
1.80M
                    return nullptr;
172
1.80M
                }
173
174
                /* Mark pending request and emit it */
175
1.80M
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
1.80M
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
1.80M
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
1.80M
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
1.80M
                auto *selectedRouter = &httpContextData->router;
184
1.80M
                if constexpr (SSL) {
185
1.80M
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
1.80M
                    if (domainRouter) {
187
1.80M
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
1.80M
                    }
189
1.80M
                }
190
191
                /* Route the method and URL */
192
1.80M
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
1.80M
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
1.80M
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
1.80M
                    return nullptr;
197
1.80M
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
1.80M
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
1.80M
                    return nullptr;
203
1.80M
                }
204
205
                /* Was the socket closed? */
206
1.80M
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
1.80M
                    return nullptr;
208
1.80M
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
1.80M
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
1.80M
                    return nullptr;
213
1.80M
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
1.80M
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
1.80M
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
1.80M
                              << std::endl
220
1.80M
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
1.80M
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
1.80M
                    std::terminate();
223
1.80M
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
1.80M
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
1.80M
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
1.80M
                }
229
230
                /* Continue parsing */
231
1.80M
                return s;
232
233
1.80M
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
1.80M
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
1.80M
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
1.80M
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
1.80M
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
1.80M
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
1.80M
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
1.80M
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
1.80M
                            httpResponseData->received_bytes_per_timeout = 0;
248
1.80M
                        }
249
1.80M
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
1.80M
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
1.80M
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
1.80M
                        return nullptr;
257
1.80M
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
1.80M
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
1.80M
                        return nullptr;
262
1.80M
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
1.80M
                    if (maxRemainingBodyLength == 0) {
267
1.80M
                        httpResponseData->inStream = nullptr;
268
1.80M
                    }
269
1.80M
                }
270
1.80M
                return user;
271
1.80M
            });
272
273
            /* Mark that we are no longer parsing Http */
274
1.80M
            httpContextData->isParsingHttp = false;
275
276
            /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
277
1.80M
            if (returnedSocket == FULLPTR) {
278
                /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
279
836k
                us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
280
836k
                us_socket_shutdown(SSL, s);
281
                /* Close any socket on HTTP errors */
282
836k
                us_socket_close(SSL, s, 0, nullptr);
283
                /* This just makes the following code act as if the socket was closed from error inside the parser. */
284
836k
                returnedSocket = nullptr;
285
836k
            }
286
287
            /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
288
1.80M
            if (returnedSocket != nullptr) {
289
                /* Timeout on uncork failure */
290
697k
                auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
291
697k
                if (failed) {
292
                    /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
293
                    /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
294
36.7k
                    ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
295
36.7k
                }
296
297
                /* We need to check if we should close this socket here now */
298
697k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
299
21.0k
                    if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
300
7.31k
                        if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
301
584
                            ((AsyncSocket<SSL> *) s)->shutdown();
302
                            /* We need to force close after sending FIN since we want to hinder
303
                             * clients from keeping to send their huge data */
304
584
                            ((AsyncSocket<SSL> *) s)->close();
305
584
                        }
306
7.31k
                    }
307
21.0k
                }
308
309
697k
                return (us_socket_t *) returnedSocket;
310
697k
            }
311
312
            /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
313
1.10M
            if (httpContextData->upgradedWebSocket) {
314
                /* This path is only for upgraded websockets */
315
268k
                AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
316
317
                /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
318
268k
                auto [written, failed] = asyncSocket->uncork();
319
320
                /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
321
268k
                if (!failed) {
322
9.87k
                    WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
323
9.87k
                    if (webSocketData->isShuttingDown) {
324
                        /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
325
0
                        asyncSocket->shutdown();
326
0
                    }
327
9.87k
                }
328
329
                /* Reset upgradedWebSocket before we return */
330
268k
                httpContextData->upgradedWebSocket = nullptr;
331
332
                /* Return the new upgraded websocket */
333
268k
                return (us_socket_t *) asyncSocket;
334
268k
            }
335
336
            /* It is okay to uncork a closed socket and we need to */
337
837k
            ((AsyncSocket<SSL> *) s)->uncork();
338
339
            /* We cannot return nullptr to the underlying stack in any case */
340
837k
            return s;
341
1.10M
        });
342
343
        /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */
344
24.7k
        us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
345
346
23.4k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
347
23.4k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
348
349
            /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
350
23.4k
            if (httpResponseData->onWritable) {
351
                /* 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 */
352
0
                us_socket_timeout(SSL, s, 0);
353
354
                /* We expect the developer to return whether or not write was successful (true).
355
                 * If write was never called, the developer should still return true so that we may drain. */
356
0
                bool success = httpResponseData->callOnWritable(httpResponseData->offset);
357
358
                /* The developer indicated that their onWritable failed. */
359
0
                if (!success) {
360
                    /* Skip testing if we can drain anything since that might perform an extra syscall */
361
0
                    return s;
362
0
                }
363
364
                /* We don't want to fall through since we don't want to mess with timeout.
365
                 * It makes little sense to drain any backpressure when the user has registered onWritable. */
366
0
                return s;
367
0
            }
368
369
            /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
370
23.4k
            /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
371
372
            /* Should we close this connection after a response - and is this response really done? */
373
23.4k
            if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
374
9.30k
                if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
375
8.53k
                    if (asyncSocket->getBufferedAmount() == 0) {
376
3.22k
                        asyncSocket->shutdown();
377
                        /* We need to force close after sending FIN since we want to hinder
378
                         * clients from keeping to send their huge data */
379
3.22k
                        asyncSocket->close();
380
3.22k
                    }
381
8.53k
                }
382
9.30k
            }
383
384
            /* Expect another writable event, or another request within the timeout */
385
23.4k
            asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
386
387
23.4k
            return s;
388
23.4k
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#1}::operator()(us_socket_t*) const
Line
Count
Source
344
7.17k
        us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
345
346
7.17k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
347
7.17k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
348
349
            /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
350
7.17k
            if (httpResponseData->onWritable) {
351
                /* 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 */
352
0
                us_socket_timeout(SSL, s, 0);
353
354
                /* We expect the developer to return whether or not write was successful (true).
355
                 * If write was never called, the developer should still return true so that we may drain. */
356
0
                bool success = httpResponseData->callOnWritable(httpResponseData->offset);
357
358
                /* The developer indicated that their onWritable failed. */
359
0
                if (!success) {
360
                    /* Skip testing if we can drain anything since that might perform an extra syscall */
361
0
                    return s;
362
0
                }
363
364
                /* We don't want to fall through since we don't want to mess with timeout.
365
                 * It makes little sense to drain any backpressure when the user has registered onWritable. */
366
0
                return s;
367
0
            }
368
369
            /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
370
7.17k
            /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
371
372
            /* Should we close this connection after a response - and is this response really done? */
373
7.17k
            if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
374
1.33k
                if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
375
1.33k
                    if (asyncSocket->getBufferedAmount() == 0) {
376
592
                        asyncSocket->shutdown();
377
                        /* We need to force close after sending FIN since we want to hinder
378
                         * clients from keeping to send their huge data */
379
592
                        asyncSocket->close();
380
592
                    }
381
1.33k
                }
382
1.33k
            }
383
384
            /* Expect another writable event, or another request within the timeout */
385
7.17k
            asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
386
387
7.17k
            return s;
388
7.17k
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#1}::operator()(us_socket_t*) const
Line
Count
Source
344
16.2k
        us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
345
346
16.2k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
347
16.2k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
348
349
            /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
350
16.2k
            if (httpResponseData->onWritable) {
351
                /* 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 */
352
0
                us_socket_timeout(SSL, s, 0);
353
354
                /* We expect the developer to return whether or not write was successful (true).
355
                 * If write was never called, the developer should still return true so that we may drain. */
356
0
                bool success = httpResponseData->callOnWritable(httpResponseData->offset);
357
358
                /* The developer indicated that their onWritable failed. */
359
0
                if (!success) {
360
                    /* Skip testing if we can drain anything since that might perform an extra syscall */
361
0
                    return s;
362
0
                }
363
364
                /* We don't want to fall through since we don't want to mess with timeout.
365
                 * It makes little sense to drain any backpressure when the user has registered onWritable. */
366
0
                return s;
367
0
            }
368
369
            /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
370
16.2k
            /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
371
372
            /* Should we close this connection after a response - and is this response really done? */
373
16.2k
            if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
374
7.96k
                if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
375
7.19k
                    if (asyncSocket->getBufferedAmount() == 0) {
376
2.63k
                        asyncSocket->shutdown();
377
                        /* We need to force close after sending FIN since we want to hinder
378
                         * clients from keeping to send their huge data */
379
2.63k
                        asyncSocket->close();
380
2.63k
                    }
381
7.19k
                }
382
7.96k
            }
383
384
            /* Expect another writable event, or another request within the timeout */
385
16.2k
            asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
386
387
16.2k
            return s;
388
16.2k
        });
389
390
        /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
391
155k
        us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
392
393
            /* We do not care for half closed sockets */
394
155k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
395
155k
            return asyncSocket->close();
396
397
155k
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#2}::operator()(us_socket_t*) const
Line
Count
Source
391
39.4k
        us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
392
393
            /* We do not care for half closed sockets */
394
39.4k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
395
39.4k
            return asyncSocket->close();
396
397
39.4k
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#2}::operator()(us_socket_t*) const
Line
Count
Source
391
116k
        us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
392
393
            /* We do not care for half closed sockets */
394
116k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
395
116k
            return asyncSocket->close();
396
397
116k
        });
398
399
        /* Handle socket timeouts, simply close them so to not confuse client with FIN */
400
34.1k
        us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
401
402
            /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
403
34.1k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
404
34.1k
            return asyncSocket->close();
405
406
34.1k
        });
uWS::HttpContext<true>::init()::{lambda(us_socket_t*)#3}::operator()(us_socket_t*) const
Line
Count
Source
400
8.81k
        us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
401
402
            /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
403
8.81k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
404
8.81k
            return asyncSocket->close();
405
406
8.81k
        });
uWS::HttpContext<false>::init()::{lambda(us_socket_t*)#3}::operator()(us_socket_t*) const
Line
Count
Source
400
25.3k
        us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
401
402
            /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
403
25.3k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
404
25.3k
            return asyncSocket->close();
405
406
25.3k
        });
407
408
24.7k
        return this;
409
24.7k
    }
uWS::HttpContext<true>::init()
Line
Count
Source
70
6.26k
    HttpContext<SSL> *init() {
71
        /* Handle socket connections */
72
6.26k
        us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) {
73
            /* Any connected socket should timeout until it has a request */
74
6.26k
            us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
76
            /* Init socket ext */
77
6.26k
            new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
79
#ifdef UWS_REMOTE_ADDRESS_USERSPACE
80
            /* Copy remote address into per-socket cache for later retrieval */
81
            AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) us_socket_ext(SSL, s);
82
            if (ip_length > 0 && ip_length <= 16) {
83
                memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length);
84
                asyncSocketData->remoteAddressLength = ip_length;
85
            } else {
86
                asyncSocketData->remoteAddressLength = 0;
87
            }
88
#else
89
6.26k
            (void) ip;
90
6.26k
            (void) ip_length;
91
6.26k
#endif
92
93
            /* Call filter */
94
6.26k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
6.26k
            for (auto &f : httpContextData->filterHandlers) {
96
6.26k
                f((HttpResponse<SSL> *) s, 1);
97
6.26k
            }
98
99
6.26k
            return s;
100
6.26k
        });
101
102
        /* Handle socket disconnections */
103
6.26k
        us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
104
            /* Get socket ext */
105
6.26k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
106
107
            /* Call filter */
108
6.26k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
109
6.26k
            for (auto &f : httpContextData->filterHandlers) {
110
6.26k
                f((HttpResponse<SSL> *) s, -1);
111
6.26k
            }
112
113
            /* Signal broken HTTP request only if we have a pending request */
114
6.26k
            if (httpResponseData->onAborted) {
115
6.26k
                httpResponseData->onAborted();
116
6.26k
            }
117
118
            /* Destruct socket ext */
119
6.26k
            httpResponseData->~HttpResponseData<SSL>();
120
121
6.26k
            return s;
122
6.26k
        });
123
124
        /* Handle HTTP data streams */
125
6.26k
        us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
126
127
            // total overhead is about 210k down to 180k
128
            // ~210k req/sec is the original perf with write in data
129
            // ~200k req/sec is with cork and formatting
130
            // ~190k req/sec is with http parsing
131
            // ~180k - 190k req/sec is with varying routing
132
133
6.26k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
134
135
            /* Do not accept any data while in shutdown state */
136
6.26k
            if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
137
6.26k
                return s;
138
6.26k
            }
139
140
6.26k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
141
142
            /* Cork this socket */
143
6.26k
            ((AsyncSocket<SSL> *) s)->cork();
144
145
            /* Mark that we are inside the parser now */
146
6.26k
            httpContextData->isParsingHttp = true;
147
148
            // clients need to know the cursor after http parse, not servers!
149
            // how far did we read then? we need to know to continue with websocket parsing data? or?
150
151
6.26k
            void *proxyParser = nullptr;
152
#ifdef UWS_WITH_PROXY
153
            proxyParser = &httpResponseData->proxyParser;
154
#endif
155
156
            /* 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 */
157
6.26k
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
6.26k
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
6.26k
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
6.26k
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
6.26k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
6.26k
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
6.26k
                    return nullptr;
172
6.26k
                }
173
174
                /* Mark pending request and emit it */
175
6.26k
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
6.26k
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
6.26k
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
6.26k
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
6.26k
                auto *selectedRouter = &httpContextData->router;
184
6.26k
                if constexpr (SSL) {
185
6.26k
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
6.26k
                    if (domainRouter) {
187
6.26k
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
6.26k
                    }
189
6.26k
                }
190
191
                /* Route the method and URL */
192
6.26k
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
6.26k
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
6.26k
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
6.26k
                    return nullptr;
197
6.26k
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
6.26k
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
6.26k
                    return nullptr;
203
6.26k
                }
204
205
                /* Was the socket closed? */
206
6.26k
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
6.26k
                    return nullptr;
208
6.26k
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
6.26k
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
6.26k
                    return nullptr;
213
6.26k
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
6.26k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
6.26k
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
6.26k
                              << std::endl
220
6.26k
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
6.26k
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
6.26k
                    std::terminate();
223
6.26k
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
6.26k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
6.26k
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
6.26k
                }
229
230
                /* Continue parsing */
231
6.26k
                return s;
232
233
6.26k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
6.26k
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
6.26k
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
6.26k
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
6.26k
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
6.26k
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
6.26k
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
6.26k
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
6.26k
                            httpResponseData->received_bytes_per_timeout = 0;
248
6.26k
                        }
249
6.26k
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
6.26k
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
6.26k
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
6.26k
                        return nullptr;
257
6.26k
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
6.26k
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
6.26k
                        return nullptr;
262
6.26k
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
6.26k
                    if (maxRemainingBodyLength == 0) {
267
6.26k
                        httpResponseData->inStream = nullptr;
268
6.26k
                    }
269
6.26k
                }
270
6.26k
                return user;
271
6.26k
            });
272
273
            /* Mark that we are no longer parsing Http */
274
6.26k
            httpContextData->isParsingHttp = false;
275
276
            /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
277
6.26k
            if (returnedSocket == FULLPTR) {
278
                /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
279
6.26k
                us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
280
6.26k
                us_socket_shutdown(SSL, s);
281
                /* Close any socket on HTTP errors */
282
6.26k
                us_socket_close(SSL, s, 0, nullptr);
283
                /* This just makes the following code act as if the socket was closed from error inside the parser. */
284
6.26k
                returnedSocket = nullptr;
285
6.26k
            }
286
287
            /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
288
6.26k
            if (returnedSocket != nullptr) {
289
                /* Timeout on uncork failure */
290
6.26k
                auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
291
6.26k
                if (failed) {
292
                    /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
293
                    /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
294
6.26k
                    ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
295
6.26k
                }
296
297
                /* We need to check if we should close this socket here now */
298
6.26k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
299
6.26k
                    if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
300
6.26k
                        if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
301
6.26k
                            ((AsyncSocket<SSL> *) s)->shutdown();
302
                            /* We need to force close after sending FIN since we want to hinder
303
                             * clients from keeping to send their huge data */
304
6.26k
                            ((AsyncSocket<SSL> *) s)->close();
305
6.26k
                        }
306
6.26k
                    }
307
6.26k
                }
308
309
6.26k
                return (us_socket_t *) returnedSocket;
310
6.26k
            }
311
312
            /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
313
6.26k
            if (httpContextData->upgradedWebSocket) {
314
                /* This path is only for upgraded websockets */
315
6.26k
                AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
316
317
                /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
318
6.26k
                auto [written, failed] = asyncSocket->uncork();
319
320
                /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
321
6.26k
                if (!failed) {
322
6.26k
                    WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
323
6.26k
                    if (webSocketData->isShuttingDown) {
324
                        /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
325
6.26k
                        asyncSocket->shutdown();
326
6.26k
                    }
327
6.26k
                }
328
329
                /* Reset upgradedWebSocket before we return */
330
6.26k
                httpContextData->upgradedWebSocket = nullptr;
331
332
                /* Return the new upgraded websocket */
333
6.26k
                return (us_socket_t *) asyncSocket;
334
6.26k
            }
335
336
            /* It is okay to uncork a closed socket and we need to */
337
6.26k
            ((AsyncSocket<SSL> *) s)->uncork();
338
339
            /* We cannot return nullptr to the underlying stack in any case */
340
6.26k
            return s;
341
6.26k
        });
342
343
        /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */
344
6.26k
        us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
345
346
6.26k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
347
6.26k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
348
349
            /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
350
6.26k
            if (httpResponseData->onWritable) {
351
                /* 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 */
352
6.26k
                us_socket_timeout(SSL, s, 0);
353
354
                /* We expect the developer to return whether or not write was successful (true).
355
                 * If write was never called, the developer should still return true so that we may drain. */
356
6.26k
                bool success = httpResponseData->callOnWritable(httpResponseData->offset);
357
358
                /* The developer indicated that their onWritable failed. */
359
6.26k
                if (!success) {
360
                    /* Skip testing if we can drain anything since that might perform an extra syscall */
361
6.26k
                    return s;
362
6.26k
                }
363
364
                /* We don't want to fall through since we don't want to mess with timeout.
365
                 * It makes little sense to drain any backpressure when the user has registered onWritable. */
366
6.26k
                return s;
367
6.26k
            }
368
369
            /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
370
            /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
371
372
            /* Should we close this connection after a response - and is this response really done? */
373
6.26k
            if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
374
6.26k
                if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
375
6.26k
                    if (asyncSocket->getBufferedAmount() == 0) {
376
6.26k
                        asyncSocket->shutdown();
377
                        /* We need to force close after sending FIN since we want to hinder
378
                         * clients from keeping to send their huge data */
379
6.26k
                        asyncSocket->close();
380
6.26k
                    }
381
6.26k
                }
382
6.26k
            }
383
384
            /* Expect another writable event, or another request within the timeout */
385
6.26k
            asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
386
387
6.26k
            return s;
388
6.26k
        });
389
390
        /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
391
6.26k
        us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
392
393
            /* We do not care for half closed sockets */
394
6.26k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
395
6.26k
            return asyncSocket->close();
396
397
6.26k
        });
398
399
        /* Handle socket timeouts, simply close them so to not confuse client with FIN */
400
6.26k
        us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
401
402
            /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
403
6.26k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
404
6.26k
            return asyncSocket->close();
405
406
6.26k
        });
407
408
6.26k
        return this;
409
6.26k
    }
uWS::HttpContext<false>::init()
Line
Count
Source
70
18.5k
    HttpContext<SSL> *init() {
71
        /* Handle socket connections */
72
18.5k
        us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char *ip, int ip_length) {
73
            /* Any connected socket should timeout until it has a request */
74
18.5k
            us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
76
            /* Init socket ext */
77
18.5k
            new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
79
#ifdef UWS_REMOTE_ADDRESS_USERSPACE
80
            /* Copy remote address into per-socket cache for later retrieval */
81
            AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) us_socket_ext(SSL, s);
82
            if (ip_length > 0 && ip_length <= 16) {
83
                memcpy(asyncSocketData->remoteAddress, ip, (size_t) ip_length);
84
                asyncSocketData->remoteAddressLength = ip_length;
85
            } else {
86
                asyncSocketData->remoteAddressLength = 0;
87
            }
88
#else
89
18.5k
            (void) ip;
90
18.5k
            (void) ip_length;
91
18.5k
#endif
92
93
            /* Call filter */
94
18.5k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
18.5k
            for (auto &f : httpContextData->filterHandlers) {
96
18.5k
                f((HttpResponse<SSL> *) s, 1);
97
18.5k
            }
98
99
18.5k
            return s;
100
18.5k
        });
101
102
        /* Handle socket disconnections */
103
18.5k
        us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
104
            /* Get socket ext */
105
18.5k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
106
107
            /* Call filter */
108
18.5k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
109
18.5k
            for (auto &f : httpContextData->filterHandlers) {
110
18.5k
                f((HttpResponse<SSL> *) s, -1);
111
18.5k
            }
112
113
            /* Signal broken HTTP request only if we have a pending request */
114
18.5k
            if (httpResponseData->onAborted) {
115
18.5k
                httpResponseData->onAborted();
116
18.5k
            }
117
118
            /* Destruct socket ext */
119
18.5k
            httpResponseData->~HttpResponseData<SSL>();
120
121
18.5k
            return s;
122
18.5k
        });
123
124
        /* Handle HTTP data streams */
125
18.5k
        us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
126
127
            // total overhead is about 210k down to 180k
128
            // ~210k req/sec is the original perf with write in data
129
            // ~200k req/sec is with cork and formatting
130
            // ~190k req/sec is with http parsing
131
            // ~180k - 190k req/sec is with varying routing
132
133
18.5k
            HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
134
135
            /* Do not accept any data while in shutdown state */
136
18.5k
            if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
137
18.5k
                return s;
138
18.5k
            }
139
140
18.5k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
141
142
            /* Cork this socket */
143
18.5k
            ((AsyncSocket<SSL> *) s)->cork();
144
145
            /* Mark that we are inside the parser now */
146
18.5k
            httpContextData->isParsingHttp = true;
147
148
            // clients need to know the cursor after http parse, not servers!
149
            // how far did we read then? we need to know to continue with websocket parsing data? or?
150
151
18.5k
            void *proxyParser = nullptr;
152
#ifdef UWS_WITH_PROXY
153
            proxyParser = &httpResponseData->proxyParser;
154
#endif
155
156
            /* 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 */
157
18.5k
            auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
158
                /* For every request we reset the timeout and hang until user makes action */
159
                /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
160
18.5k
                us_socket_timeout(SSL, (us_socket_t *) s, 0);
161
162
                /* Reset httpResponse */
163
18.5k
                HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
164
18.5k
                httpResponseData->offset = 0;
165
166
                /* Are we not ready for another request yet? Terminate the connection.
167
                 * Important for denying async pipelining until, if ever, we want to suppot it.
168
                 * Otherwise requests can get mixed up on the same connection. We still support sync pipelining. */
169
18.5k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
170
18.5k
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
171
18.5k
                    return nullptr;
172
18.5k
                }
173
174
                /* Mark pending request and emit it */
175
18.5k
                httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
176
177
                /* Mark this response as connectionClose if ancient or connection: close */
178
18.5k
                if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
179
18.5k
                    httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
180
18.5k
                }
181
182
                /* Select the router based on SNI (only possible for SSL) */
183
18.5k
                auto *selectedRouter = &httpContextData->router;
184
18.5k
                if constexpr (SSL) {
185
18.5k
                    void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
186
18.5k
                    if (domainRouter) {
187
18.5k
                        selectedRouter = (decltype(selectedRouter)) domainRouter;
188
18.5k
                    }
189
18.5k
                }
190
191
                /* Route the method and URL */
192
18.5k
                selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
193
18.5k
                if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
194
                    /* We have to force close this socket as we have no handler for it */
195
18.5k
                    us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
196
18.5k
                    return nullptr;
197
18.5k
                }
198
199
                /* First of all we need to check if this socket was deleted due to upgrade */
200
18.5k
                if (httpContextData->upgradedWebSocket) {
201
                    /* We differ between closed and upgraded below */
202
18.5k
                    return nullptr;
203
18.5k
                }
204
205
                /* Was the socket closed? */
206
18.5k
                if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
207
18.5k
                    return nullptr;
208
18.5k
                }
209
210
                /* We absolutely have to terminate parsing if shutdown */
211
18.5k
                if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
212
18.5k
                    return nullptr;
213
18.5k
                }
214
215
                /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
216
18.5k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
217
                    /* Throw exception here? */
218
18.5k
                    std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!"
219
18.5k
                              << std::endl
220
18.5k
                              << "\tMethod: \"" << httpRequest->getCaseSensitiveMethod() << "\"" << std::endl
221
18.5k
                              << "\tURL: \"" << httpRequest->getUrl() << "\"" << std::endl;
222
18.5k
                    std::terminate();
223
18.5k
                }
224
225
                /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
226
18.5k
                if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
227
18.5k
                    us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
228
18.5k
                }
229
230
                /* Continue parsing */
231
18.5k
                return s;
232
233
18.5k
            }, [httpResponseData](void *user, std::string_view data, uint64_t maxRemainingBodyLength) -> void * {
234
                /* We always get an empty chunk even if there is no data */
235
18.5k
                if (httpResponseData->inStream) {
236
237
                    /* Todo: can this handle timeout for non-post as well? */
238
18.5k
                    if (maxRemainingBodyLength == 0) {
239
                        /* If we just got the last chunk (or empty chunk), disable timeout */
240
18.5k
                        us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
241
18.5k
                    } else {
242
                        /* We still have some more data coming in later, so reset timeout */
243
                        /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
244
18.5k
                        httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
245
18.5k
                        if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
246
18.5k
                            us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
247
18.5k
                            httpResponseData->received_bytes_per_timeout = 0;
248
18.5k
                        }
249
18.5k
                    }
250
251
                    /* We might respond in the handler, so do not change timeout after this */
252
18.5k
                    httpResponseData->inStream(data, maxRemainingBodyLength);
253
254
                    /* Was the socket closed? */
255
18.5k
                    if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
256
18.5k
                        return nullptr;
257
18.5k
                    }
258
259
                    /* We absolutely have to terminate parsing if shutdown */
260
18.5k
                    if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
261
18.5k
                        return nullptr;
262
18.5k
                    }
263
264
                    /* If we were given the last data chunk, reset data handler to ensure following
265
                     * requests on the same socket won't trigger any previously registered behavior */
266
18.5k
                    if (maxRemainingBodyLength == 0) {
267
18.5k
                        httpResponseData->inStream = nullptr;
268
18.5k
                    }
269
18.5k
                }
270
18.5k
                return user;
271
18.5k
            });
272
273
            /* Mark that we are no longer parsing Http */
274
18.5k
            httpContextData->isParsingHttp = false;
275
276
            /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
277
18.5k
            if (returnedSocket == FULLPTR) {
278
                /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
279
18.5k
                us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
280
18.5k
                us_socket_shutdown(SSL, s);
281
                /* Close any socket on HTTP errors */
282
18.5k
                us_socket_close(SSL, s, 0, nullptr);
283
                /* This just makes the following code act as if the socket was closed from error inside the parser. */
284
18.5k
                returnedSocket = nullptr;
285
18.5k
            }
286
287
            /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
288
18.5k
            if (returnedSocket != nullptr) {
289
                /* Timeout on uncork failure */
290
18.5k
                auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
291
18.5k
                if (failed) {
292
                    /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
293
                    /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
294
18.5k
                    ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
295
18.5k
                }
296
297
                /* We need to check if we should close this socket here now */
298
18.5k
                if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
299
18.5k
                    if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
300
18.5k
                        if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
301
18.5k
                            ((AsyncSocket<SSL> *) s)->shutdown();
302
                            /* We need to force close after sending FIN since we want to hinder
303
                             * clients from keeping to send their huge data */
304
18.5k
                            ((AsyncSocket<SSL> *) s)->close();
305
18.5k
                        }
306
18.5k
                    }
307
18.5k
                }
308
309
18.5k
                return (us_socket_t *) returnedSocket;
310
18.5k
            }
311
312
            /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
313
18.5k
            if (httpContextData->upgradedWebSocket) {
314
                /* This path is only for upgraded websockets */
315
18.5k
                AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
316
317
                /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
318
18.5k
                auto [written, failed] = asyncSocket->uncork();
319
320
                /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
321
18.5k
                if (!failed) {
322
18.5k
                    WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
323
18.5k
                    if (webSocketData->isShuttingDown) {
324
                        /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
325
18.5k
                        asyncSocket->shutdown();
326
18.5k
                    }
327
18.5k
                }
328
329
                /* Reset upgradedWebSocket before we return */
330
18.5k
                httpContextData->upgradedWebSocket = nullptr;
331
332
                /* Return the new upgraded websocket */
333
18.5k
                return (us_socket_t *) asyncSocket;
334
18.5k
            }
335
336
            /* It is okay to uncork a closed socket and we need to */
337
18.5k
            ((AsyncSocket<SSL> *) s)->uncork();
338
339
            /* We cannot return nullptr to the underlying stack in any case */
340
18.5k
            return s;
341
18.5k
        });
342
343
        /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */
344
18.5k
        us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
345
346
18.5k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
347
18.5k
            HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
348
349
            /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
350
18.5k
            if (httpResponseData->onWritable) {
351
                /* 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 */
352
18.5k
                us_socket_timeout(SSL, s, 0);
353
354
                /* We expect the developer to return whether or not write was successful (true).
355
                 * If write was never called, the developer should still return true so that we may drain. */
356
18.5k
                bool success = httpResponseData->callOnWritable(httpResponseData->offset);
357
358
                /* The developer indicated that their onWritable failed. */
359
18.5k
                if (!success) {
360
                    /* Skip testing if we can drain anything since that might perform an extra syscall */
361
18.5k
                    return s;
362
18.5k
                }
363
364
                /* We don't want to fall through since we don't want to mess with timeout.
365
                 * It makes little sense to drain any backpressure when the user has registered onWritable. */
366
18.5k
                return s;
367
18.5k
            }
368
369
            /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
370
            /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
371
372
            /* Should we close this connection after a response - and is this response really done? */
373
18.5k
            if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
374
18.5k
                if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
375
18.5k
                    if (asyncSocket->getBufferedAmount() == 0) {
376
18.5k
                        asyncSocket->shutdown();
377
                        /* We need to force close after sending FIN since we want to hinder
378
                         * clients from keeping to send their huge data */
379
18.5k
                        asyncSocket->close();
380
18.5k
                    }
381
18.5k
                }
382
18.5k
            }
383
384
            /* Expect another writable event, or another request within the timeout */
385
18.5k
            asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
386
387
18.5k
            return s;
388
18.5k
        });
389
390
        /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
391
18.5k
        us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
392
393
            /* We do not care for half closed sockets */
394
18.5k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
395
18.5k
            return asyncSocket->close();
396
397
18.5k
        });
398
399
        /* Handle socket timeouts, simply close them so to not confuse client with FIN */
400
18.5k
        us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
401
402
            /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
403
18.5k
            AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
404
18.5k
            return asyncSocket->close();
405
406
18.5k
        });
407
408
18.5k
        return this;
409
18.5k
    }
410
411
public:
412
    /* Construct a new HttpContext using specified loop */
413
24.7k
    static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) {
414
24.7k
        HttpContext *httpContext;
415
416
24.7k
        httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options);
417
418
24.7k
        if (!httpContext) {
419
0
            return nullptr;
420
0
        }
421
422
        /* Init socket context data */
423
24.7k
        new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
424
24.7k
        return httpContext->init();
425
24.7k
    }
uWS::HttpContext<true>::create(uWS::Loop*, us_socket_context_options_t)
Line
Count
Source
413
6.26k
    static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) {
414
6.26k
        HttpContext *httpContext;
415
416
6.26k
        httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options);
417
418
6.26k
        if (!httpContext) {
419
0
            return nullptr;
420
0
        }
421
422
        /* Init socket context data */
423
6.26k
        new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
424
6.26k
        return httpContext->init();
425
6.26k
    }
uWS::HttpContext<false>::create(uWS::Loop*, us_socket_context_options_t)
Line
Count
Source
413
18.5k
    static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) {
414
18.5k
        HttpContext *httpContext;
415
416
18.5k
        httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options);
417
418
18.5k
        if (!httpContext) {
419
0
            return nullptr;
420
0
        }
421
422
        /* Init socket context data */
423
18.5k
        new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
424
18.5k
        return httpContext->init();
425
18.5k
    }
426
427
    /* Destruct the HttpContext, it does not follow RAII */
428
24.7k
    void free() {
429
        /* Destruct socket context data */
430
24.7k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
431
24.7k
        httpContextData->~HttpContextData<SSL>();
432
433
        /* Free the socket context in whole */
434
24.7k
        us_socket_context_free(SSL, getSocketContext());
435
24.7k
    }
uWS::HttpContext<true>::free()
Line
Count
Source
428
6.26k
    void free() {
429
        /* Destruct socket context data */
430
6.26k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
431
6.26k
        httpContextData->~HttpContextData<SSL>();
432
433
        /* Free the socket context in whole */
434
6.26k
        us_socket_context_free(SSL, getSocketContext());
435
6.26k
    }
uWS::HttpContext<false>::free()
Line
Count
Source
428
18.5k
    void free() {
429
        /* Destruct socket context data */
430
18.5k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
431
18.5k
        httpContextData->~HttpContextData<SSL>();
432
433
        /* Free the socket context in whole */
434
18.5k
        us_socket_context_free(SSL, getSocketContext());
435
18.5k
    }
436
437
    void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
438
        getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler));
439
    }
440
441
    /* Register an HTTP route handler acording to URL pattern */
442
85.2k
    void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
443
85.2k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
444
445
        /* Todo: This is ugly, fix */
446
85.2k
        std::vector<std::string> methods;
447
85.2k
        if (method == "*") {
448
31.8k
            methods = {"*"};
449
53.3k
        } else {
450
53.3k
            methods = {method};
451
53.3k
        }
452
453
85.2k
        uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
454
455
        /* If we are passed nullptr then remove this */
456
85.2k
        if (!handler) {
457
0
            httpContextData->currentRouter->remove(methods[0], pattern, priority);
458
0
            return;
459
0
        }
460
461
        /* Record this route's parameter offsets */
462
85.2k
        std::map<std::string, unsigned short, std::less<>> parameterOffsets;
463
85.2k
        unsigned short offset = 0;
464
350k
        for (unsigned int i = 0; i < pattern.length(); i++) {
465
264k
            if (pattern[i] == ':') {
466
7.06k
                i++;
467
7.06k
                unsigned int start = i;
468
42.4k
                while (i < pattern.length() && pattern[i] != '/') {
469
35.3k
                    i++;
470
35.3k
                }
471
7.06k
                parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
472
                //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
473
7.06k
                offset++;
474
7.06k
            }
475
264k
        }
476
477
504k
        httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
478
504k
            auto user = r->getUserData();
479
504k
            user.httpRequest->setYield(false);
480
504k
            user.httpRequest->setParameters(r->getParameters());
481
504k
            user.httpRequest->setParameterOffsets(&parameterOffsets);
482
483
            /* Middleware? Automatically respond to expectations */
484
504k
            std::string_view expect = user.httpRequest->getHeader("expect");
485
504k
            if (expect.length() && expect == "100-continue") {
486
1.55k
                user.httpResponse->writeContinue();
487
1.55k
            }
488
489
504k
            handler(user.httpResponse, user.httpRequest);
490
491
            /* If any handler yielded, the router will keep looking for a suitable handler. */
492
504k
            if (user.httpRequest->getYield()) {
493
24.3k
                return false;
494
24.3k
            }
495
480k
            return true;
496
504k
        }, 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
477
130k
        httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
478
130k
            auto user = r->getUserData();
479
130k
            user.httpRequest->setYield(false);
480
130k
            user.httpRequest->setParameters(r->getParameters());
481
130k
            user.httpRequest->setParameterOffsets(&parameterOffsets);
482
483
            /* Middleware? Automatically respond to expectations */
484
130k
            std::string_view expect = user.httpRequest->getHeader("expect");
485
130k
            if (expect.length() && expect == "100-continue") {
486
196
                user.httpResponse->writeContinue();
487
196
            }
488
489
130k
            handler(user.httpResponse, user.httpRequest);
490
491
            /* If any handler yielded, the router will keep looking for a suitable handler. */
492
130k
            if (user.httpRequest->getYield()) {
493
4.50k
                return false;
494
4.50k
            }
495
126k
            return true;
496
130k
        }, 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
477
374k
        httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
478
374k
            auto user = r->getUserData();
479
374k
            user.httpRequest->setYield(false);
480
374k
            user.httpRequest->setParameters(r->getParameters());
481
374k
            user.httpRequest->setParameterOffsets(&parameterOffsets);
482
483
            /* Middleware? Automatically respond to expectations */
484
374k
            std::string_view expect = user.httpRequest->getHeader("expect");
485
374k
            if (expect.length() && expect == "100-continue") {
486
1.35k
                user.httpResponse->writeContinue();
487
1.35k
            }
488
489
374k
            handler(user.httpResponse, user.httpRequest);
490
491
            /* If any handler yielded, the router will keep looking for a suitable handler. */
492
374k
            if (user.httpRequest->getYield()) {
493
19.8k
                return false;
494
19.8k
            }
495
354k
            return true;
496
374k
        }, priority);
497
85.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
442
12.5k
    void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
443
12.5k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
444
445
        /* Todo: This is ugly, fix */
446
12.5k
        std::vector<std::string> methods;
447
12.5k
        if (method == "*") {
448
6.26k
            methods = {"*"};
449
6.26k
        } else {
450
6.26k
            methods = {method};
451
6.26k
        }
452
453
12.5k
        uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
454
455
        /* If we are passed nullptr then remove this */
456
12.5k
        if (!handler) {
457
0
            httpContextData->currentRouter->remove(methods[0], pattern, priority);
458
0
            return;
459
0
        }
460
461
        /* Record this route's parameter offsets */
462
12.5k
        std::map<std::string, unsigned short, std::less<>> parameterOffsets;
463
12.5k
        unsigned short offset = 0;
464
37.6k
        for (unsigned int i = 0; i < pattern.length(); i++) {
465
25.0k
            if (pattern[i] == ':') {
466
0
                i++;
467
0
                unsigned int start = i;
468
0
                while (i < pattern.length() && pattern[i] != '/') {
469
0
                    i++;
470
0
                }
471
0
                parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
472
                //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
473
0
                offset++;
474
0
            }
475
25.0k
        }
476
477
12.5k
        httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
478
12.5k
            auto user = r->getUserData();
479
12.5k
            user.httpRequest->setYield(false);
480
12.5k
            user.httpRequest->setParameters(r->getParameters());
481
12.5k
            user.httpRequest->setParameterOffsets(&parameterOffsets);
482
483
            /* Middleware? Automatically respond to expectations */
484
12.5k
            std::string_view expect = user.httpRequest->getHeader("expect");
485
12.5k
            if (expect.length() && expect == "100-continue") {
486
12.5k
                user.httpResponse->writeContinue();
487
12.5k
            }
488
489
12.5k
            handler(user.httpResponse, user.httpRequest);
490
491
            /* If any handler yielded, the router will keep looking for a suitable handler. */
492
12.5k
            if (user.httpRequest->getYield()) {
493
12.5k
                return false;
494
12.5k
            }
495
12.5k
            return true;
496
12.5k
        }, priority);
497
12.5k
    }
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
442
72.6k
    void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
443
72.6k
        HttpContextData<SSL> *httpContextData = getSocketContextData();
444
445
        /* Todo: This is ugly, fix */
446
72.6k
        std::vector<std::string> methods;
447
72.6k
        if (method == "*") {
448
25.5k
            methods = {"*"};
449
47.1k
        } else {
450
47.1k
            methods = {method};
451
47.1k
        }
452
453
72.6k
        uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
454
455
        /* If we are passed nullptr then remove this */
456
72.6k
        if (!handler) {
457
0
            httpContextData->currentRouter->remove(methods[0], pattern, priority);
458
0
            return;
459
0
        }
460
461
        /* Record this route's parameter offsets */
462
72.6k
        std::map<std::string, unsigned short, std::less<>> parameterOffsets;
463
72.6k
        unsigned short offset = 0;
464
312k
        for (unsigned int i = 0; i < pattern.length(); i++) {
465
239k
            if (pattern[i] == ':') {
466
7.06k
                i++;
467
7.06k
                unsigned int start = i;
468
42.4k
                while (i < pattern.length() && pattern[i] != '/') {
469
35.3k
                    i++;
470
35.3k
                }
471
7.06k
                parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
472
                //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
473
7.06k
                offset++;
474
7.06k
            }
475
239k
        }
476
477
72.6k
        httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
478
72.6k
            auto user = r->getUserData();
479
72.6k
            user.httpRequest->setYield(false);
480
72.6k
            user.httpRequest->setParameters(r->getParameters());
481
72.6k
            user.httpRequest->setParameterOffsets(&parameterOffsets);
482
483
            /* Middleware? Automatically respond to expectations */
484
72.6k
            std::string_view expect = user.httpRequest->getHeader("expect");
485
72.6k
            if (expect.length() && expect == "100-continue") {
486
72.6k
                user.httpResponse->writeContinue();
487
72.6k
            }
488
489
72.6k
            handler(user.httpResponse, user.httpRequest);
490
491
            /* If any handler yielded, the router will keep looking for a suitable handler. */
492
72.6k
            if (user.httpRequest->getYield()) {
493
72.6k
                return false;
494
72.6k
            }
495
72.6k
            return true;
496
72.6k
        }, priority);
497
72.6k
    }
498
499
    /* Listen to port using this HttpContext */
500
24.7k
    us_listen_socket_t *listen(const char *host, int port, int options) {
501
24.7k
        return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>));
502
24.7k
    }
uWS::HttpContext<true>::listen(char const*, int, int)
Line
Count
Source
500
6.26k
    us_listen_socket_t *listen(const char *host, int port, int options) {
501
6.26k
        return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>));
502
6.26k
    }
uWS::HttpContext<false>::listen(char const*, int, int)
Line
Count
Source
500
18.5k
    us_listen_socket_t *listen(const char *host, int port, int options) {
501
18.5k
        return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>));
502
18.5k
    }
503
504
    /* Listen to unix domain socket using this HttpContext */
505
    us_listen_socket_t *listen(const char *path, int options) {
506
        return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>));
507
    }
508
509
    void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(struct us_socket_context_t *, LIBUS_SOCKET_DESCRIPTOR, char *, int)) {
510
        us_socket_context_on_pre_open(SSL, getSocketContext(), handler);
511
    }
512
513
    /* Adopt an externally accepted socket into this HttpContext */
514
    us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd, char *ip, int ip_length) {
515
        return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd, sizeof(HttpResponseData<SSL>), ip, ip_length);
516
    }
517
};
518
519
}
520
521
#endif // UWS_HTTPCONTEXT_H