/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 | 220k | us_socket_context_t *getSocketContext() { |
36 | 220k | return (us_socket_context_t *) this; |
37 | 220k | } 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 | 88.0k | us_socket_context_t *getSocketContext() { | 36 | 88.0k | return (us_socket_context_t *) this; | 37 | 88.0k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getSocketContext() Line | Count | Source | 35 | 96.7k | us_socket_context_t *getSocketContext() { | 36 | 96.7k | return (us_socket_context_t *) this; | 37 | 96.7k | } |
|
38 | | |
39 | 535k | WebSocketContextData<SSL, USERDATA> *getExt() { |
40 | 535k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); |
41 | 535k | } 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 | 216k | WebSocketContextData<SSL, USERDATA> *getExt() { | 40 | 216k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 41 | 216k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::getExt() Line | Count | Source | 39 | 223k | WebSocketContextData<SSL, USERDATA> *getExt() { | 40 | 223k | return (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 41 | 223k | } |
|
42 | | |
43 | | /* If we have negotiated compression, set this frame compressed */ |
44 | 33.1k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { |
45 | 33.1k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); |
46 | | |
47 | 33.1k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { |
48 | 3.59k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; |
49 | 3.59k | return true; |
50 | 29.5k | } else { |
51 | 29.5k | return false; |
52 | 29.5k | } |
53 | 33.1k | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 17.8k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 17.8k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 17.8k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 0 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 0 | return true; | 50 | 17.8k | } else { | 51 | 17.8k | return false; | 52 | 17.8k | } | 53 | 17.8k | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 2.52k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 2.52k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 2.52k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 1.48k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 1.48k | return true; | 50 | 1.48k | } else { | 51 | 1.04k | return false; | 52 | 1.04k | } | 53 | 2.52k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::setCompressed(uWS::WebSocketState<true>*, void*) Line | Count | Source | 44 | 12.7k | static bool setCompressed(WebSocketState<isServer> */*wState*/, void *s) { | 45 | 12.7k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 46 | | | 47 | 12.7k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::ENABLED) { | 48 | 2.11k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::COMPRESSED_FRAME; | 49 | 2.11k | return true; | 50 | 10.6k | } else { | 51 | 10.6k | return false; | 52 | 10.6k | } | 53 | 12.7k | } |
|
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 | 53.9k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 53.9k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 53.9k | } |
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 | 12.7k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 12.7k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 12.7k | } |
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 | 40.1k | static void forceClose(WebSocketState<isServer> */*wState*/, void *s, std::string_view reason = {}) { | 56 | 40.1k | us_socket_close(SSL, (us_socket_t *) s, (int) reason.length(), (void *) reason.data()); | 57 | 40.1k | } |
|
58 | | |
59 | | /* Returns true on breakage */ |
60 | 1.33M | 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.33M | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
63 | 1.33M | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); |
64 | | |
65 | | /* Is this a non-control frame? */ |
66 | 1.33M | if (opCode < 3) { |
67 | | /* Did we get everything in one go? */ |
68 | 1.26M | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { |
69 | | |
70 | | /* Handle compressed frame */ |
71 | 1.16M | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { |
72 | 1.37k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; |
73 | | |
74 | 1.37k | 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.37k | std::optional<std::string_view> inflatedFrame; |
77 | 1.37k | if (webSocketData->inflationStream) { |
78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); |
79 | 1.37k | } else { |
80 | 1.37k | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); |
81 | 1.37k | } |
82 | | |
83 | 1.37k | if (!inflatedFrame.has_value()) { |
84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); |
85 | 0 | return true; |
86 | 1.37k | } else { |
87 | 1.37k | data = (char *) inflatedFrame->data(); |
88 | 1.37k | length = inflatedFrame->length(); |
89 | 1.37k | } |
90 | 1.37k | } |
91 | | |
92 | | /* Check text messages for Utf-8 validity */ |
93 | 1.16M | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { |
94 | 20.3k | forceClose(webSocketState, s, ERR_INVALID_TEXT); |
95 | 20.3k | return true; |
96 | 20.3k | } |
97 | | |
98 | | /* Emit message event & break if we are closed or shut down when returning */ |
99 | 1.14M | if (webSocketContextData->messageHandler) { |
100 | 1.13M | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); |
101 | 1.13M | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
102 | 925 | return true; |
103 | 925 | } |
104 | 1.13M | } |
105 | 1.14M | } else { |
106 | | /* Allocate fragment buffer up front first time */ |
107 | 102k | if (!webSocketData->fragmentBuffer.length()) { |
108 | 37.6k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); |
109 | 37.6k | } |
110 | | /* Fragments forming a big message are not caught until appending them */ |
111 | 102k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { |
112 | 672 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); |
113 | 672 | return true; |
114 | 672 | } |
115 | 101k | 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 | 101k | if (!remainingBytes && fin) { |
120 | | |
121 | | /* Handle compression */ |
122 | 10.4k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { |
123 | 1.98k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; |
124 | | |
125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ |
126 | 1.98k | webSocketData->fragmentBuffer.append("123456789"); |
127 | | |
128 | 1.98k | LoopData *loopData = (LoopData *) us_loop_ext( |
129 | 1.98k | us_socket_context_loop(SSL, |
130 | 1.98k | us_socket_context(SSL, (us_socket_t *) s) |
131 | 1.98k | ) |
132 | 1.98k | ); |
133 | | |
134 | | /* Decompress using shared or dedicated decompressor */ |
135 | 1.98k | std::optional<std::string_view> inflatedFrame; |
136 | 1.98k | if (webSocketData->inflationStream) { |
137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); |
138 | 1.98k | } else { |
139 | 1.98k | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); |
140 | 1.98k | } |
141 | | |
142 | 1.98k | if (!inflatedFrame.has_value()) { |
143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); |
144 | 0 | return true; |
145 | 1.98k | } else { |
146 | 1.98k | data = (char *) inflatedFrame->data(); |
147 | 1.98k | length = inflatedFrame->length(); |
148 | 1.98k | } |
149 | | |
150 | | |
151 | 8.49k | } else { |
152 | | // reset length and data ptrs |
153 | 8.49k | length = webSocketData->fragmentBuffer.length(); |
154 | 8.49k | data = webSocketData->fragmentBuffer.data(); |
155 | 8.49k | } |
156 | | |
157 | | /* Check text messages for Utf-8 validity */ |
158 | 10.4k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { |
159 | 1.11k | forceClose(webSocketState, s, ERR_INVALID_TEXT); |
160 | 1.11k | return true; |
161 | 1.11k | } |
162 | | |
163 | | /* Emit message and check for shutdown or close */ |
164 | 9.36k | if (webSocketContextData->messageHandler) { |
165 | 9.08k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); |
166 | 9.08k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
167 | 388 | return true; |
168 | 388 | } |
169 | 9.08k | } |
170 | | |
171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ |
172 | 8.97k | webSocketData->fragmentBuffer.clear(); |
173 | 8.97k | } |
174 | 101k | } |
175 | 1.26M | } else { |
176 | | /* Control frames need the websocket to send pings, pongs and close */ |
177 | 73.0k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; |
178 | | |
179 | 73.0k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { |
180 | 46.5k | if (opCode == CLOSE) { |
181 | 18.6k | auto closeFrame = protocol::parseClosePayload(data, length); |
182 | 18.6k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); |
183 | 18.6k | return true; |
184 | 27.8k | } else { |
185 | 27.8k | if (opCode == PING) { |
186 | 22.7k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); |
187 | 22.7k | if (webSocketContextData->pingHandler) { |
188 | 22.4k | webSocketContextData->pingHandler(webSocket, {data, length}); |
189 | 22.4k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
190 | 0 | return true; |
191 | 0 | } |
192 | 22.4k | } |
193 | 22.7k | } else if (opCode == PONG) { |
194 | 5.18k | if (webSocketContextData->pongHandler) { |
195 | 4.86k | webSocketContextData->pongHandler(webSocket, {data, length}); |
196 | 4.86k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
197 | 0 | return true; |
198 | 0 | } |
199 | 4.86k | } |
200 | 5.18k | } |
201 | 27.8k | } |
202 | 46.5k | } else { |
203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ |
204 | 26.4k | webSocketData->fragmentBuffer.append(data, length); |
205 | 26.4k | webSocketData->controlTipLength += (unsigned int) length; |
206 | | |
207 | 26.4k | if (!remainingBytes && fin) { |
208 | 11.8k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; |
209 | 11.8k | if (opCode == CLOSE) { |
210 | 6.62k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); |
211 | 6.62k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); |
212 | 6.62k | return true; |
213 | 6.62k | } else { |
214 | 5.20k | if (opCode == PING) { |
215 | 2.12k | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); |
216 | 2.12k | if (webSocketContextData->pingHandler) { |
217 | 1.79k | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); |
218 | 1.79k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
219 | 0 | return true; |
220 | 0 | } |
221 | 1.79k | } |
222 | 3.07k | } else if (opCode == PONG) { |
223 | 3.07k | if (webSocketContextData->pongHandler) { |
224 | 2.27k | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); |
225 | 2.27k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { |
226 | 0 | return true; |
227 | 0 | } |
228 | 2.27k | } |
229 | 3.07k | } |
230 | 5.20k | } |
231 | | |
232 | | /* Same here, we do not care for any particular smart allocation scheme */ |
233 | 5.20k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); |
234 | 5.20k | webSocketData->controlTipLength = 0; |
235 | 5.20k | } |
236 | 26.4k | } |
237 | 73.0k | } |
238 | 1.28M | return false; |
239 | 1.33M | } 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.13M | 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.13M | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 1.13M | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 1.13M | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 1.11M | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 1.08M | 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.08M | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 14.1k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 14.1k | return true; | 96 | 14.1k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 1.06M | if (webSocketContextData->messageHandler) { | 100 | 1.06M | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 1.06M | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 0 | return true; | 103 | 0 | } | 104 | 1.06M | } | 105 | 1.06M | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 27.9k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 17.8k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 17.8k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 27.9k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 194 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 194 | return true; | 114 | 194 | } | 115 | 27.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 | 27.7k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 4.51k | 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.51k | } else { | 152 | | // reset length and data ptrs | 153 | 4.51k | length = webSocketData->fragmentBuffer.length(); | 154 | 4.51k | data = webSocketData->fragmentBuffer.data(); | 155 | 4.51k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 4.51k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 561 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 561 | return true; | 161 | 561 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 3.95k | if (webSocketContextData->messageHandler) { | 165 | 3.95k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 3.95k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 0 | return true; | 168 | 0 | } | 169 | 3.95k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 3.95k | webSocketData->fragmentBuffer.clear(); | 173 | 3.95k | } | 174 | 27.7k | } | 175 | 1.11M | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 21.5k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 21.5k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 13.1k | if (opCode == CLOSE) { | 181 | 8.53k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 8.53k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 8.53k | return true; | 184 | 8.53k | } else { | 185 | 4.56k | if (opCode == PING) { | 186 | 4.16k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 4.16k | if (webSocketContextData->pingHandler) { | 188 | 4.16k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 4.16k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 4.16k | } | 193 | 4.16k | } else if (opCode == PONG) { | 194 | 400 | if (webSocketContextData->pongHandler) { | 195 | 400 | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 400 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 400 | } | 200 | 400 | } | 201 | 4.56k | } | 202 | 13.1k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 8.47k | webSocketData->fragmentBuffer.append(data, length); | 205 | 8.47k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 8.47k | if (!remainingBytes && fin) { | 208 | 3.99k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.99k | if (opCode == CLOSE) { | 210 | 3.02k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 3.02k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 3.02k | return true; | 213 | 3.02k | } else { | 214 | 970 | if (opCode == PING) { | 215 | 656 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 656 | if (webSocketContextData->pingHandler) { | 217 | 656 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 656 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 656 | } | 222 | 656 | } else if (opCode == PONG) { | 223 | 314 | if (webSocketContextData->pongHandler) { | 224 | 314 | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 314 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 314 | } | 229 | 314 | } | 230 | 970 | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 970 | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 970 | webSocketData->controlTipLength = 0; | 235 | 970 | } | 236 | 8.47k | } | 237 | 21.5k | } | 238 | 1.10M | return false; | 239 | 1.13M | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::handleFragment(char*, unsigned long, unsigned int, int, bool, uWS::WebSocketState<true>*, void*) Line | Count | Source | 60 | 90.8k | 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 | 90.8k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 90.8k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 90.8k | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 67.8k | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 13.8k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 72 | 704 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 73 | | | 74 | 704 | 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 | 704 | std::optional<std::string_view> inflatedFrame; | 77 | 704 | if (webSocketData->inflationStream) { | 78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); | 79 | 704 | } else { | 80 | 704 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); | 81 | 704 | } | 82 | | | 83 | 704 | if (!inflatedFrame.has_value()) { | 84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 85 | 0 | return true; | 86 | 704 | } else { | 87 | 704 | data = (char *) inflatedFrame->data(); | 88 | 704 | length = inflatedFrame->length(); | 89 | 704 | } | 90 | 704 | } | 91 | | | 92 | | /* Check text messages for Utf-8 validity */ | 93 | 13.8k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 2.97k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 2.97k | return true; | 96 | 2.97k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 10.8k | if (webSocketContextData->messageHandler) { | 100 | 8.65k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 8.65k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 0 | return true; | 103 | 0 | } | 104 | 8.65k | } | 105 | 54.0k | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 54.0k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 8.13k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 8.13k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 54.0k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 120 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 120 | return true; | 114 | 120 | } | 115 | 53.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 | 53.9k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 2.28k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 123 | 665 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 124 | | | 125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ | 126 | 665 | webSocketData->fragmentBuffer.append("123456789"); | 127 | | | 128 | 665 | LoopData *loopData = (LoopData *) us_loop_ext( | 129 | 665 | us_socket_context_loop(SSL, | 130 | 665 | us_socket_context(SSL, (us_socket_t *) s) | 131 | 665 | ) | 132 | 665 | ); | 133 | | | 134 | | /* Decompress using shared or dedicated decompressor */ | 135 | 665 | std::optional<std::string_view> inflatedFrame; | 136 | 665 | if (webSocketData->inflationStream) { | 137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); | 138 | 665 | } else { | 139 | 665 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); | 140 | 665 | } | 141 | | | 142 | 665 | if (!inflatedFrame.has_value()) { | 143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 144 | 0 | return true; | 145 | 665 | } else { | 146 | 665 | data = (char *) inflatedFrame->data(); | 147 | 665 | length = inflatedFrame->length(); | 148 | 665 | } | 149 | | | 150 | | | 151 | 1.61k | } else { | 152 | | // reset length and data ptrs | 153 | 1.61k | length = webSocketData->fragmentBuffer.length(); | 154 | 1.61k | data = webSocketData->fragmentBuffer.data(); | 155 | 1.61k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 2.28k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 229 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 229 | return true; | 161 | 229 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 2.05k | if (webSocketContextData->messageHandler) { | 165 | 1.77k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 1.77k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 0 | return true; | 168 | 0 | } | 169 | 1.77k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 2.05k | webSocketData->fragmentBuffer.clear(); | 173 | 2.05k | } | 174 | 53.9k | } | 175 | 67.8k | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 22.9k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 22.9k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 13.8k | if (opCode == CLOSE) { | 181 | 4.59k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 4.59k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 4.59k | return true; | 184 | 9.26k | } else { | 185 | 9.26k | if (opCode == PING) { | 186 | 8.26k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 8.26k | if (webSocketContextData->pingHandler) { | 188 | 8.00k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 8.00k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 8.00k | } | 193 | 8.26k | } else if (opCode == PONG) { | 194 | 1.00k | if (webSocketContextData->pongHandler) { | 195 | 682 | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 682 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 682 | } | 200 | 1.00k | } | 201 | 9.26k | } | 202 | 13.8k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 9.08k | webSocketData->fragmentBuffer.append(data, length); | 205 | 9.08k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 9.08k | if (!remainingBytes && fin) { | 208 | 3.97k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.97k | if (opCode == CLOSE) { | 210 | 1.75k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 1.75k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 1.75k | return true; | 213 | 2.21k | } else { | 214 | 2.21k | if (opCode == PING) { | 215 | 889 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 889 | if (webSocketContextData->pingHandler) { | 217 | 561 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 561 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 561 | } | 222 | 1.32k | } else if (opCode == PONG) { | 223 | 1.32k | if (webSocketContextData->pongHandler) { | 224 | 525 | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 525 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 525 | } | 229 | 1.32k | } | 230 | 2.21k | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 2.21k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 2.21k | webSocketData->controlTipLength = 0; | 235 | 2.21k | } | 236 | 9.08k | } | 237 | 22.9k | } | 238 | 81.1k | return false; | 239 | 90.8k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::handleFragment(char*, unsigned long, unsigned int, int, bool, uWS::WebSocketState<true>*, void*) Line | Count | Source | 60 | 114k | 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 | 114k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 63 | 114k | WebSocketData *webSocketData = (WebSocketData *) us_socket_ext(SSL, (us_socket_t *) s); | 64 | | | 65 | | /* Is this a non-control frame? */ | 66 | 114k | if (opCode < 3) { | 67 | | /* Did we get everything in one go? */ | 68 | 86.4k | if (!remainingBytes && fin && !webSocketData->fragmentBuffer.length()) { | 69 | | | 70 | | /* Handle compressed frame */ | 71 | 66.0k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 72 | 671 | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 73 | | | 74 | 671 | 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 | 671 | std::optional<std::string_view> inflatedFrame; | 77 | 671 | if (webSocketData->inflationStream) { | 78 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, false); | 79 | 671 | } else { | 80 | 671 | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {data, length}, webSocketContextData->maxPayloadLength, true); | 81 | 671 | } | 82 | | | 83 | 671 | if (!inflatedFrame.has_value()) { | 84 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 85 | 0 | return true; | 86 | 671 | } else { | 87 | 671 | data = (char *) inflatedFrame->data(); | 88 | 671 | length = inflatedFrame->length(); | 89 | 671 | } | 90 | 671 | } | 91 | | | 92 | | /* Check text messages for Utf-8 validity */ | 93 | 66.0k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 94 | 3.29k | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 95 | 3.29k | return true; | 96 | 3.29k | } | 97 | | | 98 | | /* Emit message event & break if we are closed or shut down when returning */ | 99 | 62.7k | if (webSocketContextData->messageHandler) { | 100 | 62.7k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 101 | 62.7k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 102 | 925 | return true; | 103 | 925 | } | 104 | 62.7k | } | 105 | 62.7k | } else { | 106 | | /* Allocate fragment buffer up front first time */ | 107 | 20.4k | if (!webSocketData->fragmentBuffer.length()) { | 108 | 11.6k | webSocketData->fragmentBuffer.reserve(length + remainingBytes); | 109 | 11.6k | } | 110 | | /* Fragments forming a big message are not caught until appending them */ | 111 | 20.4k | if (refusePayloadLength(length + webSocketData->fragmentBuffer.length(), webSocketState, s)) { | 112 | 358 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE); | 113 | 358 | return true; | 114 | 358 | } | 115 | 20.0k | 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 | 20.0k | if (!remainingBytes && fin) { | 120 | | | 121 | | /* Handle compression */ | 122 | 3.67k | if (webSocketData->compressionStatus == WebSocketData::CompressionStatus::COMPRESSED_FRAME) { | 123 | 1.31k | webSocketData->compressionStatus = WebSocketData::CompressionStatus::ENABLED; | 124 | | | 125 | | /* 9 bytes of padding for libdeflate, 4 for zlib */ | 126 | 1.31k | webSocketData->fragmentBuffer.append("123456789"); | 127 | | | 128 | 1.31k | LoopData *loopData = (LoopData *) us_loop_ext( | 129 | 1.31k | us_socket_context_loop(SSL, | 130 | 1.31k | us_socket_context(SSL, (us_socket_t *) s) | 131 | 1.31k | ) | 132 | 1.31k | ); | 133 | | | 134 | | /* Decompress using shared or dedicated decompressor */ | 135 | 1.31k | std::optional<std::string_view> inflatedFrame; | 136 | 1.31k | if (webSocketData->inflationStream) { | 137 | 0 | inflatedFrame = webSocketData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, false); | 138 | 1.31k | } else { | 139 | 1.31k | inflatedFrame = loopData->inflationStream->inflate(loopData->zlibContext, {webSocketData->fragmentBuffer.data(), webSocketData->fragmentBuffer.length() - 9}, webSocketContextData->maxPayloadLength, true); | 140 | 1.31k | } | 141 | | | 142 | 1.31k | if (!inflatedFrame.has_value()) { | 143 | 0 | forceClose(webSocketState, s, ERR_TOO_BIG_MESSAGE_INFLATION); | 144 | 0 | return true; | 145 | 1.31k | } else { | 146 | 1.31k | data = (char *) inflatedFrame->data(); | 147 | 1.31k | length = inflatedFrame->length(); | 148 | 1.31k | } | 149 | | | 150 | | | 151 | 2.36k | } else { | 152 | | // reset length and data ptrs | 153 | 2.36k | length = webSocketData->fragmentBuffer.length(); | 154 | 2.36k | data = webSocketData->fragmentBuffer.data(); | 155 | 2.36k | } | 156 | | | 157 | | /* Check text messages for Utf-8 validity */ | 158 | 3.67k | if (opCode == 1 && !protocol::isValidUtf8((unsigned char *) data, length)) { | 159 | 324 | forceClose(webSocketState, s, ERR_INVALID_TEXT); | 160 | 324 | return true; | 161 | 324 | } | 162 | | | 163 | | /* Emit message and check for shutdown or close */ | 164 | 3.35k | if (webSocketContextData->messageHandler) { | 165 | 3.35k | webSocketContextData->messageHandler((WebSocket<SSL, isServer, USERDATA> *) s, std::string_view(data, length), (OpCode) opCode); | 166 | 3.35k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 167 | 388 | return true; | 168 | 388 | } | 169 | 3.35k | } | 170 | | | 171 | | /* If we shutdown or closed, this will be taken care of elsewhere */ | 172 | 2.96k | webSocketData->fragmentBuffer.clear(); | 173 | 2.96k | } | 174 | 20.0k | } | 175 | 86.4k | } else { | 176 | | /* Control frames need the websocket to send pings, pongs and close */ | 177 | 28.4k | WebSocket<SSL, isServer, USERDATA> *webSocket = (WebSocket<SSL, isServer, USERDATA> *) s; | 178 | | | 179 | 28.4k | if (!remainingBytes && fin && !webSocketData->controlTipLength) { | 180 | 19.5k | if (opCode == CLOSE) { | 181 | 5.49k | auto closeFrame = protocol::parseClosePayload(data, length); | 182 | 5.49k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 183 | 5.49k | return true; | 184 | 14.0k | } else { | 185 | 14.0k | if (opCode == PING) { | 186 | 10.2k | webSocket->send(std::string_view(data, length), (OpCode) OpCode::PONG); | 187 | 10.2k | if (webSocketContextData->pingHandler) { | 188 | 10.2k | webSocketContextData->pingHandler(webSocket, {data, length}); | 189 | 10.2k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 190 | 0 | return true; | 191 | 0 | } | 192 | 10.2k | } | 193 | 10.2k | } else if (opCode == PONG) { | 194 | 3.78k | if (webSocketContextData->pongHandler) { | 195 | 3.78k | webSocketContextData->pongHandler(webSocket, {data, length}); | 196 | 3.78k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 197 | 0 | return true; | 198 | 0 | } | 199 | 3.78k | } | 200 | 3.78k | } | 201 | 14.0k | } | 202 | 19.5k | } else { | 203 | | /* Here we never mind any size optimizations as we are in the worst possible path */ | 204 | 8.93k | webSocketData->fragmentBuffer.append(data, length); | 205 | 8.93k | webSocketData->controlTipLength += (unsigned int) length; | 206 | | | 207 | 8.93k | if (!remainingBytes && fin) { | 208 | 3.86k | char *controlBuffer = (char *) webSocketData->fragmentBuffer.data() + webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength; | 209 | 3.86k | if (opCode == CLOSE) { | 210 | 1.84k | protocol::CloseFrame closeFrame = protocol::parseClosePayload(controlBuffer, webSocketData->controlTipLength); | 211 | 1.84k | webSocket->end(closeFrame.code, std::string_view(closeFrame.message, closeFrame.length)); | 212 | 1.84k | return true; | 213 | 2.01k | } else { | 214 | 2.01k | if (opCode == PING) { | 215 | 581 | webSocket->send(std::string_view(controlBuffer, webSocketData->controlTipLength), (OpCode) OpCode::PONG); | 216 | 581 | if (webSocketContextData->pingHandler) { | 217 | 581 | webSocketContextData->pingHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 218 | 581 | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 219 | 0 | return true; | 220 | 0 | } | 221 | 581 | } | 222 | 1.43k | } else if (opCode == PONG) { | 223 | 1.43k | if (webSocketContextData->pongHandler) { | 224 | 1.43k | webSocketContextData->pongHandler(webSocket, std::string_view(controlBuffer, webSocketData->controlTipLength)); | 225 | 1.43k | if (us_socket_is_closed(SSL, (us_socket_t *) s) || webSocketData->isShuttingDown) { | 226 | 0 | return true; | 227 | 0 | } | 228 | 1.43k | } | 229 | 1.43k | } | 230 | 2.01k | } | 231 | | | 232 | | /* Same here, we do not care for any particular smart allocation scheme */ | 233 | 2.01k | webSocketData->fragmentBuffer.resize((unsigned int) webSocketData->fragmentBuffer.length() - webSocketData->controlTipLength); | 234 | 2.01k | webSocketData->controlTipLength = 0; | 235 | 2.01k | } | 236 | 8.93k | } | 237 | 28.4k | } | 238 | 102k | return false; | 239 | 114k | } |
|
240 | | |
241 | 1.37M | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { |
242 | 1.37M | 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.37M | return webSocketContextData->maxPayloadLength < length; |
246 | 1.37M | } EpollEchoServerPubSub.cpp:uWS::WebSocketContext<true, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 1.15M | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 1.15M | 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.15M | return webSocketContextData->maxPayloadLength < length; | 246 | 1.15M | } |
EpollHelloWorld.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 101k | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 101k | 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 | 101k | return webSocketContextData->maxPayloadLength < length; | 246 | 101k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::refusePayloadLength(unsigned long, uWS::WebSocketState<true>*, void*) Line | Count | Source | 241 | 126k | static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> */*wState*/, void *s) { | 242 | 126k | 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 | 126k | return webSocketContextData->maxPayloadLength < length; | 246 | 126k | } |
|
247 | | |
248 | 33.4k | 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 | 332k | 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 | 332k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); |
257 | 332k | if (!webSocketData->isShuttingDown) { |
258 | | /* Emit close event */ |
259 | 303k | 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 | 303k | 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 | 303k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); |
270 | 303k | webSocketData->subscriber = nullptr; |
271 | | |
272 | 303k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; |
273 | 303k | if (webSocketContextData->closeHandler) { |
274 | 302k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); |
275 | 302k | } |
276 | 303k | ((USERDATA *) ws->getUserData())->~USERDATA(); |
277 | 303k | } |
278 | | |
279 | | /* Destruct in-placed data struct */ |
280 | 332k | webSocketData->~WebSocketData(); |
281 | | |
282 | 332k | return s; |
283 | 332k | }); 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 | 115k | 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 | 115k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 115k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 103k | 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 | 103k | 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 | 103k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 103k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 103k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 103k | if (webSocketContextData->closeHandler) { | 274 | 103k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 103k | } | 276 | 103k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 103k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 115k | webSocketData->~WebSocketData(); | 281 | | | 282 | 115k | return s; | 283 | 115k | }); |
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 | 35.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 | 35.8k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 35.8k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 29.5k | 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 | 29.5k | 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 | 29.5k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 29.5k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 29.5k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 29.5k | if (webSocketContextData->closeHandler) { | 274 | 28.1k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 28.1k | } | 276 | 29.5k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 29.5k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 35.8k | webSocketData->~WebSocketData(); | 281 | | | 282 | 35.8k | return s; | 283 | 35.8k | }); |
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 | 180k | 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 | 180k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 180k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 170k | 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 | 170k | 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 | 170k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 170k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 170k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 170k | if (webSocketContextData->closeHandler) { | 274 | 170k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 170k | } | 276 | 170k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 170k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 180k | webSocketData->~WebSocketData(); | 281 | | | 282 | 180k | return s; | 283 | 180k | }); |
|
284 | | |
285 | | /* Handle WebSocket data streams */ |
286 | 227k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { |
287 | | |
288 | | /* We need the websocket data */ |
289 | 227k | 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 | 227k | if (webSocketData->isShuttingDown) { |
294 | 1.55k | return s; |
295 | 1.55k | } |
296 | | |
297 | 225k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
298 | 225k | auto *asyncSocket = (AsyncSocket<SSL> *) s; |
299 | | |
300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ |
301 | 225k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); |
302 | 225k | webSocketData->hasTimedOut = false; |
303 | | |
304 | | /* We always cork on data */ |
305 | 225k | asyncSocket->cork(); |
306 | | |
307 | | /* This parser has virtually no overhead */ |
308 | 225k | 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 | 225k | asyncSocket->uncork(); |
312 | | |
313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ |
314 | 225k | if (asyncSocket->getBufferedAmount() == 0) { |
315 | | /* We can now be in shutdown state */ |
316 | 52.2k | if (webSocketData->isShuttingDown) { |
317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ |
318 | 4.51k | asyncSocket->shutdown(); |
319 | 4.51k | } |
320 | 52.2k | } |
321 | | |
322 | 225k | return s; |
323 | 227k | }); 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 | 93.6k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 93.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 | 93.6k | if (webSocketData->isShuttingDown) { | 294 | 760 | return s; | 295 | 760 | } | 296 | | | 297 | 92.9k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 92.9k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 92.9k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 92.9k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 92.9k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 92.9k | 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 | 92.9k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 92.9k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 7.32k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.75k | asyncSocket->shutdown(); | 319 | 1.75k | } | 320 | 7.32k | } | 321 | | | 322 | 92.9k | return s; | 323 | 93.6k | }); |
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 | 66.2k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 66.2k | 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 | 66.2k | if (webSocketData->isShuttingDown) { | 294 | 395 | return s; | 295 | 395 | } | 296 | | | 297 | 65.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 65.8k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 65.8k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 65.8k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 65.8k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 65.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 | 65.8k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 65.8k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 31.7k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.25k | asyncSocket->shutdown(); | 319 | 1.25k | } | 320 | 31.7k | } | 321 | | | 322 | 65.8k | return s; | 323 | 66.2k | }); |
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 | 67.2k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 67.2k | 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 | 67.2k | if (webSocketData->isShuttingDown) { | 294 | 404 | return s; | 295 | 404 | } | 296 | | | 297 | 66.8k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 66.8k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 66.8k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 66.8k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 66.8k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 66.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 | 66.8k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 66.8k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 13.2k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 1.50k | asyncSocket->shutdown(); | 319 | 1.50k | } | 320 | 13.2k | } | 321 | | | 322 | 66.8k | return s; | 323 | 67.2k | }); |
|
324 | | |
325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ |
326 | 92.1k | 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 | 92.1k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { |
334 | 0 | return s; |
335 | 0 | } |
336 | | |
337 | 92.1k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; |
338 | 92.1k | 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 | 92.1k | unsigned int backpressure = asyncSocket->getBufferedAmount(); |
343 | | |
344 | | /* Drain as much as possible */ |
345 | 92.1k | 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 | 92.1k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { |
350 | 59.5k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
351 | 59.5k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); |
352 | 59.5k | webSocketData->hasTimedOut = false; |
353 | 59.5k | } |
354 | | |
355 | | /* Are we in (WebSocket) shutdown mode? */ |
356 | 92.1k | if (webSocketData->isShuttingDown) { |
357 | | /* Check if we just now drained completely */ |
358 | 7.65k | if (asyncSocket->getBufferedAmount() == 0) { |
359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ |
360 | 1.87k | asyncSocket->shutdown(); |
361 | 1.87k | } |
362 | 84.4k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { |
363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ |
364 | 56.2k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
365 | 56.2k | if (webSocketContextData->drainHandler) { |
366 | 56.0k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); |
367 | 56.0k | } |
368 | | /* No need to check for closed here as we leave the handler immediately*/ |
369 | 56.2k | } |
370 | | |
371 | 92.1k | return s; |
372 | 92.1k | }); 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 | 16.9k | 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 | 16.9k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 16.9k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 16.9k | 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 | 16.9k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 16.9k | 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 | 16.9k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 9.43k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 9.43k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 9.43k | webSocketData->hasTimedOut = false; | 353 | 9.43k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 16.9k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 4.04k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 409 | asyncSocket->shutdown(); | 361 | 409 | } | 362 | 12.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 | 8.70k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 8.70k | if (webSocketContextData->drainHandler) { | 366 | 8.70k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 8.70k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 8.70k | } | 370 | | | 371 | 16.9k | return s; | 372 | 16.9k | }); |
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 | 8.73k | 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 | 8.73k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 8.73k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 8.73k | 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 | 8.73k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 8.73k | 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 | 8.73k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 6.02k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 6.02k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 6.02k | webSocketData->hasTimedOut = false; | 353 | 6.02k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 8.73k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 1.08k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 209 | asyncSocket->shutdown(); | 361 | 209 | } | 362 | 7.64k | } 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.46k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 5.46k | if (webSocketContextData->drainHandler) { | 366 | 5.19k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 5.19k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 5.46k | } | 370 | | | 371 | 8.73k | return s; | 372 | 8.73k | }); |
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 | 66.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 | 66.4k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 0 | return s; | 335 | 0 | } | 336 | | | 337 | 66.4k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 66.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 | 66.4k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 66.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 | 66.4k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 44.1k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 44.1k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 44.1k | webSocketData->hasTimedOut = false; | 353 | 44.1k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 66.4k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 2.52k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 1.25k | asyncSocket->shutdown(); | 361 | 1.25k | } | 362 | 63.9k | } else if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 363 | | /* Only call drain if we actually drained backpressure or if we came here with 0 backpressure */ | 364 | 42.1k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 42.1k | if (webSocketContextData->drainHandler) { | 366 | 42.1k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 42.1k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 42.1k | } | 370 | | | 371 | 66.4k | return s; | 372 | 66.4k | }); |
|
373 | | |
374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ |
375 | 33.4k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { |
376 | | |
377 | | /* If we get a fin, we just close I guess */ |
378 | 6.56k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); |
379 | | |
380 | 6.56k | return s; |
381 | 6.56k | }); 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.50k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 1.50k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 1.50k | return s; | 381 | 1.50k | }); |
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 | 318 | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 318 | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 318 | return s; | 381 | 318 | }); |
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.74k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 4.74k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 4.74k | return s; | 381 | 4.74k | }); |
|
382 | | |
383 | 33.4k | 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 | 40.1k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { |
391 | | |
392 | 40.1k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); |
393 | 40.1k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); |
394 | | |
395 | 40.1k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { |
396 | 24.4k | webSocketData->hasTimedOut = true; |
397 | 24.4k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); |
398 | | /* Send ping without being corked */ |
399 | 24.4k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); |
400 | 24.4k | return s; |
401 | 24.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 | 15.6k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); |
406 | | |
407 | 15.6k | return s; |
408 | 40.1k | }); 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 | 209 | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 209 | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 209 | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 209 | 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 | 209 | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 209 | return s; | 408 | 209 | }); |
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 | 6.57k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 6.57k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 6.57k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 6.57k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 2.57k | webSocketData->hasTimedOut = true; | 397 | 2.57k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 2.57k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 2.57k | return s; | 401 | 2.57k | } | 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.99k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 3.99k | return s; | 408 | 6.57k | }); |
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 | 33.3k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 33.3k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 33.3k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 33.3k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 21.8k | webSocketData->hasTimedOut = true; | 397 | 21.8k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 21.8k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 21.8k | return s; | 401 | 21.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 | 11.4k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 11.4k | return s; | 408 | 33.3k | }); |
|
409 | | |
410 | 33.4k | return this; |
411 | 33.4k | } 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.5k | 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.5k | 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.5k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 13.5k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 13.5k | 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.5k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 13.5k | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 13.5k | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 13.5k | } | 266 | 13.5k | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 13.5k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 13.5k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 13.5k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 13.5k | if (webSocketContextData->closeHandler) { | 274 | 13.5k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 13.5k | } | 276 | 13.5k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 13.5k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 13.5k | webSocketData->~WebSocketData(); | 281 | | | 282 | 13.5k | return s; | 283 | 13.5k | }); | 284 | | | 285 | | /* Handle WebSocket data streams */ | 286 | 13.5k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 13.5k | 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.5k | if (webSocketData->isShuttingDown) { | 294 | 13.5k | return s; | 295 | 13.5k | } | 296 | | | 297 | 13.5k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 13.5k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 13.5k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 13.5k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 13.5k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 13.5k | 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.5k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 13.5k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 13.5k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 13.5k | asyncSocket->shutdown(); | 319 | 13.5k | } | 320 | 13.5k | } | 321 | | | 322 | 13.5k | return s; | 323 | 13.5k | }); | 324 | | | 325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 326 | 13.5k | 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.5k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 13.5k | return s; | 335 | 13.5k | } | 336 | | | 337 | 13.5k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 13.5k | 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.5k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 13.5k | 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.5k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 13.5k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 13.5k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 13.5k | webSocketData->hasTimedOut = false; | 353 | 13.5k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 13.5k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 13.5k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 13.5k | asyncSocket->shutdown(); | 361 | 13.5k | } | 362 | 13.5k | } 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.5k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 13.5k | if (webSocketContextData->drainHandler) { | 366 | 13.5k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 13.5k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 13.5k | } | 370 | | | 371 | 13.5k | return s; | 372 | 13.5k | }); | 373 | | | 374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ | 375 | 13.5k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 13.5k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 13.5k | return s; | 381 | 13.5k | }); | 382 | | | 383 | 13.5k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { | 384 | 13.5k | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); | 385 | | | 386 | 13.5k | return s; | 387 | 13.5k | }); | 388 | | | 389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 390 | 13.5k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 13.5k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 13.5k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 13.5k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 13.5k | webSocketData->hasTimedOut = true; | 397 | 13.5k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 13.5k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 13.5k | return s; | 401 | 13.5k | } | 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.5k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 13.5k | return s; | 408 | 13.5k | }); | 409 | | | 410 | 13.5k | return this; | 411 | 13.5k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::init() Line | Count | Source | 248 | 13.9k | 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.9k | 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.9k | WebSocketData *webSocketData = (WebSocketData *) (us_socket_ext(SSL, s)); | 257 | 13.9k | if (!webSocketData->isShuttingDown) { | 258 | | /* Emit close event */ | 259 | 13.9k | 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.9k | if (webSocketData->subscriber && webSocketContextData->subscriptionHandler) { | 263 | 13.9k | for (Topic *t : webSocketData->subscriber->topics) { | 264 | 13.9k | webSocketContextData->subscriptionHandler((WebSocket<SSL, isServer, USERDATA> *) s, t->name, (int) t->size() - 1, (int) t->size()); | 265 | 13.9k | } | 266 | 13.9k | } | 267 | | | 268 | | /* Make sure to unsubscribe from any pub/sub node at exit */ | 269 | 13.9k | webSocketContextData->topicTree->freeSubscriber(webSocketData->subscriber); | 270 | 13.9k | webSocketData->subscriber = nullptr; | 271 | | | 272 | 13.9k | auto *ws = (WebSocket<SSL, isServer, USERDATA> *) s; | 273 | 13.9k | if (webSocketContextData->closeHandler) { | 274 | 13.9k | webSocketContextData->closeHandler(ws, 1006, {(char *) reason, (size_t) code}); | 275 | 13.9k | } | 276 | 13.9k | ((USERDATA *) ws->getUserData())->~USERDATA(); | 277 | 13.9k | } | 278 | | | 279 | | /* Destruct in-placed data struct */ | 280 | 13.9k | webSocketData->~WebSocketData(); | 281 | | | 282 | 13.9k | return s; | 283 | 13.9k | }); | 284 | | | 285 | | /* Handle WebSocket data streams */ | 286 | 13.9k | us_socket_context_on_data(SSL, getSocketContext(), [](auto *s, char *data, int length) { | 287 | | | 288 | | /* We need the websocket data */ | 289 | 13.9k | 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.9k | if (webSocketData->isShuttingDown) { | 294 | 13.9k | return s; | 295 | 13.9k | } | 296 | | | 297 | 13.9k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 298 | 13.9k | auto *asyncSocket = (AsyncSocket<SSL> *) s; | 299 | | | 300 | | /* Every time we get data and not in shutdown state we simply reset the timeout */ | 301 | 13.9k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 302 | 13.9k | webSocketData->hasTimedOut = false; | 303 | | | 304 | | /* We always cork on data */ | 305 | 13.9k | asyncSocket->cork(); | 306 | | | 307 | | /* This parser has virtually no overhead */ | 308 | 13.9k | 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.9k | asyncSocket->uncork(); | 312 | | | 313 | | /* If uncorking was successful and we are in shutdown state then send TCP FIN */ | 314 | 13.9k | if (asyncSocket->getBufferedAmount() == 0) { | 315 | | /* We can now be in shutdown state */ | 316 | 13.9k | if (webSocketData->isShuttingDown) { | 317 | | /* Shutting down a closed socket is handled by uSockets and just fine */ | 318 | 13.9k | asyncSocket->shutdown(); | 319 | 13.9k | } | 320 | 13.9k | } | 321 | | | 322 | 13.9k | return s; | 323 | 13.9k | }); | 324 | | | 325 | | /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */ | 326 | 13.9k | 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.9k | if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) { | 334 | 13.9k | return s; | 335 | 13.9k | } | 336 | | | 337 | 13.9k | AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; | 338 | 13.9k | 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.9k | unsigned int backpressure = asyncSocket->getBufferedAmount(); | 343 | | | 344 | | /* Drain as much as possible */ | 345 | 13.9k | 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.9k | if (!backpressure || backpressure > asyncSocket->getBufferedAmount()) { | 350 | 13.9k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 351 | 13.9k | asyncSocket->timeout(webSocketContextData->idleTimeoutComponents.first); | 352 | 13.9k | webSocketData->hasTimedOut = false; | 353 | 13.9k | } | 354 | | | 355 | | /* Are we in (WebSocket) shutdown mode? */ | 356 | 13.9k | if (webSocketData->isShuttingDown) { | 357 | | /* Check if we just now drained completely */ | 358 | 13.9k | if (asyncSocket->getBufferedAmount() == 0) { | 359 | | /* Now perform the actual TCP/TLS shutdown which was postponed due to backpressure */ | 360 | 13.9k | asyncSocket->shutdown(); | 361 | 13.9k | } | 362 | 13.9k | } 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.9k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 365 | 13.9k | if (webSocketContextData->drainHandler) { | 366 | 13.9k | webSocketContextData->drainHandler((WebSocket<SSL, isServer, USERDATA> *) s); | 367 | 13.9k | } | 368 | | /* No need to check for closed here as we leave the handler immediately*/ | 369 | 13.9k | } | 370 | | | 371 | 13.9k | return s; | 372 | 13.9k | }); | 373 | | | 374 | | /* Handle FIN, WebSocket does not support half-closed sockets, so simply close */ | 375 | 13.9k | us_socket_context_on_end(SSL, getSocketContext(), [](auto *s) { | 376 | | | 377 | | /* If we get a fin, we just close I guess */ | 378 | 13.9k | us_socket_close(SSL, (us_socket_t *) s, (int) ERR_TCP_FIN.length(), (void *) ERR_TCP_FIN.data()); | 379 | | | 380 | 13.9k | return s; | 381 | 13.9k | }); | 382 | | | 383 | 13.9k | us_socket_context_on_long_timeout(SSL, getSocketContext(), [](auto *s) { | 384 | 13.9k | ((WebSocket<SSL, isServer, USERDATA> *) s)->end(1000, "please reconnect"); | 385 | | | 386 | 13.9k | return s; | 387 | 13.9k | }); | 388 | | | 389 | | /* Handle socket timeouts, simply close them so to not confuse client with FIN */ | 390 | 13.9k | us_socket_context_on_timeout(SSL, getSocketContext(), [](auto *s) { | 391 | | | 392 | 13.9k | auto *webSocketData = (WebSocketData *)(us_socket_ext(SSL, s)); | 393 | 13.9k | auto *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, us_socket_context(SSL, (us_socket_t *) s)); | 394 | | | 395 | 13.9k | if (webSocketContextData->sendPingsAutomatically && !webSocketData->isShuttingDown && !webSocketData->hasTimedOut) { | 396 | 13.9k | webSocketData->hasTimedOut = true; | 397 | 13.9k | us_socket_timeout(SSL, s, webSocketContextData->idleTimeoutComponents.second); | 398 | | /* Send ping without being corked */ | 399 | 13.9k | ((AsyncSocket<SSL> *) s)->write("\x89\x00", 2); | 400 | 13.9k | return s; | 401 | 13.9k | } | 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.9k | forceClose(nullptr, s, ERR_WEBSOCKET_TIMEOUT); | 406 | | | 407 | 13.9k | return s; | 408 | 13.9k | }); | 409 | | | 410 | 13.9k | return this; | 411 | 13.9k | } |
|
412 | | |
413 | 33.4k | void free() { |
414 | 33.4k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); |
415 | 33.4k | webSocketContextData->~WebSocketContextData(); |
416 | | |
417 | 33.4k | us_socket_context_free(SSL, (us_socket_context_t *) this); |
418 | 33.4k | } 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.5k | void free() { | 414 | 13.5k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 415 | 13.5k | webSocketContextData->~WebSocketContextData(); | 416 | | | 417 | 13.5k | us_socket_context_free(SSL, (us_socket_context_t *) this); | 418 | 13.5k | } |
EpollEchoServer.cpp:uWS::WebSocketContext<false, true, test()::PerSocketData>::free() Line | Count | Source | 413 | 13.9k | void free() { | 414 | 13.9k | WebSocketContextData<SSL, USERDATA> *webSocketContextData = (WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *) this); | 415 | 13.9k | webSocketContextData->~WebSocketContextData(); | 416 | | | 417 | 13.9k | us_socket_context_free(SSL, (us_socket_context_t *) this); | 418 | 13.9k | } |
|
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 | 33.4k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { |
423 | 33.4k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); |
424 | 33.4k | if (!webSocketContext) { |
425 | 0 | return nullptr; |
426 | 0 | } |
427 | | |
428 | | /* Init socket context data */ |
429 | 33.4k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); |
430 | 33.4k | return webSocketContext->init(); |
431 | 33.4k | } 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.5k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { | 423 | 13.5k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); | 424 | 13.5k | if (!webSocketContext) { | 425 | 0 | return nullptr; | 426 | 0 | } | 427 | | | 428 | | /* Init socket context data */ | 429 | 13.5k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); | 430 | 13.5k | return webSocketContext->init(); | 431 | 13.5k | } |
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 | 13.9k | static WebSocketContext *create(Loop */*loop*/, us_socket_context_t *parentSocketContext, TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree) { | 423 | 13.9k | WebSocketContext *webSocketContext = (WebSocketContext *) us_create_child_socket_context(SSL, parentSocketContext, sizeof(WebSocketContextData<SSL, USERDATA>)); | 424 | 13.9k | if (!webSocketContext) { | 425 | 0 | return nullptr; | 426 | 0 | } | 427 | | | 428 | | /* Init socket context data */ | 429 | 13.9k | new ((WebSocketContextData<SSL, USERDATA> *) us_socket_context_ext(SSL, (us_socket_context_t *)webSocketContext)) WebSocketContextData<SSL, USERDATA>(topicTree); | 430 | 13.9k | return webSocketContext->init(); | 431 | 13.9k | } |
|
432 | | }; |
433 | | |
434 | | } |
435 | | |
436 | | #endif // UWS_WEBSOCKETCONTEXT_H |