Coverage Report

Created: 2023-06-06 06:17

/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