Coverage Report

Created: 2023-09-25 07:18

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