/src/uWebSockets/src/WebSocketContext.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_WEBSOCKETCONTEXT_H |
19 | | #define UWS_WEBSOCKETCONTEXT_H |
20 | | |
21 | | #include "WebSocketContextData.h" |
22 | | #include "WebSocketProtocol.h" |
23 | | #include "WebSocketData.h" |
24 | | #include "WebSocket.h" |
25 | | |
26 | | namespace uWS { |
27 | | |
28 | | template <bool SSL, bool isServer, typename USERDATA> |
29 | | struct WebSocketContext { |
30 | | template <bool> friend struct TemplatedApp; |
31 | | template <bool, typename> friend struct WebSocketProtocol; |
32 | | private: |
33 | | WebSocketContext() = delete; |
34 | | |
35 | 225k | us_socket_context_t *getSocketContext() { |
36 | 225k | return (us_socket_context_t *) this; |
37 | 225k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::getSocketContext() Line | Count | Source | 35 | 35.6k | us_socket_context_t *getSocketContext() { | 36 | 35.6k | return (us_socket_context_t *) this; | 37 | 35.6k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getSocketContext() Line | Count | Source | 35 | 90.2k | us_socket_context_t *getSocketContext() { | 36 | 90.2k | return (us_socket_context_t *) this; | 37 | 90.2k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getSocketContext() Line | Count | Source | 35 | 99.3k | us_socket_context_t *getSocketContext() { | 36 | 99.3k | return (us_socket_context_t *) this; | 37 | 99.3k | } |
|
38 | | |
39 | 547k | WebSocketContextData<SSL, USERDATA> *getExt() { |
40 | 547k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); |
41 | 547k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::getExt() Line | Count | Source | 39 | 95.1k | WebSocketContextData<SSL, USERDATA> *getExt() { | 40 | 95.1k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 41 | 95.1k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getExt() Line | Count | Source | 39 | 222k | WebSocketContextData<SSL, USERDATA> *getExt() { | 40 | 222k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 41 | 222k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getExt() Line | Count | Source | 39 | 230k | WebSocketContextData<SSL, USERDATA> *getExt() { | 40 | 230k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 41 | 230k | } |
|
42 | | |
43 | | /* If we have negotiated compression, set this frame compressed */ |
44 | 32.1k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { |
45 | 32.1k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); |
46 | | |
47 | 32.1k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { |
48 | 3.19k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; |
49 | 3.19k | return true; |
50 | 28.9k | } else { |
51 | 28.9k | return false; |
52 | 28.9k | } |
53 | 32.1k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 16.3k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 16.3k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 16.3k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 0 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 0 | return true; | 50 | 16.3k | } else { | 51 | 16.3k | return false; | 52 | 16.3k | } | 53 | 16.3k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 2.54k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 2.54k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 2.54k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 1.45k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 1.45k | return true; | 50 | 1.45k | } else { | 51 | 1.09k | return false; | 52 | 1.09k | } | 53 | 2.54k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 13.2k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 13.2k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 13.2k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 1.74k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 1.74k | return true; | 50 | 11.5k | } else { | 51 | 11.5k | return false; | 52 | 11.5k | } | 53 | 13.2k | } |
|
54 | | |
55 | 106k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { |
56 | 106k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); |
57 | 106k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::forceClose(uWS::WebSocketState<true>*, void*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 55 | 51.7k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 51.7k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 51.7k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::forceClose(uWS::WebSocketState<true>*, void*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 55 | 11.9k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 11.9k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 11.9k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::forceClose(uWS::WebSocketState<true>*, void*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 55 | 43.2k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 43.2k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 43.2k | } |
|
58 | | |
59 | | /* Returns true on breakage */ |
60 | 1.30M | static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState, void *s) { |
61 | | /* WebSocketData and WebSocketContextData */ |
62 | 1.30M | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
63 | 1.30M | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); |
64 | | |
65 | | /* Is this a non-control frame? */ |
66 | 1.30M | if (opCode < 3) { |
67 | | /* Did we get everything in one go? */ |
68 | 1.23M | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { |
69 | | |
70 | | /* Handle compressed frame */ |
71 | 1.14M | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { |
72 | 1.36k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; |
73 | | |
74 | 1.36k | LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) s))); |
75 | | /* Decompress using shared or dedicated decompressor */ |
76 | 1.36k | std::optional<std::string_view> inflatedFrame; |
77 | 1.36k | if (webSocketData->inflationStream) { |
78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); |
79 | 1.36k | } else { |
80 | 1.36k | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); |
81 | 1.36k | } |
82 | | |
83 | 1.36k | if (!inflatedFrame.has_value()) { |
84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); |
85 | 0 | return true; |
86 | 1.36k | } else { |
87 | 1.36k | data = (char *) inflatedFrame->data(); |
88 | 1.36k | length = inflatedFrame->length(); |
89 | 1.36k | } |
90 | 1.36k | } |
91 | | |
92 | | /* Check text messages for Utf-8 validity */ |
93 | 1.14M | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { |
94 | 20.7k | forceClose(webSocketState, s, ERR_INVALID_TEXT); |
95 | 20.7k | return true; |
96 | 20.7k | } |
97 | | |
98 | | /* Emit message event & break if we are closed or shut down when returning */ |
99 | 1.11M | if (webSocketContextData->messageHandler) { |
100 | 1.11M | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); |
101 | 1.11M | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
102 | 1.03k | return true; |
103 | 1.03k | } |
104 | 1.11M | } |
105 | 1.11M | } else { |
106 | | /* Allocate fragment buffer up front first time */ |
107 | 96.0k | if (!webSocketData->fragmentBuffer.length()) { |
108 | 35.8k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); |
109 | 35.8k | } |
110 | | /* Fragments forming a big message are not caught until appending them */ |
111 | 96.0k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { |
112 | 585 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); |
113 | 585 | return true; |
114 | 585 | } |
115 | 95.4k | webSocketData->fragmentBuffer.append(data, length); |
116 | | |
117 | | /* Are we done now? */ |
118 | | // todo: what if we don't have any remaining bytes yet we are not fin? forceclose! |
119 | 95.4k | if (!remainingBytes && fin) { |
120 | | |
121 | | /* Handle compression */ |
122 | 9.80k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { |
123 | 1.63k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; |
124 | | |
125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ |
126 | 1.63k | webSocketData->fragmentBuffer.append("123456789"); |
127 | | |
128 | 1.63k | LoopData *loopData = (LoopData *) us_loop_ext( |
129 | 1.63k | us_socket_context_loop(SSL, |
130 | 1.63k | us_socket_context(SSL, (us_socket_t *) s) |
131 | 1.63k | ) |
132 | 1.63k | ); |
133 | | |
134 | | /* Decompress using shared or dedicated decompressor */ |
135 | 1.63k | std::optional<std::string_view> inflatedFrame; |
136 | 1.63k | if (webSocketData->inflationStream) { |
137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); |
138 | 1.63k | } else { |
139 | 1.63k | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); |
140 | 1.63k | } |
141 | | |
142 | 1.63k | if (!inflatedFrame.has_value()) { |
143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); |
144 | 0 | return true; |
145 | 1.63k | } else { |
146 | 1.63k | data = (char *) inflatedFrame->data(); |
147 | 1.63k | length = inflatedFrame->length(); |
148 | 1.63k | } |
149 | | |
150 | | |
151 | 8.17k | } else { |
152 | | // reset length and data ptrs |
153 | 8.17k | length = webSocketData->fragmentBuffer.length(); |
154 | 8.17k | data = webSocketData->fragmentBuffer.data(); |
155 | 8.17k | } |
156 | | |
157 | | /* Check text messages for Utf-8 validity */ |
158 | 9.80k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { |
159 | 851 | forceClose(webSocketState, s, ERR_INVALID_TEXT); |
160 | 851 | return true; |
161 | 851 | } |
162 | | |
163 | | /* Emit message and check for shutdown or close */ |
164 | 8.95k | if (webSocketContextData->messageHandler) { |
165 | 8.68k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); |
166 | 8.68k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
167 | 408 | return true; |
168 | 408 | } |
169 | 8.68k | } |
170 | | |
171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ |
172 | 8.54k | webSocketData->fragmentBuffer.clear(); |
173 | 8.54k | } |
174 | 95.4k | } |
175 | 1.23M | } else { |
176 | | /* Control frames need the websocket to send pings, pongs and close */ |
177 | 68.3k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; |
178 | | |
179 | 68.3k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { |
180 | 44.3k | if (opCode == CLOSE) { |
181 | 15.0k | auto closeFrame = protocol::parseClosePayload(data, length); |
182 | 15.0k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); |
183 | 15.0k | return true; |
184 | 29.3k | } else { |
185 | 29.3k | if (opCode == PING) { |
186 | 22.9k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); |
187 | 22.9k | if (webSocketContextData->pingHandler) { |
188 | 22.6k | webSocketContextData->pingHandler(webSocket, {data, length}); |
189 | 22.6k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
190 | 0 | return true; |
191 | 0 | } |
192 | 22.6k | } |
193 | 22.9k | } else if (opCode == PONG) { |
194 | 6.38k | if (webSocketContextData->pongHandler) { |
195 | 6.19k | webSocketContextData->pongHandler(webSocket, {data, length}); |
196 | 6.19k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
197 | 0 | return true; |
198 | 0 | } |
199 | 6.19k | } |
200 | 6.38k | } |
201 | 29.3k | } |
202 | 44.3k | } else { |
203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ |
204 | 23.9k | webSocketData->fragmentBuffer.append(data, length); |
205 | 23.9k | webSocketData->controlTipLength += (unsigned int) length; |
206 | | |
207 | 23.9k | if (!remainingBytes && fin) { |
208 | 10.0k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; |
209 | 10.0k | if (opCode == CLOSE) { |
210 | 5.61k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); |
211 | 5.61k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); |
212 | 5.61k | return true; |
213 | 5.61k | } else { |
214 | 4.44k | if (opCode == PING) { |
215 | 1.63k | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); |
216 | 1.63k | if (webSocketContextData->pingHandler) { |
217 | 1.42k | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); |
218 | 1.42k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
219 | 0 | return true; |
220 | 0 | } |
221 | 1.42k | } |
222 | 2.81k | } else if (opCode == PONG) { |
223 | 2.81k | if (webSocketContextData->pongHandler) { |
224 | 2.35k | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); |
225 | 2.35k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
226 | 0 | return true; |
227 | 0 | } |
228 | 2.35k | } |
229 | 2.81k | } |
230 | 4.44k | } |
231 | | |
232 | | /* Same here, we do not care for any particular smart allocation scheme */ |
233 | 4.44k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); |
234 | 4.44k | webSocketData->controlTipLength = 0; |
235 | 4.44k | } |
236 | 23.9k | } |
237 | 68.3k | } |
238 | 1.26M | return false; |
239 | 1.30M | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::handleFragment(char*, unsigned long, unsigned int, int, bool, uWS::WebSocketState<true>*, void*) Line | Count | Source | 60 | 1.10M | static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState, void *s) { | 61 | | /* WebSocketData and WebSocketContextData */ | 62 | 1.10M | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 1.10M | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 1.10M | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 1.08M | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 1.05M | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 72 | 0 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 73 | |
| 74 | 0 | LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) s))); | 75 | | /* Decompress using shared or dedicated decompressor */ | 76 | 0 | std::optional<std::string_view> inflatedFrame; | 77 | 0 | if (webSocketData->inflationStream) { | 78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); | 79 | 0 | } else { | 80 | 0 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); | 81 | 0 | } | 82 | |
| 83 | 0 | if (!inflatedFrame.has_value()) { | 84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 85 | 0 | return true; | 86 | 0 | } else { | 87 | 0 | data = (char *) inflatedFrame->data(); | 88 | 0 | length = inflatedFrame->length(); | 89 | 0 | } | 90 | 0 | } | 91 | | | 92 | | /* Check text messages for Utf-8 validity */ | 93 | 1.05M | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 14.3k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 14.3k | return true; | 96 | 14.3k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 1.04M | if (webSocketContextData->messageHandler) { | 100 | 1.04M | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 1.04M | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 0 | return true; | 103 | 0 | } | 104 | 1.04M | } | 105 | 1.04M | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 28.1k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 17.3k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 17.3k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 28.1k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 205 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 205 | return true; | 114 | 205 | } | 115 | 27.9k | webSocketData->fragmentBuffer.append(data, length); | 116 | | | 117 | | /* Are we done now? */ | 118 | | // todo: what if we don't have any remaining bytes yet we are not fin? forceclose! | 119 | 27.9k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 4.09k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 123 | 0 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 124 | | | 125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ | 126 | 0 | webSocketData->fragmentBuffer.append("123456789"); | 127 | |
| 128 | 0 | LoopData *loopData = (LoopData *) us_loop_ext( | 129 | 0 | us_socket_context_loop(SSL, | 130 | 0 | us_socket_context(SSL, (us_socket_t *) s) | 131 | 0 | ) | 132 | 0 | ); | 133 | | | 134 | | /* Decompress using shared or dedicated decompressor */ | 135 | 0 | std::optional<std::string_view> inflatedFrame; | 136 | 0 | if (webSocketData->inflationStream) { | 137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); | 138 | 0 | } else { | 139 | 0 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); | 140 | 0 | } | 141 | |
| 142 | 0 | if (!inflatedFrame.has_value()) { | 143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 144 | 0 | return true; | 145 | 0 | } else { | 146 | 0 | data = (char *) inflatedFrame->data(); | 147 | 0 | length = inflatedFrame->length(); | 148 | 0 | } | 149 | | | 150 | |
| 151 | 4.09k | } else { | 152 | | // reset length and data ptrs | 153 | 4.09k | length = webSocketData->fragmentBuffer.length(); | 154 | 4.09k | data = webSocketData->fragmentBuffer.data(); | 155 | 4.09k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 4.09k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 319 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 319 | return true; | 161 | 319 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 3.77k | if (webSocketContextData->messageHandler) { | 165 | 3.77k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 3.77k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 0 | return true; | 168 | 0 | } | 169 | 3.77k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 3.77k | webSocketData->fragmentBuffer.clear(); | 173 | 3.77k | } | 174 | 27.9k | } | 175 | 1.08M | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 19.0k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 19.0k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 11.7k | if (opCode == CLOSE) { | 181 | 7.49k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 7.49k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 7.49k | return true; | 184 | 7.49k | } else { | 185 | 4.21k | if (opCode == PING) { | 186 | 3.82k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 3.82k | if (webSocketContextData->pingHandler) { | 188 | 3.82k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 3.82k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 3.82k | } | 193 | 3.82k | } else if (opCode == PONG) { | 194 | 391 | if (webSocketContextData->pongHandler) { | 195 | 391 | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 391 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 391 | } | 200 | 391 | } | 201 | 4.21k | } | 202 | 11.7k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 7.29k | webSocketData->fragmentBuffer.append(data, length); | 205 | 7.29k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 7.29k | if (!remainingBytes && fin) { | 208 | 3.24k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.24k | if (opCode == CLOSE) { | 210 | 2.12k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 2.12k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 2.12k | return true; | 213 | 2.12k | } else { | 214 | 1.12k | if (opCode == PING) { | 215 | 760 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 760 | if (webSocketContextData->pingHandler) { | 217 | 760 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 760 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 760 | } | 222 | 760 | } else if (opCode == PONG) { | 223 | 361 | if (webSocketContextData->pongHandler) { | 224 | 361 | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 361 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 361 | } | 229 | 361 | } | 230 | 1.12k | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 1.12k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 1.12k | webSocketData->controlTipLength = 0; | 235 | 1.12k | } | 236 | 7.29k | } | 237 | 19.0k | } | 238 | 1.07M | return false; | 239 | 1.10M | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::handleFragment(char*, unsigned long, unsigned int, int, bool, uWS::WebSocketState<true>*, void*) Line | Count | Source | 60 | 82.0k | static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState, void *s) { | 61 | | /* WebSocketData and WebSocketContextData */ | 62 | 82.0k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 82.0k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 82.0k | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 61.5k | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 12.7k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 72 | 699 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 73 | | | 74 | 699 | LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) s))); | 75 | | /* Decompress using shared or dedicated decompressor */ | 76 | 699 | std::optional<std::string_view> inflatedFrame; | 77 | 699 | if (webSocketData->inflationStream) { | 78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); | 79 | 699 | } else { | 80 | 699 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); | 81 | 699 | } | 82 | | | 83 | 699 | if (!inflatedFrame.has_value()) { | 84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 85 | 0 | return true; | 86 | 699 | } else { | 87 | 699 | data = (char *) inflatedFrame->data(); | 88 | 699 | length = inflatedFrame->length(); | 89 | 699 | } | 90 | 699 | } | 91 | | | 92 | | /* Check text messages for Utf-8 validity */ | 93 | 12.7k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 2.35k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 2.35k | return true; | 96 | 2.35k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 10.4k | if (webSocketContextData->messageHandler) { | 100 | 8.56k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 8.56k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 0 | return true; | 103 | 0 | } | 104 | 8.56k | } | 105 | 48.7k | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 48.7k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 7.90k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 7.90k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 48.7k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 73 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 73 | return true; | 114 | 73 | } | 115 | 48.7k | webSocketData->fragmentBuffer.append(data, length); | 116 | | | 117 | | /* Are we done now? */ | 118 | | // todo: what if we don't have any remaining bytes yet we are not fin? forceclose! | 119 | 48.7k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 2.40k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 123 | 658 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 124 | | | 125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ | 126 | 658 | webSocketData->fragmentBuffer.append("123456789"); | 127 | | | 128 | 658 | LoopData *loopData = (LoopData *) us_loop_ext( | 129 | 658 | us_socket_context_loop(SSL, | 130 | 658 | us_socket_context(SSL, (us_socket_t *) s) | 131 | 658 | ) | 132 | 658 | ); | 133 | | | 134 | | /* Decompress using shared or dedicated decompressor */ | 135 | 658 | std::optional<std::string_view> inflatedFrame; | 136 | 658 | if (webSocketData->inflationStream) { | 137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); | 138 | 658 | } else { | 139 | 658 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); | 140 | 658 | } | 141 | | | 142 | 658 | if (!inflatedFrame.has_value()) { | 143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 144 | 0 | return true; | 145 | 658 | } else { | 146 | 658 | data = (char *) inflatedFrame->data(); | 147 | 658 | length = inflatedFrame->length(); | 148 | 658 | } | 149 | | | 150 | | | 151 | 1.75k | } else { | 152 | | // reset length and data ptrs | 153 | 1.75k | length = webSocketData->fragmentBuffer.length(); | 154 | 1.75k | data = webSocketData->fragmentBuffer.data(); | 155 | 1.75k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 2.40k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 230 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 230 | return true; | 161 | 230 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 2.17k | if (webSocketContextData->messageHandler) { | 165 | 1.91k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 1.91k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 0 | return true; | 168 | 0 | } | 169 | 1.91k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 2.17k | webSocketData->fragmentBuffer.clear(); | 173 | 2.17k | } | 174 | 48.7k | } | 175 | 61.5k | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 20.4k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 20.4k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 12.0k | if (opCode == CLOSE) { | 181 | 4.18k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 4.18k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 4.18k | return true; | 184 | 7.90k | } else { | 185 | 7.90k | if (opCode == PING) { | 186 | 6.91k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 6.91k | if (webSocketContextData->pingHandler) { | 188 | 6.64k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 6.64k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 6.64k | } | 193 | 6.91k | } else if (opCode == PONG) { | 194 | 999 | if (webSocketContextData->pongHandler) { | 195 | 804 | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 804 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 804 | } | 200 | 999 | } | 201 | 7.90k | } | 202 | 12.0k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 8.40k | webSocketData->fragmentBuffer.append(data, length); | 205 | 8.40k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 8.40k | if (!remainingBytes && fin) { | 208 | 3.24k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.24k | if (opCode == CLOSE) { | 210 | 1.81k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 1.81k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 1.81k | return true; | 213 | 1.81k | } else { | 214 | 1.42k | if (opCode == PING) { | 215 | 542 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 542 | if (webSocketContextData->pingHandler) { | 217 | 333 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 333 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 333 | } | 222 | 884 | } else if (opCode == PONG) { | 223 | 884 | if (webSocketContextData->pongHandler) { | 224 | 422 | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 422 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 422 | } | 229 | 884 | } | 230 | 1.42k | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 1.42k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 1.42k | webSocketData->controlTipLength = 0; | 235 | 1.42k | } | 236 | 8.40k | } | 237 | 20.4k | } | 238 | 73.3k | return false; | 239 | 82.0k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::handleFragment(char*, unsigned long, unsigned int, int, bool, uWS::WebSocketState<true>*, void*) Line | Count | Source | 60 | 118k | static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState, void *s) { | 61 | | /* WebSocketData and WebSocketContextData */ | 62 | 118k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 118k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 118k | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 89.7k | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 70.5k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 72 | 668 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 73 | | | 74 | 668 | LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) s))); | 75 | | /* Decompress using shared or dedicated decompressor */ | 76 | 668 | std::optional<std::string_view> inflatedFrame; | 77 | 668 | if (webSocketData->inflationStream) { | 78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); | 79 | 668 | } else { | 80 | 668 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); | 81 | 668 | } | 82 | | | 83 | 668 | if (!inflatedFrame.has_value()) { | 84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 85 | 0 | return true; | 86 | 668 | } else { | 87 | 668 | data = (char *) inflatedFrame->data(); | 88 | 668 | length = inflatedFrame->length(); | 89 | 668 | } | 90 | 668 | } | 91 | | | 92 | | /* Check text messages for Utf-8 validity */ | 93 | 70.5k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 4.09k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 4.09k | return true; | 96 | 4.09k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 66.4k | if (webSocketContextData->messageHandler) { | 100 | 66.4k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 66.4k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 1.03k | return true; | 103 | 1.03k | } | 104 | 66.4k | } | 105 | 66.4k | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 19.1k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 10.5k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 10.5k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 19.1k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 307 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 307 | return true; | 114 | 307 | } | 115 | 18.8k | webSocketData->fragmentBuffer.append(data, length); | 116 | | | 117 | | /* Are we done now? */ | 118 | | // todo: what if we don't have any remaining bytes yet we are not fin? forceclose! | 119 | 18.8k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 3.29k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 123 | 972 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 124 | | | 125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ | 126 | 972 | webSocketData->fragmentBuffer.append("123456789"); | 127 | | | 128 | 972 | LoopData *loopData = (LoopData *) us_loop_ext( | 129 | 972 | us_socket_context_loop(SSL, | 130 | 972 | us_socket_context(SSL, (us_socket_t *) s) | 131 | 972 | ) | 132 | 972 | ); | 133 | | | 134 | | /* Decompress using shared or dedicated decompressor */ | 135 | 972 | std::optional<std::string_view> inflatedFrame; | 136 | 972 | if (webSocketData->inflationStream) { | 137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); | 138 | 972 | } else { | 139 | 972 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); | 140 | 972 | } | 141 | | | 142 | 972 | if (!inflatedFrame.has_value()) { | 143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 144 | 0 | return true; | 145 | 972 | } else { | 146 | 972 | data = (char *) inflatedFrame->data(); | 147 | 972 | length = inflatedFrame->length(); | 148 | 972 | } | 149 | | | 150 | | | 151 | 2.32k | } else { | 152 | | // reset length and data ptrs | 153 | 2.32k | length = webSocketData->fragmentBuffer.length(); | 154 | 2.32k | data = webSocketData->fragmentBuffer.data(); | 155 | 2.32k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 3.29k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 302 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 302 | return true; | 161 | 302 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 2.99k | if (webSocketContextData->messageHandler) { | 165 | 2.99k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 2.99k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 408 | return true; | 168 | 408 | } | 169 | 2.99k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 2.58k | webSocketData->fragmentBuffer.clear(); | 173 | 2.58k | } | 174 | 18.8k | } | 175 | 89.7k | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 28.8k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 28.8k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 20.6k | if (opCode == CLOSE) { | 181 | 3.42k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 3.42k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 3.42k | return true; | 184 | 17.1k | } else { | 185 | 17.1k | if (opCode == PING) { | 186 | 12.1k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 12.1k | if (webSocketContextData->pingHandler) { | 188 | 12.1k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 12.1k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 12.1k | } | 193 | 12.1k | } else if (opCode == PONG) { | 194 | 4.99k | if (webSocketContextData->pongHandler) { | 195 | 4.99k | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 4.99k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 4.99k | } | 200 | 4.99k | } | 201 | 17.1k | } | 202 | 20.6k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 8.24k | webSocketData->fragmentBuffer.append(data, length); | 205 | 8.24k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 8.24k | if (!remainingBytes && fin) { | 208 | 3.57k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.57k | if (opCode == CLOSE) { | 210 | 1.67k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 1.67k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 1.67k | return true; | 213 | 1.89k | } else { | 214 | 1.89k | if (opCode == PING) { | 215 | 329 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 329 | if (webSocketContextData->pingHandler) { | 217 | 329 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 329 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 329 | } | 222 | 1.56k | } else if (opCode == PONG) { | 223 | 1.56k | if (webSocketContextData->pongHandler) { | 224 | 1.56k | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 1.56k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 1.56k | } | 229 | 1.56k | } | 230 | 1.89k | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 1.89k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 1.89k | webSocketData->controlTipLength = 0; | 235 | 1.89k | } | 236 | 8.24k | } | 237 | 28.8k | } | 238 | 107k | return false; | 239 | 118k | } |
|
240 | | |
241 | 1.34M | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { |
242 | 1.34M | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
243 | | |
244 | | /* Return true for refuse, false for accept */ |
245 | 1.34M | return webSocketContextData->maxPayloadLength < length; |
246 | 1.34M | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 1.12M | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 1.12M | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 243 | | | 244 | | /* Return true for refuse, false for accept */ | 245 | 1.12M | return webSocketContextData->maxPayloadLength < length; | 246 | 1.12M | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 93.1k | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 93.1k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 243 | | | 244 | | /* Return true for refuse, false for accept */ | 245 | 93.1k | return webSocketContextData->maxPayloadLength < length; | 246 | 93.1k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 129k | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 129k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 243 | | | 244 | | /* Return true for refuse, false for accept */ | 245 | 129k | return webSocketContextData->maxPayloadLength < length; | 246 | 129k | } |
|
247 | | |
248 | 34.2k | WebSocketContext<SSL, isServer, USERDATA> *init() { |
249 | | /* Adopting a socket does not trigger open event. |
250 | | * We arreive as WebSocket with timeout set and |
251 | | * any backpressure from HTTP state kept. */ |
252 | | |
253 | | /* Handle socket disconnections */ |
254 | 362k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { |
255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ |
256 | 362k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); |
257 | 362k | if (!webSocketData->isShuttingDown) { |
258 | | /* Emit close event */ |
259 | 339k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
260 | | |
261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ |
262 | 339k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { |
263 | 0 | for (Topic *t : webSocketData->subscriber->topics) { |
264 | 0 | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ |
269 | 339k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); |
270 | 339k | webSocketData->subscriber = nullptr; |
271 | | |
272 | 339k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; |
273 | 339k | if (webSocketContextData->closeHandler) { |
274 | 338k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); |
275 | 338k | } |
276 | 339k | ((USERDATA *) ws->getUserData())->~USERDATA(); |
277 | 339k | } |
278 | | |
279 | | /* Destruct in-placed data struct */ |
280 | 362k | webSocketData->~WebSocketData(); |
281 | | |
282 | 362k | return s; |
283 | 362k | }); EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*, int, void*)#1}::operator()<us_socket_t>(us_socket_t*, int, void*) const Line | Count | Source | 254 | 106k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 106k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 106k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 97.0k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 97.0k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 0 | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 0 | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 0 | } | 266 | 0 | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 97.0k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 97.0k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 97.0k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 97.0k | if (webSocketContextData->closeHandler) { | 274 | 97.0k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 97.0k | } | 276 | 97.0k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 97.0k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 106k | webSocketData->~WebSocketData(); | 281 | | | 282 | 106k | return s; | 283 | 106k | }); |
EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*, int, void*)#1}::operator()<us_socket_t>(us_socket_t*, int, void*) const Line | Count | Source | 254 | 40.4k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 40.4k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 40.4k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 34.4k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 34.4k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 0 | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 0 | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 0 | } | 266 | 0 | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 34.4k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 34.4k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 34.4k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 34.4k | if (webSocketContextData->closeHandler) { | 274 | 33.4k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 33.4k | } | 276 | 34.4k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 34.4k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 40.4k | webSocketData->~WebSocketData(); | 281 | | | 282 | 40.4k | return s; | 283 | 40.4k | }); |
EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*, int, void*)#1}::operator()<us_socket_t>(us_socket_t*, int, void*) const Line | Count | Source | 254 | 215k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 215k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 215k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 207k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 207k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 0 | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 0 | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 0 | } | 266 | 0 | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 207k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 207k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 207k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 207k | if (webSocketContextData->closeHandler) { | 274 | 207k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 207k | } | 276 | 207k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 207k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 215k | webSocketData->~WebSocketData(); | 281 | | | 282 | 215k | return s; | 283 | 215k | }); |
|
284 | | |
285 | | /* Handle WebSocket data streams */ |
286 | 220k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { |
287 | | |
288 | | /* We need the websocket data */ |
289 | 220k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); |
290 | | |
291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. |
292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ |
293 | 220k | if (webSocketData->isShuttingDown) { |
294 | 1.64k | return s; |
295 | 1.64k | } |
296 | | |
297 | 218k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
298 | 218k | auto *asyncSocket = (AsyncSocket<SSL> *) s; |
299 | | |
300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ |
301 | 218k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); |
302 | 218k | webSocketData->hasTimedOut = false; |
303 | | |
304 | | /* We always cork on data */ |
305 | 218k | asyncSocket->cork(); |
306 | | |
307 | | /* This parser has virtually no overhead */ |
308 | 218k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); |
309 | | |
310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ |
311 | 218k | asyncSocket->uncork(); |
312 | | |
313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ |
314 | 218k | if (asyncSocket->getBufferedAmount() == 0) { |
315 | | /* We can now be in shutdown state */ |
316 | 45.6k | if (webSocketData->isShuttingDown) { |
317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ |
318 | 4.10k | asyncSocket->shutdown(); |
319 | 4.10k | } |
320 | 45.6k | } |
321 | | |
322 | 218k | return s; |
323 | 220k | }); EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*, char*, int)#1}::operator()<us_socket_t>(us_socket_t*, char*, int) const Line | Count | Source | 286 | 90.1k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 90.1k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 90.1k | if (webSocketData->isShuttingDown) { | 294 | 853 | return s; | 295 | 853 | } | 296 | | | 297 | 89.2k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 89.2k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 89.2k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 89.2k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 89.2k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 89.2k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 89.2k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 89.2k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 6.16k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.29k | asyncSocket->shutdown(); | 319 | 1.29k | } | 320 | 6.16k | } | 321 | | | 322 | 89.2k | return s; | 323 | 90.1k | }); |
EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*, char*, int)#1}::operator()<us_socket_t>(us_socket_t*, char*, int) const Line | Count | Source | 286 | 60.4k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 60.4k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 60.4k | if (webSocketData->isShuttingDown) { | 294 | 408 | return s; | 295 | 408 | } | 296 | | | 297 | 60.0k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 60.0k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 60.0k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 60.0k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 60.0k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 60.0k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 60.0k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 60.0k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 26.0k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.17k | asyncSocket->shutdown(); | 319 | 1.17k | } | 320 | 26.0k | } | 321 | | | 322 | 60.0k | return s; | 323 | 60.4k | }); |
EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*, char*, int)#1}::operator()<us_socket_t>(us_socket_t*, char*, int) const Line | Count | Source | 286 | 69.6k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 69.6k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 69.6k | if (webSocketData->isShuttingDown) { | 294 | 380 | return s; | 295 | 380 | } | 296 | | | 297 | 69.2k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 69.2k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 69.2k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 69.2k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 69.2k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 69.2k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 69.2k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 69.2k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 13.3k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.64k | asyncSocket->shutdown(); | 319 | 1.64k | } | 320 | 13.3k | } | 321 | | | 322 | 69.2k | return s; | 323 | 69.6k | }); |
|
324 | | |
325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ |
326 | 102k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { |
327 | | |
328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since |
329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before |
330 | | * we actually uncorked and sent off things */ |
331 | | |
332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ |
333 | 102k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
334 | 0 | return s; |
335 | 0 | } |
336 | | |
337 | 102k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
338 | 102k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); |
339 | | |
340 | | /* We store old backpressure since it is unclear whether write drained anything, |
341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ |
342 | 102k | unsigned int backpressure = asyncSocket->getBufferedAmount(); |
343 | | |
344 | | /* Drain as much as possible */ |
345 | 102k | asyncSocket->write(nullptr, 0); |
346 | | |
347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ |
348 | | /* Also reset timeout if we came here with 0 backpressure */ |
349 | 102k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { |
350 | 64.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
351 | 64.3k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); |
352 | 64.3k | webSocketData->hasTimedOut = false; |
353 | 64.3k | } |
354 | | |
355 | | /* Are we in (WebSocket) shutdown mode? */ |
356 | 102k | if (webSocketData->isShuttingDown) { |
357 | | /* Check if we just now drained completely */ |
358 | 5.97k | if (asyncSocket->getBufferedAmount() == 0) { |
359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ |
360 | 1.19k | asyncSocket->shutdown(); |
361 | 1.19k | } |
362 | 96.6k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { |
363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ |
364 | 62.0k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
365 | 62.0k | if (webSocketContextData->drainHandler) { |
366 | 61.7k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); |
367 | 61.7k | } |
368 | | /* No need to check for closed here as we leave the handler immediately*/ |
369 | 62.0k | } |
370 | | |
371 | 102k | return s; |
372 | 102k | }); EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*)#1}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 326 | 14.8k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 14.8k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 14.8k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 14.8k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 14.8k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 14.8k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 14.8k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 7.98k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 7.98k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 7.98k | webSocketData->hasTimedOut = false; | 353 | 7.98k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 14.8k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 3.21k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 338 | asyncSocket->shutdown(); | 361 | 338 | } | 362 | 11.6k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 7.32k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 7.32k | if (webSocketContextData->drainHandler) { | 366 | 7.32k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 7.32k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 7.32k | } | 370 | | | 371 | 14.8k | return s; | 372 | 14.8k | }); |
EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#1}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 326 | 7.29k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 7.29k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 7.29k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 7.29k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 7.29k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 7.29k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 7.29k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 5.11k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 5.11k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 5.11k | webSocketData->hasTimedOut = false; | 353 | 5.11k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 7.29k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 1.40k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 419 | asyncSocket->shutdown(); | 361 | 419 | } | 362 | 5.88k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 4.32k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 4.32k | if (webSocketContextData->drainHandler) { | 366 | 4.10k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 4.10k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 4.32k | } | 370 | | | 371 | 7.29k | return s; | 372 | 7.29k | }); |
EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#1}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 326 | 80.4k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 80.4k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 80.4k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 80.4k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 80.4k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 80.4k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 80.4k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 51.2k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 51.2k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 51.2k | webSocketData->hasTimedOut = false; | 353 | 51.2k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 80.4k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 1.34k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 441 | asyncSocket->shutdown(); | 361 | 441 | } | 362 | 79.1k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 50.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 50.3k | if (webSocketContextData->drainHandler) { | 366 | 50.3k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 50.3k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 50.3k | } | 370 | | | 371 | 80.4k | return s; | 372 | 80.4k | }); |
|
373 | | |
374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ |
375 | 34.2k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { |
376 | | |
377 | | /* If we get a fin, we just close I guess */ |
378 | 5.97k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); |
379 | | |
380 | 5.97k | return s; |
381 | 5.97k | }); EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*)#2}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 375 | 1.45k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 1.45k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 1.45k | return s; | 381 | 1.45k | }); |
EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#2}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 375 | 342 | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 342 | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 342 | return s; | 381 | 342 | }); |
EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#2}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 375 | 4.17k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 4.17k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 4.17k | return s; | 381 | 4.17k | }); |
|
382 | | |
383 | 34.2k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { |
384 | 0 | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); |
385 | |
|
386 | 0 | return s; |
387 | 0 | }); Unexecuted instantiation: EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*)#3}::operator()<us_socket_t>(us_socket_t*) const Unexecuted instantiation: EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#3}::operator()<us_socket_t>(us_socket_t*) const Unexecuted instantiation: EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#3}::operator()<us_socket_t>(us_socket_t*) const |
388 | | |
389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ |
390 | 43.3k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { |
391 | | |
392 | 43.3k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); |
393 | 43.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
394 | | |
395 | 43.3k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { |
396 | 29.4k | webSocketData->hasTimedOut = true; |
397 | 29.4k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); |
398 | | /* Send ping without being corked */ |
399 | 29.4k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); |
400 | 29.4k | return s; |
401 | 29.4k | } |
402 | | |
403 | | /* Timeout is very simple; we just close it */ |
404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ |
405 | 13.8k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); |
406 | | |
407 | 13.8k | return s; |
408 | 43.3k | }); EpollEchoServerPubSub.cpp:auto uWS::WebSocketContext<true, true, test()::PerSocketData>::init()::{lambda(auto:1*)#4}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 390 | 204 | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 204 | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 204 | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 204 | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 0 | webSocketData->hasTimedOut = true; | 397 | 0 | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 0 | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 0 | return s; | 401 | 0 | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 204 | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 204 | return s; | 408 | 204 | }); |
EpollHelloWorld.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#4}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 390 | 5.86k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 5.86k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 5.86k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 5.86k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 2.54k | webSocketData->hasTimedOut = true; | 397 | 2.54k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 2.54k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 2.54k | return s; | 401 | 2.54k | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 3.32k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 3.32k | return s; | 408 | 5.86k | }); |
EpollEchoServer.cpp:auto uWS::WebSocketContext<false, true, test()::PerSocketData>::init()::{lambda(auto:1*)#4}::operator()<us_socket_t>(us_socket_t*) const Line | Count | Source | 390 | 37.2k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 37.2k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 37.2k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 37.2k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 26.8k | webSocketData->hasTimedOut = true; | 397 | 26.8k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 26.8k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 26.8k | return s; | 401 | 26.8k | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 10.3k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 10.3k | return s; | 408 | 37.2k | }); |
|
409 | | |
410 | 34.2k | return this; |
411 | 34.2k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::init() Line | Count | Source | 248 | 5.94k | WebSocketContext<SSL, isServer, USERDATA> *init() { | 249 | | /* Adopting a socket does not trigger open event. | 250 | | * We arreive as WebSocket with timeout set and | 251 | | * any backpressure from HTTP state kept. */ | 252 | | | 253 | | /* Handle socket disconnections */ | 254 | 5.94k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 5.94k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 5.94k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 5.94k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 5.94k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 5.94k | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 5.94k | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 5.94k | } | 266 | 5.94k | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 5.94k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 5.94k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 5.94k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 5.94k | if (webSocketContextData->closeHandler) { | 274 | 5.94k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 5.94k | } | 276 | 5.94k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 5.94k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 5.94k | webSocketData->~WebSocketData(); | 281 | | | 282 | 5.94k | return s; | 283 | 5.94k | }); | 284 | | | 285 | | /* Handle WebSocket data streams */ | 286 | 5.94k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 5.94k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 5.94k | if (webSocketData->isShuttingDown) { | 294 | 5.94k | return s; | 295 | 5.94k | } | 296 | | | 297 | 5.94k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 5.94k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 5.94k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 5.94k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 5.94k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 5.94k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 5.94k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 5.94k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 5.94k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 5.94k | asyncSocket->shutdown(); | 319 | 5.94k | } | 320 | 5.94k | } | 321 | | | 322 | 5.94k | return s; | 323 | 5.94k | }); | 324 | | | 325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 326 | 5.94k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 5.94k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 5.94k | return s; | 335 | 5.94k | } | 336 | | | 337 | 5.94k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 5.94k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 5.94k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 5.94k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 5.94k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 5.94k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 5.94k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 5.94k | webSocketData->hasTimedOut = false; | 353 | 5.94k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 5.94k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 5.94k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 5.94k | asyncSocket->shutdown(); | 361 | 5.94k | } | 362 | 5.94k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 5.94k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 5.94k | if (webSocketContextData->drainHandler) { | 366 | 5.94k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 5.94k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 5.94k | } | 370 | | | 371 | 5.94k | return s; | 372 | 5.94k | }); | 373 | | | 374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ | 375 | 5.94k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 5.94k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 5.94k | return s; | 381 | 5.94k | }); | 382 | | | 383 | 5.94k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { | 384 | 5.94k | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); | 385 | | | 386 | 5.94k | return s; | 387 | 5.94k | }); | 388 | | | 389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 390 | 5.94k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 5.94k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 5.94k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 5.94k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 5.94k | webSocketData->hasTimedOut = true; | 397 | 5.94k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 5.94k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 5.94k | return s; | 401 | 5.94k | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 5.94k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 5.94k | return s; | 408 | 5.94k | }); | 409 | | | 410 | 5.94k | return this; | 411 | 5.94k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::init() Line | Count | Source | 248 | 13.8k | WebSocketContext<SSL, isServer, USERDATA> *init() { | 249 | | /* Adopting a socket does not trigger open event. | 250 | | * We arreive as WebSocket with timeout set and | 251 | | * any backpressure from HTTP state kept. */ | 252 | | | 253 | | /* Handle socket disconnections */ | 254 | 13.8k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 13.8k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 13.8k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 13.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 13.8k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 13.8k | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 13.8k | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 13.8k | } | 266 | 13.8k | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 13.8k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 13.8k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 13.8k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 13.8k | if (webSocketContextData->closeHandler) { | 274 | 13.8k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 13.8k | } | 276 | 13.8k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 13.8k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 13.8k | webSocketData->~WebSocketData(); | 281 | | | 282 | 13.8k | return s; | 283 | 13.8k | }); | 284 | | | 285 | | /* Handle WebSocket data streams */ | 286 | 13.8k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 13.8k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 13.8k | if (webSocketData->isShuttingDown) { | 294 | 13.8k | return s; | 295 | 13.8k | } | 296 | | | 297 | 13.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 13.8k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 13.8k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 13.8k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 13.8k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 13.8k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 13.8k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 13.8k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 13.8k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 13.8k | asyncSocket->shutdown(); | 319 | 13.8k | } | 320 | 13.8k | } | 321 | | | 322 | 13.8k | return s; | 323 | 13.8k | }); | 324 | | | 325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 326 | 13.8k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 13.8k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 13.8k | return s; | 335 | 13.8k | } | 336 | | | 337 | 13.8k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 13.8k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 13.8k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 13.8k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 13.8k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 13.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 13.8k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 13.8k | webSocketData->hasTimedOut = false; | 353 | 13.8k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 13.8k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 13.8k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 13.8k | asyncSocket->shutdown(); | 361 | 13.8k | } | 362 | 13.8k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 13.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 13.8k | if (webSocketContextData->drainHandler) { | 366 | 13.8k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 13.8k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 13.8k | } | 370 | | | 371 | 13.8k | return s; | 372 | 13.8k | }); | 373 | | | 374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ | 375 | 13.8k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 13.8k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 13.8k | return s; | 381 | 13.8k | }); | 382 | | | 383 | 13.8k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { | 384 | 13.8k | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); | 385 | | | 386 | 13.8k | return s; | 387 | 13.8k | }); | 388 | | | 389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 390 | 13.8k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 13.8k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 13.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 13.8k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 13.8k | webSocketData->hasTimedOut = true; | 397 | 13.8k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 13.8k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 13.8k | return s; | 401 | 13.8k | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 13.8k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 13.8k | return s; | 408 | 13.8k | }); | 409 | | | 410 | 13.8k | return this; | 411 | 13.8k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::init() Line | Count | Source | 248 | 14.3k | WebSocketContext<SSL, isServer, USERDATA> *init() { | 249 | | /* Adopting a socket does not trigger open event. | 250 | | * We arreive as WebSocket with timeout set and | 251 | | * any backpressure from HTTP state kept. */ | 252 | | | 253 | | /* Handle socket disconnections */ | 254 | 14.3k | us_socket_context_on_close(SSL, getSocketContext(), [](auto *s, int code, void *reason) { | 255 | | /* For whatever reason, if we already have emitted close event, do not emit it again */ | 256 | 14.3k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 14.3k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 14.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 260 | | | 261 | | /* At this point we iterate all currently held subscriptions and emit an event for all of them */ | 262 | 14.3k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 14.3k | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 14.3k | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 14.3k | } | 266 | 14.3k | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 14.3k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 14.3k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 14.3k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 14.3k | if (webSocketContextData->closeHandler) { | 274 | 14.3k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 14.3k | } | 276 | 14.3k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 14.3k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 14.3k | webSocketData->~WebSocketData(); | 281 | | | 282 | 14.3k | return s; | 283 | 14.3k | }); | 284 | | | 285 | | /* Handle WebSocket data streams */ | 286 | 14.3k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 14.3k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 290 | | | 291 | | /* When in websocket shutdown mode, we do not care for ANY message, whether responding close frame or not. | 292 | | * We only care for the TCP FIN really, not emitting any message after closing is key */ | 293 | 14.3k | if (webSocketData->isShuttingDown) { | 294 | 14.3k | return s; | 295 | 14.3k | } | 296 | | | 297 | 14.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 14.3k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 14.3k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 14.3k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 14.3k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 14.3k | WebSocketProtocol<isServer, WebSocketContext<SSL, isServer, USERDATA>>::consume(data, (unsigned int) length, (WebSocketState<isServer> *) webSocketData, s); | 309 | | | 310 | | /* Uncorking a closed socekt is fine, in fact it is needed */ | 311 | 14.3k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 14.3k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 14.3k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 14.3k | asyncSocket->shutdown(); | 319 | 14.3k | } | 320 | 14.3k | } | 321 | | | 322 | 14.3k | return s; | 323 | 14.3k | }); | 324 | | | 325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 326 | 14.3k | us_socket_context_on_writable(SSL, getSocketContext(), [](auto *s) { | 327 | | | 328 | | /* NOTE: Are we called here corked? If so, the below write code is broken, since | 329 | | * we will have 0 as getBufferedAmount due to writing to cork buffer, then sending TCP FIN before | 330 | | * we actually uncorked and sent off things */ | 331 | | | 332 | | /* It makes sense to check for us_is_shut_down here and return if so, to avoid shutting down twice */ | 333 | 14.3k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 14.3k | return s; | 335 | 14.3k | } | 336 | | | 337 | 14.3k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 14.3k | WebSocketData *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 339 | | | 340 | | /* We store old backpressure since it is unclear whether write drained anything, | 341 | | * however, in case of coming here with 0 backpressure we still need to emit drain event */ | 342 | 14.3k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 14.3k | asyncSocket->write(nullptr, 0); | 346 | | | 347 | | /* Behavior: if we actively drain backpressure, always reset timeout (even if we are in shutdown) */ | 348 | | /* Also reset timeout if we came here with 0 backpressure */ | 349 | 14.3k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 14.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 14.3k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 14.3k | webSocketData->hasTimedOut = false; | 353 | 14.3k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 14.3k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 14.3k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 14.3k | asyncSocket->shutdown(); | 361 | 14.3k | } | 362 | 14.3k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 14.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 14.3k | if (webSocketContextData->drainHandler) { | 366 | 14.3k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 14.3k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 14.3k | } | 370 | | | 371 | 14.3k | return s; | 372 | 14.3k | }); | 373 | | | 374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ | 375 | 14.3k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 14.3k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 14.3k | return s; | 381 | 14.3k | }); | 382 | | | 383 | 14.3k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { | 384 | 14.3k | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); | 385 | | | 386 | 14.3k | return s; | 387 | 14.3k | }); | 388 | | | 389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 390 | 14.3k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 14.3k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 14.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 14.3k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 14.3k | webSocketData->hasTimedOut = true; | 397 | 14.3k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 14.3k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 14.3k | return s; | 401 | 14.3k | } | 402 | | | 403 | | /* Timeout is very simple; we just close it */ | 404 | | /* Warning: we happen to know forceClose will not use first parameter so pass nullptr here */ | 405 | 14.3k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 14.3k | return s; | 408 | 14.3k | }); | 409 | | | 410 | 14.3k | return this; | 411 | 14.3k | } |
|
412 | | |
413 | 34.2k | void free() { |
414 | 34.2k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); |
415 | 34.2k | webSocketContextData->~WebSocketContextData(); |
416 | | |
417 | 34.2k | us_socket_context_free(SSL, (us_socket_context_t *) this); |
418 | 34.2k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::free() Line | Count | Source | 413 | 5.94k | void free() { | 414 | 5.94k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 415 | 5.94k | webSocketContextData->~WebSocketContextData(); | 416 | | | 417 | 5.94k | us_socket_context_free(SSL, (us_socket_context_t *) this); | 418 | 5.94k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::free() Line | Count | Source | 413 | 13.8k | void free() { | 414 | 13.8k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 415 | 13.8k | webSocketContextData->~WebSocketContextData(); | 416 | | | 417 | 13.8k | us_socket_context_free(SSL, (us_socket_context_t *) this); | 418 | 13.8k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::free() Line | Count | Source | 413 | 14.3k | void free() { | 414 | 14.3k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 415 | 14.3k | webSocketContextData->~WebSocketContextData(); | 416 | | | 417 | 14.3k | us_socket_context_free(SSL, (us_socket_context_t *) this); | 418 | 14.3k | } |
|
419 | | |
420 | | public: |
421 | | /* WebSocket contexts are always child contexts to a HTTP context so no SSL options are needed as they are inherited */ |
422 | 34.2k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { |
423 | 34.2k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); |
424 | 34.2k | if (!webSocketContext) { |
425 | 0 | return nullptr; |
426 | 0 | } |
427 | | |
428 | | /* Init socket context data */ |
429 | 34.2k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); |
430 | 34.2k | return webSocketContext->init(); |
431 | 34.2k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::create(uWS::Loop*, us_socket_context_t*, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>*) Line | Count | Source | 422 | 5.94k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { | 423 | 5.94k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); | 424 | 5.94k | if (!webSocketContext) { | 425 | 0 | return nullptr; | 426 | 0 | } | 427 | | | 428 | | /* Init socket context data */ | 429 | 5.94k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); | 430 | 5.94k | return webSocketContext->init(); | 431 | 5.94k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::create(uWS::Loop*, us_socket_context_t*, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>*) Line | Count | Source | 422 | 13.8k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { | 423 | 13.8k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); | 424 | 13.8k | if (!webSocketContext) { | 425 | 0 | return nullptr; | 426 | 0 | } | 427 | | | 428 | | /* Init socket context data */ | 429 | 13.8k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); | 430 | 13.8k | return webSocketContext->init(); | 431 | 13.8k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::create(uWS::Loop*, us_socket_context_t*, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>*) Line | Count | Source | 422 | 14.3k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { | 423 | 14.3k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); | 424 | 14.3k | if (!webSocketContext) { | 425 | 0 | return nullptr; | 426 | 0 | } | 427 | | | 428 | | /* Init socket context data */ | 429 | 14.3k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); | 430 | 14.3k | return webSocketContext->init(); | 431 | 14.3k | } |
|
432 | | }; |
433 | | |
434 | | } |
435 | | |
436 | | #endif // UWS_WEBSOCKETCONTEXT_H |