Coverage Report

Created: 2025-12-05 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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