/src/uWebSockets/src/AsyncSocket.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_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 | 34.7k | void *getNativeHandle() { |
81 | 34.7k | return us_socket_get_native_handle(SSL, (us_socket_t *) this); |
82 | 34.7k | } |
83 | | |
84 | | /* Get loop data for socket */ |
85 | 11.2M | LoopData *getLoopData() { |
86 | 11.2M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); |
87 | 11.2M | } uWS::AsyncSocket<false>::getLoopData() Line | Count | Source | 85 | 7.63M | LoopData *getLoopData() { | 86 | 7.63M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); | 87 | 7.63M | } |
uWS::AsyncSocket<true>::getLoopData() Line | Count | Source | 85 | 3.57M | LoopData *getLoopData() { | 86 | 3.57M | return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this))); | 87 | 3.57M | } |
|
88 | | |
89 | | /* Get socket extension */ |
90 | 9.41M | AsyncSocketData<SSL> *getAsyncSocketData() { |
91 | 9.41M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); |
92 | 9.41M | } uWS::AsyncSocket<false>::getAsyncSocketData() Line | Count | Source | 90 | 6.69M | AsyncSocketData<SSL> *getAsyncSocketData() { | 91 | 6.69M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); | 92 | 6.69M | } |
uWS::AsyncSocket<true>::getAsyncSocketData() Line | Count | Source | 90 | 2.71M | AsyncSocketData<SSL> *getAsyncSocketData() { | 91 | 2.71M | return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this); | 92 | 2.71M | } |
|
93 | | |
94 | | /* Socket timeout */ |
95 | 536k | void timeout(unsigned int seconds) { |
96 | 536k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); |
97 | 536k | } uWS::AsyncSocket<false>::timeout(unsigned int) Line | Count | Source | 95 | 383k | void timeout(unsigned int seconds) { | 96 | 383k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); | 97 | 383k | } |
uWS::AsyncSocket<true>::timeout(unsigned int) Line | Count | Source | 95 | 152k | void timeout(unsigned int seconds) { | 96 | 152k | us_socket_timeout(SSL, (us_socket_t *) this, seconds); | 97 | 152k | } |
|
98 | | |
99 | | /* Shutdown socket without any automatic drainage */ |
100 | 7.96k | void shutdown() { |
101 | 7.96k | us_socket_shutdown(SSL, (us_socket_t *) this); |
102 | 7.96k | } uWS::AsyncSocket<false>::shutdown() Line | Count | Source | 100 | 6.49k | void shutdown() { | 101 | 6.49k | us_socket_shutdown(SSL, (us_socket_t *) this); | 102 | 6.49k | } |
uWS::AsyncSocket<true>::shutdown() Line | Count | Source | 100 | 1.47k | void shutdown() { | 101 | 1.47k | us_socket_shutdown(SSL, (us_socket_t *) this); | 102 | 1.47k | } |
|
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 | 236k | us_socket_t *close() { |
118 | 236k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); |
119 | 236k | } uWS::AsyncSocket<false>::close() Line | Count | Source | 117 | 177k | us_socket_t *close() { | 118 | 177k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); | 119 | 177k | } |
uWS::AsyncSocket<true>::close() Line | Count | Source | 117 | 58.8k | us_socket_t *close() { | 118 | 58.8k | return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr); | 119 | 58.8k | } |
|
120 | | |
121 | 210k | void corkUnchecked() { |
122 | | /* What if another socket is corked? */ |
123 | 210k | getLoopData()->corkedSocket = this; |
124 | 210k | } uWS::AsyncSocket<false>::corkUnchecked() Line | Count | Source | 121 | 145k | void corkUnchecked() { | 122 | | /* What if another socket is corked? */ | 123 | 145k | getLoopData()->corkedSocket = this; | 124 | 145k | } |
uWS::AsyncSocket<true>::corkUnchecked() Line | Count | Source | 121 | 64.9k | void corkUnchecked() { | 122 | | /* What if another socket is corked? */ | 123 | 64.9k | getLoopData()->corkedSocket = this; | 124 | 64.9k | } |
|
125 | | |
126 | 0 | void uncorkWithoutSending() { |
127 | 0 | if (isCorked()) { |
128 | 0 | getLoopData()->corkedSocket = nullptr; |
129 | 0 | } |
130 | 0 | } Unexecuted instantiation: uWS::AsyncSocket<false>::uncorkWithoutSending() Unexecuted instantiation: uWS::AsyncSocket<true>::uncorkWithoutSending() |
131 | | |
132 | | /* Cork this socket. Only one socket may ever be corked per-loop at any given time */ |
133 | 1.23M | void cork() { |
134 | | /* Extra check for invalid corking of others */ |
135 | 1.23M | 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.23M | getLoopData()->corkedSocket = this; |
142 | 1.23M | } uWS::AsyncSocket<false>::cork() Line | Count | Source | 133 | 772k | void cork() { | 134 | | /* Extra check for invalid corking of others */ | 135 | 772k | 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 | 772k | getLoopData()->corkedSocket = this; | 142 | 772k | } |
uWS::AsyncSocket<true>::cork() Line | Count | Source | 133 | 464k | void cork() { | 134 | | /* Extra check for invalid corking of others */ | 135 | 464k | 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 | 464k | getLoopData()->corkedSocket = this; | 142 | 464k | } |
|
143 | | |
144 | | /* Returns the corked socket or nullptr */ |
145 | 13.0k | void *corkedSocket() { |
146 | 13.0k | return getLoopData()->corkedSocket; |
147 | 13.0k | } |
148 | | |
149 | | /* Returns wheter we are corked or not */ |
150 | 657k | bool isCorked() { |
151 | 657k | return getLoopData()->corkedSocket == this; |
152 | 657k | } uWS::AsyncSocket<false>::isCorked() Line | Count | Source | 150 | 439k | bool isCorked() { | 151 | 439k | return getLoopData()->corkedSocket == this; | 152 | 439k | } |
uWS::AsyncSocket<true>::isCorked() Line | Count | Source | 150 | 217k | bool isCorked() { | 151 | 217k | return getLoopData()->corkedSocket == this; | 152 | 217k | } |
|
153 | | |
154 | | /* Returns whether we could cork (it is free) */ |
155 | 30.3k | bool canCork() { |
156 | 30.3k | return getLoopData()->corkedSocket == nullptr; |
157 | 30.3k | } uWS::AsyncSocket<false>::canCork() Line | Count | Source | 155 | 18.8k | bool canCork() { | 156 | 18.8k | return getLoopData()->corkedSocket == nullptr; | 157 | 18.8k | } |
uWS::AsyncSocket<true>::canCork() Line | Count | Source | 155 | 11.5k | bool canCork() { | 156 | 11.5k | return getLoopData()->corkedSocket == nullptr; | 157 | 11.5k | } |
|
158 | | |
159 | | /* Returns a suitable buffer for temporary assemblation of send data */ |
160 | 143k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { |
161 | | /* First step is to determine if we already have backpressure or not */ |
162 | 143k | LoopData *loopData = getLoopData(); |
163 | 143k | BackPressure &backPressure = getAsyncSocketData()->buffer; |
164 | 143k | size_t existingBackpressure = backPressure.length(); |
165 | 143k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { |
166 | | /* Cork automatically if we can */ |
167 | 20.9k | if (isCorked()) { |
168 | 18.0k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; |
169 | 18.0k | loopData->corkOffset += (unsigned int) size; |
170 | 18.0k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; |
171 | 18.0k | } else { |
172 | 2.88k | cork(); |
173 | 2.88k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; |
174 | 2.88k | loopData->corkOffset += (unsigned int) size; |
175 | 2.88k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; |
176 | 2.88k | } |
177 | 122k | } else { |
178 | | |
179 | | /* If we are corked and there is already data in the cork buffer, |
180 | | mark how much is ours and reset it */ |
181 | 122k | unsigned int ourCorkOffset = 0; |
182 | 122k | if (isCorked() && loopData->corkOffset) { |
183 | 69 | ourCorkOffset = loopData->corkOffset; |
184 | 69 | loopData->corkOffset = 0; |
185 | 69 | } |
186 | | |
187 | | /* Fallback is to use the backpressure as buffer */ |
188 | 122k | backPressure.resize(ourCorkOffset + existingBackpressure + size); |
189 | | |
190 | | /* And copy corkbuffer in front */ |
191 | 122k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); |
192 | | |
193 | 122k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; |
194 | 122k | } |
195 | 143k | } uWS::AsyncSocket<false>::getSendBuffer(unsigned long) Line | Count | Source | 160 | 83.9k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { | 161 | | /* First step is to determine if we already have backpressure or not */ | 162 | 83.9k | LoopData *loopData = getLoopData(); | 163 | 83.9k | BackPressure &backPressure = getAsyncSocketData()->buffer; | 164 | 83.9k | size_t existingBackpressure = backPressure.length(); | 165 | 83.9k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { | 166 | | /* Cork automatically if we can */ | 167 | 9.64k | if (isCorked()) { | 168 | 8.40k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 169 | 8.40k | loopData->corkOffset += (unsigned int) size; | 170 | 8.40k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; | 171 | 8.40k | } else { | 172 | 1.23k | cork(); | 173 | 1.23k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 174 | 1.23k | loopData->corkOffset += (unsigned int) size; | 175 | 1.23k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; | 176 | 1.23k | } | 177 | 74.3k | } else { | 178 | | | 179 | | /* If we are corked and there is already data in the cork buffer, | 180 | | mark how much is ours and reset it */ | 181 | 74.3k | unsigned int ourCorkOffset = 0; | 182 | 74.3k | if (isCorked() && loopData->corkOffset) { | 183 | 69 | ourCorkOffset = loopData->corkOffset; | 184 | 69 | loopData->corkOffset = 0; | 185 | 69 | } | 186 | | | 187 | | /* Fallback is to use the backpressure as buffer */ | 188 | 74.3k | backPressure.resize(ourCorkOffset + existingBackpressure + size); | 189 | | | 190 | | /* And copy corkbuffer in front */ | 191 | 74.3k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); | 192 | | | 193 | 74.3k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; | 194 | 74.3k | } | 195 | 83.9k | } |
uWS::AsyncSocket<true>::getSendBuffer(unsigned long) Line | Count | Source | 160 | 59.0k | std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) { | 161 | | /* First step is to determine if we already have backpressure or not */ | 162 | 59.0k | LoopData *loopData = getLoopData(); | 163 | 59.0k | BackPressure &backPressure = getAsyncSocketData()->buffer; | 164 | 59.0k | size_t existingBackpressure = backPressure.length(); | 165 | 59.0k | if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) { | 166 | | /* Cork automatically if we can */ | 167 | 11.2k | if (isCorked()) { | 168 | 9.63k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 169 | 9.63k | loopData->corkOffset += (unsigned int) size; | 170 | 9.63k | return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING}; | 171 | 9.63k | } else { | 172 | 1.65k | cork(); | 173 | 1.65k | char *sendBuffer = loopData->corkBuffer + loopData->corkOffset; | 174 | 1.65k | loopData->corkOffset += (unsigned int) size; | 175 | 1.65k | return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK}; | 176 | 1.65k | } | 177 | 47.7k | } else { | 178 | | | 179 | | /* If we are corked and there is already data in the cork buffer, | 180 | | mark how much is ours and reset it */ | 181 | 47.7k | unsigned int ourCorkOffset = 0; | 182 | 47.7k | if (isCorked() && loopData->corkOffset) { | 183 | 0 | ourCorkOffset = loopData->corkOffset; | 184 | 0 | loopData->corkOffset = 0; | 185 | 0 | } | 186 | | | 187 | | /* Fallback is to use the backpressure as buffer */ | 188 | 47.7k | backPressure.resize(ourCorkOffset + existingBackpressure + size); | 189 | | | 190 | | /* And copy corkbuffer in front */ | 191 | 47.7k | memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset); | 192 | | | 193 | 47.7k | return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN}; | 194 | 47.7k | } | 195 | 59.0k | } |
|
196 | | |
197 | | /* Returns the user space backpressure. */ |
198 | 499k | unsigned int getBufferedAmount() { |
199 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ |
200 | 499k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); |
201 | 499k | } uWS::AsyncSocket<false>::getBufferedAmount() Line | Count | Source | 198 | 330k | unsigned int getBufferedAmount() { | 199 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ | 200 | 330k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); | 201 | 330k | } |
uWS::AsyncSocket<true>::getBufferedAmount() Line | Count | Source | 198 | 168k | unsigned int getBufferedAmount() { | 199 | | /* We return the actual amount of bytes in backbuffer, including pendingRemoval */ | 200 | 168k | return (unsigned int) getAsyncSocketData()->buffer.totalLength(); | 201 | 168k | } |
|
202 | | |
203 | | /* Returns the text representation of an IPv4 or IPv6 address */ |
204 | 34.7k | std::string_view addressAsText(std::string_view binary) { |
205 | 34.7k | static thread_local char buf[64]; |
206 | 34.7k | int ipLength = 0; |
207 | | |
208 | 34.7k | if (!binary.length()) { |
209 | 0 | return {}; |
210 | 0 | } |
211 | | |
212 | 34.7k | unsigned char *b = (unsigned char *) binary.data(); |
213 | | |
214 | 34.7k | if (binary.length() == 4) { |
215 | 12.7k | ipLength = sprintf(buf, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]); |
216 | 21.9k | } else { |
217 | 21.9k | ipLength = sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", |
218 | 21.9k | b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], |
219 | 21.9k | b[12], b[13], b[14], b[15]); |
220 | 21.9k | } |
221 | | |
222 | 34.7k | return {buf, (unsigned int) ipLength}; |
223 | 34.7k | } |
224 | | |
225 | | /* Returns the remote IP address or empty string on failure */ |
226 | 34.7k | std::string_view getRemoteAddress() { |
227 | 34.7k | static thread_local char buf[16]; |
228 | 34.7k | int ipLength = 16; |
229 | 34.7k | us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength); |
230 | 34.7k | return std::string_view(buf, (unsigned int) ipLength); |
231 | 34.7k | } |
232 | | |
233 | | /* Returns the text representation of IP */ |
234 | 34.7k | std::string_view getRemoteAddressAsText() { |
235 | 34.7k | return addressAsText(getRemoteAddress()); |
236 | 34.7k | } |
237 | | |
238 | | /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer. Always drain if possible. |
239 | | * Returns pair of bytes written (anywhere) and wheter or not this call resulted in the polling for |
240 | | * writable (or we are in a state that implies polling for writable). */ |
241 | 6.96M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { |
242 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ |
243 | 6.96M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { |
244 | 770k | return {length, false}; |
245 | 770k | } |
246 | | |
247 | 6.19M | LoopData *loopData = getLoopData(); |
248 | 6.19M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); |
249 | | |
250 | | /* We are limited if we have a per-socket buffer */ |
251 | 6.19M | if (asyncSocketData->buffer.length()) { |
252 | | /* Write off as much as we can */ |
253 | 359k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); |
254 | | |
255 | | /* On failure return, otherwise continue down the function */ |
256 | 359k | if ((unsigned int) written < asyncSocketData->buffer.length()) { |
257 | | |
258 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ |
259 | 341k | asyncSocketData->buffer.erase((unsigned int) written); |
260 | | |
261 | 341k | if (optionally) { |
262 | | /* Thankfully we can exit early here */ |
263 | 10.1k | return {0, true}; |
264 | 331k | } else { |
265 | | /* This path is horrible and points towards erroneous usage */ |
266 | 331k | asyncSocketData->buffer.append(src, (unsigned int) length); |
267 | | |
268 | 331k | return {length, true}; |
269 | 331k | } |
270 | 341k | } |
271 | | |
272 | | /* At this point we simply have no buffer and can continue as normal */ |
273 | 17.3k | asyncSocketData->buffer.clear(); |
274 | 17.3k | } |
275 | | |
276 | 5.85M | if (length) { |
277 | 5.70M | if (loopData->corkedSocket == this) { |
278 | | /* We are corked */ |
279 | 5.46M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { |
280 | | /* If the entire chunk fits in cork buffer */ |
281 | 5.46M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); |
282 | 5.46M | loopData->corkOffset += (unsigned int) length; |
283 | | /* Fall through to default return */ |
284 | 5.46M | } else { |
285 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ |
286 | 0 | if constexpr (SSL) { |
287 | | /* Cork up as much as we can */ |
288 | 0 | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; |
289 | 0 | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); |
290 | 0 | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; |
291 | |
|
292 | 0 | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); |
293 | 0 | return {written + (int) stripped, failed}; |
294 | 0 | } |
295 | | |
296 | | /* For non-SSL we take the penalty of two syscalls */ |
297 | 0 | return uncork(src, length, optionally); |
298 | 0 | } |
299 | 5.46M | } else { |
300 | | /* We are not corked */ |
301 | 236k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); |
302 | | |
303 | | /* Did we fail? */ |
304 | 236k | if (written < length) { |
305 | | /* If the write was optional then just bail out */ |
306 | 223k | if (optionally) { |
307 | 0 | return {written, true}; |
308 | 0 | } |
309 | | |
310 | | /* Fall back to worst possible case (should be very rare for HTTP) */ |
311 | | /* At least we can reserve room for next chunk if we know it up front */ |
312 | 223k | if (nextLength) { |
313 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); |
314 | 0 | } |
315 | | |
316 | | /* Buffer this chunk */ |
317 | 223k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); |
318 | | |
319 | | /* Return the failure */ |
320 | 223k | return {length, true}; |
321 | 223k | } |
322 | | /* Fall through to default return */ |
323 | 236k | } |
324 | 5.70M | } |
325 | | |
326 | | /* Default fall through return */ |
327 | 5.63M | return {length, false}; |
328 | 5.85M | } uWS::AsyncSocket<false>::write(char const*, int, bool, int) Line | Count | Source | 241 | 4.86M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { | 242 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ | 243 | 4.86M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { | 244 | 433k | return {length, false}; | 245 | 433k | } | 246 | | | 247 | 4.43M | LoopData *loopData = getLoopData(); | 248 | 4.43M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); | 249 | | | 250 | | /* We are limited if we have a per-socket buffer */ | 251 | 4.43M | if (asyncSocketData->buffer.length()) { | 252 | | /* Write off as much as we can */ | 253 | 257k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); | 254 | | | 255 | | /* On failure return, otherwise continue down the function */ | 256 | 257k | if ((unsigned int) written < asyncSocketData->buffer.length()) { | 257 | | | 258 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ | 259 | 244k | asyncSocketData->buffer.erase((unsigned int) written); | 260 | | | 261 | 244k | if (optionally) { | 262 | | /* Thankfully we can exit early here */ | 263 | 10.1k | return {0, true}; | 264 | 234k | } else { | 265 | | /* This path is horrible and points towards erroneous usage */ | 266 | 234k | asyncSocketData->buffer.append(src, (unsigned int) length); | 267 | | | 268 | 234k | return {length, true}; | 269 | 234k | } | 270 | 244k | } | 271 | | | 272 | | /* At this point we simply have no buffer and can continue as normal */ | 273 | 13.1k | asyncSocketData->buffer.clear(); | 274 | 13.1k | } | 275 | | | 276 | 4.18M | if (length) { | 277 | 4.06M | if (loopData->corkedSocket == this) { | 278 | | /* We are corked */ | 279 | 3.89M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { | 280 | | /* If the entire chunk fits in cork buffer */ | 281 | 3.89M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); | 282 | 3.89M | loopData->corkOffset += (unsigned int) length; | 283 | | /* Fall through to default return */ | 284 | 3.89M | } else { | 285 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ | 286 | 0 | if constexpr (SSL) { | 287 | | /* Cork up as much as we can */ | 288 | 0 | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; | 289 | 0 | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); | 290 | 0 | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; | 291 | |
| 292 | 0 | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); | 293 | 0 | return {written + (int) stripped, failed}; | 294 | 0 | } | 295 | | | 296 | | /* For non-SSL we take the penalty of two syscalls */ | 297 | 0 | return uncork(src, length, optionally); | 298 | 0 | } | 299 | 3.89M | } else { | 300 | | /* We are not corked */ | 301 | 168k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); | 302 | | | 303 | | /* Did we fail? */ | 304 | 168k | if (written < length) { | 305 | | /* If the write was optional then just bail out */ | 306 | 158k | if (optionally) { | 307 | 0 | return {written, true}; | 308 | 0 | } | 309 | | | 310 | | /* Fall back to worst possible case (should be very rare for HTTP) */ | 311 | | /* At least we can reserve room for next chunk if we know it up front */ | 312 | 158k | if (nextLength) { | 313 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); | 314 | 0 | } | 315 | | | 316 | | /* Buffer this chunk */ | 317 | 158k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); | 318 | | | 319 | | /* Return the failure */ | 320 | 158k | return {length, true}; | 321 | 158k | } | 322 | | /* Fall through to default return */ | 323 | 168k | } | 324 | 4.06M | } | 325 | | | 326 | | /* Default fall through return */ | 327 | 4.03M | return {length, false}; | 328 | 4.18M | } |
uWS::AsyncSocket<true>::write(char const*, int, bool, int) Line | Count | Source | 241 | 2.10M | std::pair<int, bool> write(const char *src, int length, bool optionally = false, int nextLength = 0) { | 242 | | /* Fake success if closed, simple fix to allow uncork of closed socket to succeed */ | 243 | 2.10M | if (us_socket_is_closed(SSL, (us_socket_t *) this)) { | 244 | 337k | return {length, false}; | 245 | 337k | } | 246 | | | 247 | 1.76M | LoopData *loopData = getLoopData(); | 248 | 1.76M | AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData(); | 249 | | | 250 | | /* We are limited if we have a per-socket buffer */ | 251 | 1.76M | if (asyncSocketData->buffer.length()) { | 252 | | /* Write off as much as we can */ | 253 | 101k | int written = us_socket_write(SSL, (us_socket_t *) this, asyncSocketData->buffer.data(), (int) asyncSocketData->buffer.length(), /*nextLength != 0 | */length); | 254 | | | 255 | | /* On failure return, otherwise continue down the function */ | 256 | 101k | if ((unsigned int) written < asyncSocketData->buffer.length()) { | 257 | | | 258 | | /* Update buffering (todo: we can do better here if we keep track of what happens to this guy later on) */ | 259 | 97.5k | asyncSocketData->buffer.erase((unsigned int) written); | 260 | | | 261 | 97.5k | if (optionally) { | 262 | | /* Thankfully we can exit early here */ | 263 | 0 | return {0, true}; | 264 | 97.5k | } else { | 265 | | /* This path is horrible and points towards erroneous usage */ | 266 | 97.5k | asyncSocketData->buffer.append(src, (unsigned int) length); | 267 | | | 268 | 97.5k | return {length, true}; | 269 | 97.5k | } | 270 | 97.5k | } | 271 | | | 272 | | /* At this point we simply have no buffer and can continue as normal */ | 273 | 4.22k | asyncSocketData->buffer.clear(); | 274 | 4.22k | } | 275 | | | 276 | 1.66M | if (length) { | 277 | 1.63M | if (loopData->corkedSocket == this) { | 278 | | /* We are corked */ | 279 | 1.56M | if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) { | 280 | | /* If the entire chunk fits in cork buffer */ | 281 | 1.56M | memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length); | 282 | 1.56M | loopData->corkOffset += (unsigned int) length; | 283 | | /* Fall through to default return */ | 284 | 1.56M | } else { | 285 | | /* Strategy differences between SSL and non-SSL regarding syscall minimizing */ | 286 | 0 | if constexpr (SSL) { | 287 | | /* Cork up as much as we can */ | 288 | 0 | unsigned int stripped = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset; | 289 | 0 | memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped); | 290 | 0 | loopData->corkOffset = LoopData::CORK_BUFFER_SIZE; | 291 | |
| 292 | 0 | auto [written, failed] = uncork(src + stripped, length - (int) stripped, optionally); | 293 | 0 | return {written + (int) stripped, failed}; | 294 | 0 | } | 295 | | | 296 | | /* For non-SSL we take the penalty of two syscalls */ | 297 | 0 | return uncork(src, length, optionally); | 298 | 0 | } | 299 | 1.56M | } else { | 300 | | /* We are not corked */ | 301 | 68.6k | int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0); | 302 | | | 303 | | /* Did we fail? */ | 304 | 68.6k | if (written < length) { | 305 | | /* If the write was optional then just bail out */ | 306 | 65.5k | if (optionally) { | 307 | 0 | return {written, true}; | 308 | 0 | } | 309 | | | 310 | | /* Fall back to worst possible case (should be very rare for HTTP) */ | 311 | | /* At least we can reserve room for next chunk if we know it up front */ | 312 | 65.5k | if (nextLength) { | 313 | 0 | asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength)); | 314 | 0 | } | 315 | | | 316 | | /* Buffer this chunk */ | 317 | 65.5k | asyncSocketData->buffer.append(src + written, (size_t) (length - written)); | 318 | | | 319 | | /* Return the failure */ | 320 | 65.5k | return {length, true}; | 321 | 65.5k | } | 322 | | /* Fall through to default return */ | 323 | 68.6k | } | 324 | 1.63M | } | 325 | | | 326 | | /* Default fall through return */ | 327 | 1.60M | return {length, false}; | 328 | 1.66M | } |
|
329 | | |
330 | | /* Uncork this socket and flush or buffer any corked and/or passed data. It is essential to remember doing this. */ |
331 | | /* It does NOT count bytes written from cork buffer (they are already accounted for in the write call responsible for its corking)! */ |
332 | 1.24M | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { |
333 | 1.24M | LoopData *loopData = getLoopData(); |
334 | | |
335 | 1.24M | if (loopData->corkedSocket == this) { |
336 | 1.23M | loopData->corkedSocket = nullptr; |
337 | | |
338 | 1.23M | if (loopData->corkOffset) { |
339 | | /* Corked data is already accounted for via its write call */ |
340 | 240k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); |
341 | 240k | loopData->corkOffset = 0; |
342 | | |
343 | 240k | if (failed) { |
344 | | /* We do not need to care for buffering here, write does that */ |
345 | 222k | return {0, true}; |
346 | 222k | } |
347 | 240k | } |
348 | | |
349 | | /* We should only return with new writes, not things written to cork already */ |
350 | 1.01M | return write(src, length, optionally, 0); |
351 | 1.23M | } else { |
352 | | /* We are not even corked! */ |
353 | 2.89k | return {0, false}; |
354 | 2.89k | } |
355 | 1.24M | } uWS::AsyncSocket<false>::uncork(char const*, int, bool) Line | Count | Source | 332 | 773k | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { | 333 | 773k | LoopData *loopData = getLoopData(); | 334 | | | 335 | 773k | if (loopData->corkedSocket == this) { | 336 | 772k | loopData->corkedSocket = nullptr; | 337 | | | 338 | 772k | if (loopData->corkOffset) { | 339 | | /* Corked data is already accounted for via its write call */ | 340 | 171k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); | 341 | 171k | loopData->corkOffset = 0; | 342 | | | 343 | 171k | if (failed) { | 344 | | /* We do not need to care for buffering here, write does that */ | 345 | 156k | return {0, true}; | 346 | 156k | } | 347 | 171k | } | 348 | | | 349 | | /* We should only return with new writes, not things written to cork already */ | 350 | 615k | return write(src, length, optionally, 0); | 351 | 772k | } else { | 352 | | /* We are not even corked! */ | 353 | 953 | return {0, false}; | 354 | 953 | } | 355 | 773k | } |
uWS::AsyncSocket<true>::uncork(char const*, int, bool) Line | Count | Source | 332 | 466k | std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) { | 333 | 466k | LoopData *loopData = getLoopData(); | 334 | | | 335 | 466k | if (loopData->corkedSocket == this) { | 336 | 464k | loopData->corkedSocket = nullptr; | 337 | | | 338 | 464k | if (loopData->corkOffset) { | 339 | | /* Corked data is already accounted for via its write call */ | 340 | 69.2k | auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length); | 341 | 69.2k | loopData->corkOffset = 0; | 342 | | | 343 | 69.2k | if (failed) { | 344 | | /* We do not need to care for buffering here, write does that */ | 345 | 65.5k | return {0, true}; | 346 | 65.5k | } | 347 | 69.2k | } | 348 | | | 349 | | /* We should only return with new writes, not things written to cork already */ | 350 | 399k | return write(src, length, optionally, 0); | 351 | 464k | } else { | 352 | | /* We are not even corked! */ | 353 | 1.94k | return {0, false}; | 354 | 1.94k | } | 355 | 466k | } |
|
356 | | }; |
357 | | |
358 | | } |
359 | | |
360 | | #endif // UWS_ASYNCSOCKET_H |