/src/uWebSockets/src/AsyncSocket.h
Line | Count | Source |
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_ASYNCSOCKET_H |
19 | | #define UWS_ASYNCSOCKET_H |
20 | | |
21 | | /* This class implements async socket memory management strategies */ |
22 | | |
23 | | /* NOTE: Many unsigned/signed conversion warnings could be solved by moving from int length |
24 | | * to unsigned length for everything to/from uSockets - this would however remove the opportunity |
25 | | * to signal error with -1 (which is how the entire UNIX syscalling is built). */ |
26 | | |
27 | | #include <cstring> |
28 | | #include <iostream> |
29 | | |
30 | | #include "libusockets.h" |
31 | | |
32 | | #include "LoopData.h" |
33 | | #include "AsyncSocketData.h" |
34 | | |
35 | | namespace uWS { |
36 | | |
37 | | enum SendBufferAttribute { |
38 | | NEEDS_NOTHING, |
39 | | NEEDS_DRAIN, |
40 | | NEEDS_UNCORK |
41 | | }; |
42 | | |
43 | | template <bool, bool, typename> struct WebSocketContext; |
44 | | |
45 | | template <bool SSL> |
46 | | struct AsyncSocket { |
47 | | /* This guy is promiscuous */ |
48 | | template <bool> friend struct HttpContext; |
49 | | template <bool, bool, typename> friend struct WebSocketContext; |
50 | | template <bool> friend struct TemplatedApp; |
51 | | template <bool, typename> friend struct WebSocketContextData; |
52 | | template <typename, typename> friend struct TopicTree; |
53 | | template <bool> friend struct HttpResponse; |
54 | | |
55 | | private: |
56 | | /* Helper, do not use directly (todo: move to uSockets or de-crazify) */ |
57 | | void throttle_helper(int toggle) { |
58 | | /* These should be exposed by uSockets */ |
59 | | static thread_local int us_events[2] = {0, 0}; |
60 | | |
61 | | struct us_poll_t *p = (struct us_poll_t *) this; |
62 | | struct us_loop_t *loop = us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this)); |
63 | | |
64 | | if (toggle) { |
65 | | /* Pause */ |
66 | | int events = us_poll_events(p); |
67 | | if (events) { |
68 | | us_events[getBufferedAmount() ? 1 : 0] = events; |
69 | | } |
70 | | us_poll_change(p, loop, 0); |
71 | | } else { |
72 | | /* Resume */ |
73 | | int events = us_events[getBufferedAmount() ? 1 : 0]; |
74 | | us_poll_change(p, loop, events); |
75 | | } |
76 | | } |
77 | | |
78 | | protected: |
79 | | /* Returns SSL pointer or FD as pointer */ |
80 | 33.2k | void *getNativeHandle() { |
81 | 33.2k | return us_socket_get_native_handle(SSL, (us_socket_t *) this); |
82 | 33.2k | } |
83 | | |
84 | | /* Get loop data for socket */ |
85 | 24.6M | LoopData *getLoopData() { |
86 | 24.6M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); |
87 | 24.6M | } uWS::AsyncSocket<true>::getLoopData() Line | Count | Source | 85 | 6.99M | LoopData *getLoopData() { | 86 | 6.99M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); | 87 | 6.99M | } |
uWS::AsyncSocket<false>::getLoopData() Line | Count | Source | 85 | 17.6M | LoopData *getLoopData() { | 86 | 17.6M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); | 87 | 17.6M | } |
|
88 | | |
89 | | /* Get socket extension */ |
90 | 18.0M | AsyncSocketData<SSL> *getAsyncSocketData() { |
91 | 18.0M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); |
92 | 18.0M | } uWS::AsyncSocket<true>::getAsyncSocketData() Line | Count | Source | 90 | 4.56M | AsyncSocketData<SSL> *getAsyncSocketData() { | 91 | 4.56M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); | 92 | 4.56M | } |
uWS::AsyncSocket<false>::getAsyncSocketData() Line | Count | Source | 90 | 13.5M | AsyncSocketData<SSL> *getAsyncSocketData() { | 91 | 13.5M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); | 92 | 13.5M | } |
|
93 | | |
94 | | /* Socket timeout */ |
95 | 915k | void timeout(unsigned int seconds) { |
96 | 915k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); |
97 | 915k | } uWS::AsyncSocket<true>::timeout(unsigned int) Line | Count | Source | 95 | 240k | void timeout(unsigned int seconds) { | 96 | 240k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); | 97 | 240k | } |
uWS::AsyncSocket<false>::timeout(unsigned int) Line | Count | Source | 95 | 675k | void timeout(unsigned int seconds) { | 96 | 675k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); | 97 | 675k | } |
|
98 | | |
99 | | /* Shutdown socket without any automatic drainage */ |
100 | 8.37k | void shutdown() { |
101 | 8.37k | us_socket_shutdown(SSL, (us_socket_t *) this); |
102 | 8.37k | } uWS::AsyncSocket<true>::shutdown() Line | Count | Source | 100 | 2.68k | void shutdown() { | 101 | 2.68k | us_socket_shutdown(SSL, (us_socket_t *) this); | 102 | 2.68k | } |
uWS::AsyncSocket<false>::shutdown() Line | Count | Source | 100 | 5.68k | void shutdown() { | 101 | 5.68k | us_socket_shutdown(SSL, (us_socket_t *) this); | 102 | 5.68k | } |
|
103 | | |
104 | | /* Experimental pause */ |
105 | | us_socket_t *pause() { |
106 | | throttle_helper(1); |
107 | | return (us_socket_t *) this; |
108 | | } |
109 | | |
110 | | /* Experimental resume */ |
111 | | us_socket_t *resume() { |
112 | | throttle_helper(0); |
113 | | return (us_socket_t *) this; |
114 | | } |
115 | | |
116 | | /* Immediately close socket */ |
117 | 205k | us_socket_t *close() { |
118 | 205k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); |
119 | 205k | } uWS::AsyncSocket<true>::close() Line | Count | Source | 117 | 75.0k | us_socket_t *close() { | 118 | 75.0k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); | 119 | 75.0k | } |
uWS::AsyncSocket<false>::close() Line | Count | Source | 117 | 130k | us_socket_t *close() { | 118 | 130k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); | 119 | 130k | } |
|
120 | | |
121 | 361k | void corkUnchecked() { |
122 | | /* What if another socket is corked? */ |
123 | 361k | getLoopData()->corkedSocket = this; |
124 | 361k | } uWS::AsyncSocket<true>::corkUnchecked() Line | Count | Source | 121 | 97.5k | void corkUnchecked() { | 122 | | /* What if another socket is corked? */ | 123 | 97.5k | getLoopData()->corkedSocket = this; | 124 | 97.5k | } |
uWS::AsyncSocket<false>::corkUnchecked() Line | Count | Source | 121 | 263k | void corkUnchecked() { | 122 | | /* What if another socket is corked? */ | 123 | 263k | getLoopData()->corkedSocket = this; | 124 | 263k | } |
|
125 | | |
126 | 0 | void uncorkWithoutSending() { |
127 | 0 | if (isCorked()) { |
128 | 0 | getLoopData()->corkedSocket = nullptr; |
129 | 0 | } |
130 | 0 | } Unexecuted instantiation: uWS::AsyncSocket<true>::uncorkWithoutSending() Unexecuted instantiation: uWS::AsyncSocket<false>::uncorkWithoutSending() |
131 | | |
132 | | /* Cork this socket. Only one socket may ever be corked per-loop at any given time */ |
133 | 3.33M | void cork() { |
134 | | /* Extra check for invalid corking of others */ |
135 | 3.33M | if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) { |
136 | 0 | std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl; |
137 | 0 | std::terminate(); |
138 | 0 | } |
139 | | |
140 | | /* What if another socket is corked? */ |
141 | 3.33M | getLoopData()->corkedSocket = this; |
142 | 3.33M | } uWS::AsyncSocket<true>::cork() Line | Count | Source | 133 | 1.07M | void cork() { | 134 | | /* Extra check for invalid corking of others */ | 135 | 1.07M | if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) { | 136 | 0 | std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl; | 137 | 0 | std::terminate(); | 138 | 0 | } | 139 | | | 140 | | /* What if another socket is corked? */ | 141 | 1.07M | getLoopData()->corkedSocket = this; | 142 | 1.07M | } |
uWS::AsyncSocket<false>::cork() Line | Count | Source | 133 | 2.25M | void cork() { | 134 | | /* Extra check for invalid corking of others */ | 135 | 2.25M | if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) { | 136 | 0 | std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl; | 137 | 0 | std::terminate(); | 138 | 0 | } | 139 | | | 140 | | /* What if another socket is corked? */ | 141 | 2.25M | getLoopData()->corkedSocket = this; | 142 | 2.25M | } |
|
143 | | |
144 | | /* Returns whether we are corked or not */ |
145 | 1.07M | bool isCorked() { |
146 | 1.07M | return getLoopData()->corkedSocket == this; |
147 | 1.07M | } uWS::AsyncSocket<true>::isCorked() Line | Count | Source | 145 | 275k | bool isCorked() { | 146 | 275k | return getLoopData()->corkedSocket == this; | 147 | 275k | } |
uWS::AsyncSocket<false>::isCorked() Line | Count | Source | 145 | 799k | bool isCorked() { | 146 | 799k | return getLoopData()->corkedSocket == this; | 147 | 799k | } |
|
148 | | |
149 | | /* Returns whether we could cork (it is free) */ |
150 | 36.9k | bool canCork() { |
151 | 36.9k | return getLoopData()->corkedSocket == nullptr; |
152 | 36.9k | } uWS::AsyncSocket<true>::canCork() Line | Count | Source | 150 | 12.8k | bool canCork() { | 151 | 12.8k | return getLoopData()->corkedSocket == nullptr; | 152 | 12.8k | } |
uWS::AsyncSocket<false>::canCork() Line | Count | Source | 150 | 24.1k | bool canCork() { | 151 | 24.1k | return getLoopData()->corkedSocket == nullptr; | 152 | 24.1k | } |
|
153 | | |
154 | | /* Returns a suitable buffer for temporary assemblation of send data */ |
155 | 157k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { |
156 | | /* First step is to determine if we already have backpressure or not */ |
157 | 157k | LoopData *loopData = getLoopData(); |
158 | 157k | BackPressure &backPressure = getAsyncSocketData()->buffer; |
159 | 157k | size_t existingBackpressure = backPressure.length(); |
160 | 157k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { |
161 | | /* Cork automatically if we can */ |
162 | 32.4k | if (isCorked()) { |
163 | 29.2k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; |
164 | 29.2k | loopData->corkOffset += (unsigned int) size; |
165 | 29.2k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; |
166 | 29.2k | } else { |
167 | 3.21k | cork(); |
168 | 3.21k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; |
169 | 3.21k | loopData->corkOffset += (unsigned int) size; |
170 | 3.21k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; |
171 | 3.21k | } |
172 | 125k | } else { |
173 | | |
174 | | /* If we are corked and there is already data in the cork buffer, |
175 | | mark how much is ours and reset it */ |
176 | 125k | unsigned int ourCorkOffset = 0; |
177 | 125k | if (isCorked() && loopData->corkOffset) { |
178 | 73 | ourCorkOffset = loopData->corkOffset; |
179 | 73 | loopData->corkOffset = 0; |
180 | 73 | } |
181 | | |
182 | | /* Fallback is to use the backpressure as buffer */ |
183 | 125k | backPressure.resize(ourCorkOffset + existingBackpressure + size); |
184 | | |
185 | | /* And copy corkbuffer in front */ |
186 | 125k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); |
187 | | |
188 | 125k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; |
189 | 125k | } |
190 | 157k | } uWS::AsyncSocket<true>::getSendBuffer(unsigned long) Line | Count | Source | 155 | 37.6k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { | 156 | | /* First step is to determine if we already have backpressure or not */ | 157 | 37.6k | LoopData *loopData = getLoopData(); | 158 | 37.6k | BackPressure &backPressure = getAsyncSocketData()->buffer; | 159 | 37.6k | size_t existingBackpressure = backPressure.length(); | 160 | 37.6k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { | 161 | | /* Cork automatically if we can */ | 162 | 7.24k | if (isCorked()) { | 163 | 5.64k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 164 | 5.64k | loopData->corkOffset += (unsigned int) size; | 165 | 5.64k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; | 166 | 5.64k | } else { | 167 | 1.60k | cork(); | 168 | 1.60k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 169 | 1.60k | loopData->corkOffset += (unsigned int) size; | 170 | 1.60k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; | 171 | 1.60k | } | 172 | 30.3k | } else { | 173 | | | 174 | | /* If we are corked and there is already data in the cork buffer, | 175 | | mark how much is ours and reset it */ | 176 | 30.3k | unsigned int ourCorkOffset = 0; | 177 | 30.3k | if (isCorked() && loopData->corkOffset) { | 178 | 0 | ourCorkOffset = loopData->corkOffset; | 179 | 0 | loopData->corkOffset = 0; | 180 | 0 | } | 181 | | | 182 | | /* Fallback is to use the backpressure as buffer */ | 183 | 30.3k | backPressure.resize(ourCorkOffset + existingBackpressure + size); | 184 | | | 185 | | /* And copy corkbuffer in front */ | 186 | 30.3k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); | 187 | | | 188 | 30.3k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; | 189 | 30.3k | } | 190 | 37.6k | } |
uWS::AsyncSocket<false>::getSendBuffer(unsigned long) Line | Count | Source | 155 | 120k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { | 156 | | /* First step is to determine if we already have backpressure or not */ | 157 | 120k | LoopData *loopData = getLoopData(); | 158 | 120k | BackPressure &backPressure = getAsyncSocketData()->buffer; | 159 | 120k | size_t existingBackpressure = backPressure.length(); | 160 | 120k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { | 161 | | /* Cork automatically if we can */ | 162 | 25.1k | if (isCorked()) { | 163 | 23.5k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 164 | 23.5k | loopData->corkOffset += (unsigned int) size; | 165 | 23.5k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; | 166 | 23.5k | } else { | 167 | 1.61k | cork(); | 168 | 1.61k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 169 | 1.61k | loopData->corkOffset += (unsigned int) size; | 170 | 1.61k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; | 171 | 1.61k | } | 172 | 94.9k | } else { | 173 | | | 174 | | /* If we are corked and there is already data in the cork buffer, | 175 | | mark how much is ours and reset it */ | 176 | 94.9k | unsigned int ourCorkOffset = 0; | 177 | 94.9k | if (isCorked() && loopData->corkOffset) { | 178 | 73 | ourCorkOffset = loopData->corkOffset; | 179 | 73 | loopData->corkOffset = 0; | 180 | 73 | } | 181 | | | 182 | | /* Fallback is to use the backpressure as buffer */ | 183 | 94.9k | backPressure.resize(ourCorkOffset + existingBackpressure + size); | 184 | | | 185 | | /* And copy corkbuffer in front */ | 186 | 94.9k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); | 187 | | | 188 | 94.9k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; | 189 | 94.9k | } | 190 | 120k | } |
|
191 | | |
192 | | /* Returns the user space backpressure. */ |
193 | 688k | unsigned int getBufferedAmount() { |
194 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ |
195 | 688k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); |
196 | 688k | } uWS::AsyncSocket<true>::getBufferedAmount() Line | Count | Source | 193 | 168k | unsigned int getBufferedAmount() { | 194 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ | 195 | 168k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); | 196 | 168k | } |
uWS::AsyncSocket<false>::getBufferedAmount() Line | Count | Source | 193 | 520k | unsigned int getBufferedAmount() { | 194 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ | 195 | 520k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); | 196 | 520k | } |
|
197 | | |
198 | | /* Returns the text representation of an IPv4 or IPv6 address */ |
199 | 33.2k | std::string_view addressAsText(std::string_view binary) { |
200 | 33.2k | static thread_local char buf[64]; |
201 | 33.2k | int ipLength = 0; |
202 | | |
203 | 33.2k | if (!binary.length()) { |
204 | 0 | return {}; |
205 | 0 | } |
206 | | |
207 | 33.2k | unsigned char *b = (unsigned char *) binary.data(); |
208 | | |
209 | 33.2k | if (binary.length() == 4) { |
210 | 17.4k | ipLength = snprintf(buf, 64, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]); |
211 | 17.4k | } else { |
212 | 15.8k | ipLength = snprintf(buf, 64, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", |
213 | 15.8k | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], |
214 | 15.8k | b[12], b[13], b[14], b[15]); |
215 | 15.8k | } |
216 | | |
217 | 33.2k | return {buf, (unsigned int) ipLength}; |
218 | 33.2k | } |
219 | | |
220 | | /* Returns the remote IP address or empty string on failure */ |
221 | 33.2k | std::string_view getRemoteAddress() { |
222 | 33.2k | static thread_local char buf[16]; |
223 | 33.2k | int ipLength = 16; |
224 | 33.2k | us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength); |
225 | 33.2k | return std::string_view(buf, (unsigned int) ipLength); |
226 | 33.2k | } |
227 | | |
228 | | /* Returns the text representation of IP */ |
229 | 33.2k | std::string_view getRemoteAddressAsText() { |
230 | 33.2k | return addressAsText(getRemoteAddress()); |
231 | 33.2k | } |
232 | | |
233 | | /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible. |
234 | | * Returns pair of bytes written (anywhere) and whether or not this call resulted in the polling for |
235 | | * writable (or we are in a state that implies polling for writable). */ |
236 | 14.0M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { |
237 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ |
238 | 14.0M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { |
239 | 1.50M | return {length, false}; |
240 | 1.50M | } |
241 | | |
242 | 12.4M | LoopData *loopData = getLoopData(); |
243 | 12.4M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); |
244 | | |
245 | | /* We are limited if we have a per-socket buffer */ |
246 | 12.4M | if (asyncSocketData->buffer.length()) { |
247 | | /* Write off as much as we can */ |
248 | 794k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); |
249 | | |
250 | | /* On failure return, otherwise continue down the function */ |
251 | 794k | if ((unsigned int) written < asyncSocketData->buffer.length()) { |
252 | | |
253 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ |
254 | 768k | asyncSocketData->buffer.erase((unsigned int) written); |
255 | | |
256 | 768k | if (optionally) { |
257 | | /* Thankfully we can exit early here */ |
258 | 20.3k | return {0, true}; |
259 | 747k | } else { |
260 | | /* This path is horrible and points towards erroneous usage */ |
261 | 747k | asyncSocketData->buffer.append(src, (unsigned int) length); |
262 | | |
263 | 747k | return {length, true}; |
264 | 747k | } |
265 | 768k | } |
266 | | |
267 | | /* At this point we simply have no buffer and can continue as normal */ |
268 | 25.8k | asyncSocketData->buffer.clear(); |
269 | 25.8k | } |
270 | | |
271 | 11.7M | if (length) { |
272 | 10.4M | if (loopData->corkedSocket == this) { |
273 | | /* We are corked */ |
274 | 10.0M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { |
275 | | /* If the entire chunk fits in cork buffer */ |
276 | 10.0M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); |
277 | 10.0M | loopData->corkOffset += (unsigned int) length; |
278 | | /* Fall through to default return */ |
279 | 10.0M | } else { |
280 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ |
281 | | if constexpr (false) { |
282 | | /* Cork up as much as we can */ |
283 | | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; |
284 | | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); |
285 | | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; |
286 | | |
287 | | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); |
288 | | return {written + (int) stripped, failed}; |
289 | | } |
290 | | |
291 | | /* For non-SSL we take the penalty of two syscalls */ |
292 | 0 | return uncork(src, length, optionally); |
293 | 0 | } |
294 | 10.0M | } else { |
295 | | /* We are not corked */ |
296 | 416k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); |
297 | | |
298 | | /* Did we fail? */ |
299 | 416k | if (written < length) { |
300 | | /* If the write was optional then just bail out */ |
301 | 384k | if (optionally) { |
302 | 0 | return {written, true}; |
303 | 0 | } |
304 | | |
305 | | /* Fall back to worst possible case (should be very rare for HTTP) */ |
306 | | /* At least we can reserve room for next chunk if we know it up front */ |
307 | 384k | if (nextLength) { |
308 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); |
309 | 0 | } |
310 | | |
311 | | /* Buffer this chunk */ |
312 | 384k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); |
313 | | |
314 | | /* Return the failure */ |
315 | 384k | return {length, true}; |
316 | 384k | } |
317 | | /* Fall through to default return */ |
318 | 416k | } |
319 | 10.4M | } |
320 | | |
321 | | /* Default fall through return */ |
322 | 11.3M | return {length, false}; |
323 | 11.7M | } uWS::AsyncSocket<true>::write(char const*, int, bool, int) Line | Count | Source | 236 | 3.71M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { | 237 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ | 238 | 3.71M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { | 239 | 501k | return {length, false}; | 240 | 501k | } | 241 | | | 242 | 3.21M | LoopData *loopData = getLoopData(); | 243 | 3.21M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); | 244 | | | 245 | | /* We are limited if we have a per-socket buffer */ | 246 | 3.21M | if (asyncSocketData->buffer.length()) { | 247 | | /* Write off as much as we can */ | 248 | 98.3k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); | 249 | | | 250 | | /* On failure return, otherwise continue down the function */ | 251 | 98.3k | if ((unsigned int) written < asyncSocketData->buffer.length()) { | 252 | | | 253 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ | 254 | 93.2k | asyncSocketData->buffer.erase((unsigned int) written); | 255 | | | 256 | 93.2k | if (optionally) { | 257 | | /* Thankfully we can exit early here */ | 258 | 1.84k | return {0, true}; | 259 | 91.4k | } else { | 260 | | /* This path is horrible and points towards erroneous usage */ | 261 | 91.4k | asyncSocketData->buffer.append(src, (unsigned int) length); | 262 | | | 263 | 91.4k | return {length, true}; | 264 | 91.4k | } | 265 | 93.2k | } | 266 | | | 267 | | /* At this point we simply have no buffer and can continue as normal */ | 268 | 5.04k | asyncSocketData->buffer.clear(); | 269 | 5.04k | } | 270 | | | 271 | 3.12M | if (length) { | 272 | 2.68M | if (loopData->corkedSocket == this) { | 273 | | /* We are corked */ | 274 | 2.57M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { | 275 | | /* If the entire chunk fits in cork buffer */ | 276 | 2.57M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); | 277 | 2.57M | loopData->corkOffset += (unsigned int) length; | 278 | | /* Fall through to default return */ | 279 | 2.57M | } else { | 280 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ | 281 | | if constexpr (false) { | 282 | | /* Cork up as much as we can */ | 283 | | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; | 284 | | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); | 285 | | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; | 286 | | | 287 | | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); | 288 | | return {written + (int) stripped, failed}; | 289 | | } | 290 | | | 291 | | /* For non-SSL we take the penalty of two syscalls */ | 292 | 0 | return uncork(src, length, optionally); | 293 | 0 | } | 294 | 2.57M | } else { | 295 | | /* We are not corked */ | 296 | 107k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); | 297 | | | 298 | | /* Did we fail? */ | 299 | 107k | if (written < length) { | 300 | | /* If the write was optional then just bail out */ | 301 | 104k | if (optionally) { | 302 | 0 | return {written, true}; | 303 | 0 | } | 304 | | | 305 | | /* Fall back to worst possible case (should be very rare for HTTP) */ | 306 | | /* At least we can reserve room for next chunk if we know it up front */ | 307 | 104k | if (nextLength) { | 308 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); | 309 | 0 | } | 310 | | | 311 | | /* Buffer this chunk */ | 312 | 104k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); | 313 | | | 314 | | /* Return the failure */ | 315 | 104k | return {length, true}; | 316 | 104k | } | 317 | | /* Fall through to default return */ | 318 | 107k | } | 319 | 2.68M | } | 320 | | | 321 | | /* Default fall through return */ | 322 | 3.01M | return {length, false}; | 323 | 3.12M | } |
uWS::AsyncSocket<false>::write(char const*, int, bool, int) Line | Count | Source | 236 | 10.2M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { | 237 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ | 238 | 10.2M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { | 239 | 1.00M | return {length, false}; | 240 | 1.00M | } | 241 | | | 242 | 9.28M | LoopData *loopData = getLoopData(); | 243 | 9.28M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); | 244 | | | 245 | | /* We are limited if we have a per-socket buffer */ | 246 | 9.28M | if (asyncSocketData->buffer.length()) { | 247 | | /* Write off as much as we can */ | 248 | 695k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); | 249 | | | 250 | | /* On failure return, otherwise continue down the function */ | 251 | 695k | if ((unsigned int) written < asyncSocketData->buffer.length()) { | 252 | | | 253 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ | 254 | 674k | asyncSocketData->buffer.erase((unsigned int) written); | 255 | | | 256 | 674k | if (optionally) { | 257 | | /* Thankfully we can exit early here */ | 258 | 18.4k | return {0, true}; | 259 | 656k | } else { | 260 | | /* This path is horrible and points towards erroneous usage */ | 261 | 656k | asyncSocketData->buffer.append(src, (unsigned int) length); | 262 | | | 263 | 656k | return {length, true}; | 264 | 656k | } | 265 | 674k | } | 266 | | | 267 | | /* At this point we simply have no buffer and can continue as normal */ | 268 | 20.8k | asyncSocketData->buffer.clear(); | 269 | 20.8k | } | 270 | | | 271 | 8.60M | if (length) { | 272 | 7.73M | if (loopData->corkedSocket == this) { | 273 | | /* We are corked */ | 274 | 7.43M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { | 275 | | /* If the entire chunk fits in cork buffer */ | 276 | 7.43M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); | 277 | 7.43M | loopData->corkOffset += (unsigned int) length; | 278 | | /* Fall through to default return */ | 279 | 7.43M | } else { | 280 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ | 281 | | if constexpr (false) { | 282 | | /* Cork up as much as we can */ | 283 | | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; | 284 | | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); | 285 | | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; | 286 | | | 287 | | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); | 288 | | return {written + (int) stripped, failed}; | 289 | | } | 290 | | | 291 | | /* For non-SSL we take the penalty of two syscalls */ | 292 | 0 | return uncork(src, length, optionally); | 293 | 0 | } | 294 | 7.43M | } else { | 295 | | /* We are not corked */ | 296 | 308k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); | 297 | | | 298 | | /* Did we fail? */ | 299 | 308k | if (written < length) { | 300 | | /* If the write was optional then just bail out */ | 301 | 280k | if (optionally) { | 302 | 0 | return {written, true}; | 303 | 0 | } | 304 | | | 305 | | /* Fall back to worst possible case (should be very rare for HTTP) */ | 306 | | /* At least we can reserve room for next chunk if we know it up front */ | 307 | 280k | if (nextLength) { | 308 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); | 309 | 0 | } | 310 | | | 311 | | /* Buffer this chunk */ | 312 | 280k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); | 313 | | | 314 | | /* Return the failure */ | 315 | 280k | return {length, true}; | 316 | 280k | } | 317 | | /* Fall through to default return */ | 318 | 308k | } | 319 | 7.73M | } | 320 | | | 321 | | /* Default fall through return */ | 322 | 8.32M | return {length, false}; | 323 | 8.60M | } |
|
324 | | |
325 | | /* Uncork this socket and flush or buffer any corked and/or passed data. It is essential to remember doing this. */ |
326 | | /* It does NOT count bytes written from cork buffer (they are already accounted for in the write call responsible for its corking)! */ |
327 | 3.33M | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { |
328 | 3.33M | LoopData *loopData = getLoopData(); |
329 | | |
330 | 3.33M | if (loopData->corkedSocket == this) { |
331 | 3.33M | loopData->corkedSocket = nullptr; |
332 | | |
333 | 3.33M | if (loopData->corkOffset) { |
334 | | /* Corked data is already accounted for via its write call */ |
335 | 443k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); |
336 | 443k | loopData->corkOffset = 0; |
337 | | |
338 | 443k | if (failed) { |
339 | | /* We do not need to care for buffering here, write does that */ |
340 | 382k | return {0, true}; |
341 | 382k | } |
342 | 443k | } |
343 | | |
344 | | /* We should only return with new writes, not things written to cork already */ |
345 | 2.94M | return write(src, length, optionally, 0); |
346 | 3.33M | } else { |
347 | | /* We are not even corked! */ |
348 | 1.68k | return {0, false}; |
349 | 1.68k | } |
350 | 3.33M | } uWS::AsyncSocket<true>::uncork(char const*, int, bool) Line | Count | Source | 327 | 1.07M | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { | 328 | 1.07M | LoopData *loopData = getLoopData(); | 329 | | | 330 | 1.07M | if (loopData->corkedSocket == this) { | 331 | 1.07M | loopData->corkedSocket = nullptr; | 332 | | | 333 | 1.07M | if (loopData->corkOffset) { | 334 | | /* Corked data is already accounted for via its write call */ | 335 | 116k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); | 336 | 116k | loopData->corkOffset = 0; | 337 | | | 338 | 116k | if (failed) { | 339 | | /* We do not need to care for buffering here, write does that */ | 340 | 104k | return {0, true}; | 341 | 104k | } | 342 | 116k | } | 343 | | | 344 | | /* We should only return with new writes, not things written to cork already */ | 345 | 974k | return write(src, length, optionally, 0); | 346 | 1.07M | } else { | 347 | | /* We are not even corked! */ | 348 | 952 | return {0, false}; | 349 | 952 | } | 350 | 1.07M | } |
uWS::AsyncSocket<false>::uncork(char const*, int, bool) Line | Count | Source | 327 | 2.25M | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { | 328 | 2.25M | LoopData *loopData = getLoopData(); | 329 | | | 330 | 2.25M | if (loopData->corkedSocket == this) { | 331 | 2.25M | loopData->corkedSocket = nullptr; | 332 | | | 333 | 2.25M | if (loopData->corkOffset) { | 334 | | /* Corked data is already accounted for via its write call */ | 335 | 327k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); | 336 | 327k | loopData->corkOffset = 0; | 337 | | | 338 | 327k | if (failed) { | 339 | | /* We do not need to care for buffering here, write does that */ | 340 | 278k | return {0, true}; | 341 | 278k | } | 342 | 327k | } | 343 | | | 344 | | /* We should only return with new writes, not things written to cork already */ | 345 | 1.97M | return write(src, length, optionally, 0); | 346 | 2.25M | } else { | 347 | | /* We are not even corked! */ | 348 | 731 | return {0, false}; | 349 | 731 | } | 350 | 2.25M | } |
|
351 | | }; |
352 | | |
353 | | } |
354 | | |
355 | | #endif // UWS_ASYNCSOCKET_H |