opal-up 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +66 -51
  3. data/ext/up_ext/App.h +665 -544
  4. data/ext/up_ext/AsyncSocket.h +307 -284
  5. data/ext/up_ext/AsyncSocketData.h +35 -51
  6. data/ext/up_ext/BloomFilter.h +37 -42
  7. data/ext/up_ext/ChunkedEncoding.h +174 -175
  8. data/ext/up_ext/ClientApp.h +20 -23
  9. data/ext/up_ext/HttpContext.h +476 -381
  10. data/ext/up_ext/HttpContextData.h +20 -20
  11. data/ext/up_ext/HttpErrors.h +14 -10
  12. data/ext/up_ext/HttpParser.h +631 -563
  13. data/ext/up_ext/HttpResponse.h +526 -460
  14. data/ext/up_ext/HttpResponseData.h +59 -55
  15. data/ext/up_ext/HttpRouter.h +328 -310
  16. data/ext/up_ext/Loop.h +174 -168
  17. data/ext/up_ext/LoopData.h +60 -67
  18. data/ext/up_ext/MoveOnlyFunction.h +71 -80
  19. data/ext/up_ext/PerMessageDeflate.h +218 -198
  20. data/ext/up_ext/ProxyParser.h +100 -99
  21. data/ext/up_ext/QueryParser.h +91 -84
  22. data/ext/up_ext/TopicTree.h +273 -268
  23. data/ext/up_ext/Utilities.h +25 -25
  24. data/ext/up_ext/WebSocket.h +376 -310
  25. data/ext/up_ext/WebSocketContext.h +487 -372
  26. data/ext/up_ext/WebSocketContextData.h +74 -62
  27. data/ext/up_ext/WebSocketData.h +53 -46
  28. data/ext/up_ext/WebSocketExtensions.h +194 -178
  29. data/ext/up_ext/WebSocketHandshake.h +115 -110
  30. data/ext/up_ext/WebSocketProtocol.h +441 -398
  31. data/ext/up_ext/extconf.rb +1 -1
  32. data/ext/up_ext/libuwebsockets.cpp +1262 -1292
  33. data/ext/up_ext/libuwebsockets.h +337 -201
  34. data/ext/up_ext/up_ext.c +853 -163
  35. data/lib/up/bun/rack_env.rb +1 -13
  36. data/lib/up/bun/server.rb +93 -19
  37. data/lib/up/cli.rb +3 -0
  38. data/lib/up/client.rb +68 -0
  39. data/lib/up/ruby/cluster.rb +62 -0
  40. data/lib/up/ruby/rack_cluster.rb +1 -1
  41. data/lib/up/ruby/rack_server.rb +0 -1
  42. data/lib/up/u_web_socket/cluster.rb +18 -3
  43. data/lib/up/u_web_socket/server.rb +108 -15
  44. data/lib/up/version.rb +1 -1
  45. metadata +4 -15
  46. data/bin/up_node +0 -12
  47. data/bin/up_node_cluster +0 -12
  48. data/lib/up/node/cluster.rb +0 -39
  49. data/lib/up/node/cluster_cli.rb +0 -15
  50. data/lib/up/node/rack_cluster.rb +0 -25
  51. data/lib/up/node/rack_env.rb +0 -106
  52. data/lib/up/node/rack_server.rb +0 -25
  53. data/lib/up/node/server.rb +0 -84
  54. data/lib/up/node/server_cli.rb +0 -15
  55. data/lib/up/ruby/rack_env.rb +0 -97
  56. data/lib/up/u_web_socket/rack_env.rb +0 -101
@@ -20,336 +20,359 @@
20
20
 
21
21
  /* This class implements async socket memory management strategies */
22
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). */
23
+ /* NOTE: Many unsigned/signed conversion warnings could be solved by moving from
24
+ * int length to unsigned length for everything to/from uSockets - this would
25
+ * however remove the opportunity to signal error with -1 (which is how the
26
+ * entire UNIX syscalling is built). */
26
27
 
27
28
  #include <cstring>
28
29
  #include <iostream>
29
30
 
30
31
  #include "libusockets.h"
31
32
 
32
- #include "LoopData.h"
33
33
  #include "AsyncSocketData.h"
34
+ #include "LoopData.h"
34
35
 
35
36
  namespace uWS {
36
37
 
37
- enum SendBufferAttribute {
38
- NEEDS_NOTHING,
39
- NEEDS_DRAIN,
40
- NEEDS_UNCORK
41
- };
38
+ enum SendBufferAttribute { NEEDS_NOTHING, NEEDS_DRAIN, NEEDS_UNCORK };
42
39
 
43
- template <bool, bool, typename> struct WebSocketContext;
40
+ template <bool, bool, typename> struct WebSocketContext;
44
41
 
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;
42
+ template <bool SSL> struct AsyncSocket {
43
+ /* This guy is promiscuous */
44
+ template <bool> friend struct HttpContext;
45
+ template <bool, bool, typename> friend struct WebSocketContext;
46
+ template <bool> friend struct TemplatedApp;
47
+ template <bool, typename> friend struct WebSocketContextData;
48
+ template <typename, typename> friend struct TopicTree;
49
+ template <bool> friend struct HttpResponse;
54
50
 
55
51
  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
- }
52
+ /* Helper, do not use directly (todo: move to uSockets or de-crazify) */
53
+ void throttle_helper(int toggle) {
54
+ /* These should be exposed by uSockets */
55
+ static thread_local int us_events[2] = {0, 0};
56
+
57
+ struct us_poll_t *p = (struct us_poll_t *)this;
58
+ struct us_loop_t *loop = us_socket_context_loop(
59
+ SSL, us_socket_context(SSL, (us_socket_t *)this));
60
+
61
+ if (toggle) {
62
+ /* Pause */
63
+ int events = us_poll_events(p);
64
+ if (events) {
65
+ us_events[getBufferedAmount() ? 1 : 0] = events;
66
+ }
67
+ us_poll_change(p, loop, 0);
68
+ } else {
69
+ /* Resume */
70
+ int events = us_events[getBufferedAmount() ? 1 : 0];
71
+ us_poll_change(p, loop, events);
76
72
  }
73
+ }
77
74
 
78
75
  protected:
79
- /* Returns SSL pointer or FD as pointer */
80
- void *getNativeHandle() {
81
- return us_socket_get_native_handle(SSL, (us_socket_t *) this);
76
+ /* Returns SSL pointer or FD as pointer */
77
+ void *getNativeHandle() {
78
+ return us_socket_get_native_handle(SSL, (us_socket_t *)this);
79
+ }
80
+
81
+ /* Get loop data for socket */
82
+ LoopData *getLoopData() {
83
+ return (LoopData *)us_loop_ext(us_socket_context_loop(
84
+ SSL, us_socket_context(SSL, (us_socket_t *)this)));
85
+ }
86
+
87
+ /* Get socket extension */
88
+ AsyncSocketData<SSL> *getAsyncSocketData() {
89
+ return (AsyncSocketData<SSL> *)us_socket_ext(SSL, (us_socket_t *)this);
90
+ }
91
+
92
+ /* Socket timeout */
93
+ void timeout(unsigned int seconds) {
94
+ us_socket_timeout(SSL, (us_socket_t *)this, seconds);
95
+ }
96
+
97
+ /* Shutdown socket without any automatic drainage */
98
+ void shutdown() { us_socket_shutdown(SSL, (us_socket_t *)this); }
99
+
100
+ /* Experimental pause */
101
+ us_socket_t *pause() {
102
+ throttle_helper(1);
103
+ return (us_socket_t *)this;
104
+ }
105
+
106
+ /* Experimental resume */
107
+ us_socket_t *resume() {
108
+ throttle_helper(0);
109
+ return (us_socket_t *)this;
110
+ }
111
+
112
+ /* Immediately close socket */
113
+ us_socket_t *close() {
114
+ return us_socket_close(SSL, (us_socket_t *)this, 0, nullptr);
115
+ }
116
+
117
+ void corkUnchecked() {
118
+ /* What if another socket is corked? */
119
+ getLoopData()->corkedSocket = this;
120
+ }
121
+
122
+ void uncorkWithoutSending() {
123
+ if (isCorked()) {
124
+ getLoopData()->corkedSocket = nullptr;
82
125
  }
83
-
84
- /* Get loop data for socket */
85
- LoopData *getLoopData() {
86
- return (LoopData *) us_loop_ext(us_socket_context_loop(SSL, us_socket_context(SSL, (us_socket_t *) this)));
126
+ }
127
+
128
+ /* Cork this socket. Only one socket may ever be corked per-loop at any given
129
+ * time */
130
+ void cork() {
131
+ /* Extra check for invalid corking of others */
132
+ if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) {
133
+ std::cerr
134
+ << "Error: Cork buffer must not be acquired without checking canCork!"
135
+ << std::endl;
136
+ std::terminate();
87
137
  }
88
138
 
89
- /* Get socket extension */
90
- AsyncSocketData<SSL> *getAsyncSocketData() {
91
- return (AsyncSocketData<SSL> *) us_socket_ext(SSL, (us_socket_t *) this);
139
+ /* What if another socket is corked? */
140
+ getLoopData()->corkedSocket = this;
141
+ }
142
+
143
+ /* Returns wheter we are corked or not */
144
+ bool isCorked() { return getLoopData()->corkedSocket == this; }
145
+
146
+ /* Returns whether we could cork (it is free) */
147
+ bool canCork() { return getLoopData()->corkedSocket == nullptr; }
148
+
149
+ /* Returns a suitable buffer for temporary assemblation of send data */
150
+ std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) {
151
+ /* First step is to determine if we already have backpressure or not */
152
+ LoopData *loopData = getLoopData();
153
+ BackPressure &backPressure = getAsyncSocketData()->buffer;
154
+ size_t existingBackpressure = backPressure.length();
155
+ if ((!existingBackpressure) && (isCorked() || canCork()) &&
156
+ (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) {
157
+ /* Cork automatically if we can */
158
+ if (isCorked()) {
159
+ char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
160
+ loopData->corkOffset += (unsigned int)size;
161
+ return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING};
162
+ } else {
163
+ cork();
164
+ char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
165
+ loopData->corkOffset += (unsigned int)size;
166
+ return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK};
167
+ }
168
+ } else {
169
+
170
+ /* If we are corked and there is already data in the cork buffer,
171
+ mark how much is ours and reset it */
172
+ unsigned int ourCorkOffset = 0;
173
+ if (isCorked() && loopData->corkOffset) {
174
+ ourCorkOffset = loopData->corkOffset;
175
+ loopData->corkOffset = 0;
176
+ }
177
+
178
+ /* Fallback is to use the backpressure as buffer */
179
+ backPressure.resize(ourCorkOffset + existingBackpressure + size);
180
+
181
+ /* And copy corkbuffer in front */
182
+ memcpy((char *)backPressure.data() + existingBackpressure,
183
+ loopData->corkBuffer, ourCorkOffset);
184
+
185
+ return {(char *)backPressure.data() + ourCorkOffset +
186
+ existingBackpressure,
187
+ SendBufferAttribute::NEEDS_DRAIN};
92
188
  }
93
-
94
- /* Socket timeout */
95
- void timeout(unsigned int seconds) {
96
- us_socket_timeout(SSL, (us_socket_t *) this, seconds);
189
+ }
190
+
191
+ /* Returns the user space backpressure. */
192
+ unsigned int getBufferedAmount() {
193
+ /* We return the actual amount of bytes in backbuffer, including
194
+ * pendingRemoval */
195
+ return (unsigned int)getAsyncSocketData()->buffer.totalLength();
196
+ }
197
+
198
+ /* Returns the text representation of an IPv4 or IPv6 address */
199
+ std::string_view addressAsText(std::string_view binary) {
200
+ static thread_local char buf[64];
201
+ int ipLength = 0;
202
+
203
+ if (!binary.length()) {
204
+ return {};
97
205
  }
98
206
 
99
- /* Shutdown socket without any automatic drainage */
100
- void shutdown() {
101
- us_socket_shutdown(SSL, (us_socket_t *) this);
102
- }
207
+ unsigned char *b = (unsigned char *)binary.data();
103
208
 
104
- /* Experimental pause */
105
- us_socket_t *pause() {
106
- throttle_helper(1);
107
- return (us_socket_t *) this;
209
+ if (binary.length() == 4) {
210
+ ipLength = snprintf(buf, 64, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
211
+ } else {
212
+ ipLength = snprintf(buf, 64,
213
+ "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%"
214
+ "02x:%02x%02x:%02x%02x",
215
+ b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8],
216
+ b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
108
217
  }
109
218
 
110
- /* Experimental resume */
111
- us_socket_t *resume() {
112
- throttle_helper(0);
113
- return (us_socket_t *) this;
219
+ return {buf, (unsigned int)ipLength};
220
+ }
221
+
222
+ /* Returns the remote IP address or empty string on failure */
223
+ std::string_view getRemoteAddress() {
224
+ static thread_local char buf[16];
225
+ int ipLength = 16;
226
+ us_socket_remote_address(SSL, (us_socket_t *)this, buf, &ipLength);
227
+ return std::string_view(buf, (unsigned int)ipLength);
228
+ }
229
+
230
+ /* Returns the text representation of IP */
231
+ std::string_view getRemoteAddressAsText() {
232
+ return addressAsText(getRemoteAddress());
233
+ }
234
+
235
+ /* Write in three levels of prioritization: cork-buffer, syscall,
236
+ * socket-buffer. Always drain if possible. Returns pair of bytes written
237
+ * (anywhere) and wheter or not this call resulted in the polling for writable
238
+ * (or we are in a state that implies polling for writable). */
239
+ std::pair<int, bool> write(const char *src, int length,
240
+ bool optionally = false, int nextLength = 0) {
241
+ /* Fake success if closed, simple fix to allow uncork of closed socket to
242
+ * succeed */
243
+ if (us_socket_is_closed(SSL, (us_socket_t *)this)) {
244
+ return {length, false};
114
245
  }
115
246
 
116
- /* Immediately close socket */
117
- us_socket_t *close() {
118
- return us_socket_close(SSL, (us_socket_t *) this, 0, nullptr);
119
- }
247
+ LoopData *loopData = getLoopData();
248
+ AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData();
120
249
 
121
- void corkUnchecked() {
122
- /* What if another socket is corked? */
123
- getLoopData()->corkedSocket = this;
124
- }
250
+ /* We are limited if we have a per-socket buffer */
251
+ if (asyncSocketData->buffer.length()) {
252
+ /* Write off as much as we can */
253
+ int written = us_socket_write(
254
+ SSL, (us_socket_t *)this, asyncSocketData->buffer.data(),
255
+ (int)asyncSocketData->buffer.length(), /*nextLength != 0 | */ length);
125
256
 
126
- void uncorkWithoutSending() {
127
- if (isCorked()) {
128
- getLoopData()->corkedSocket = nullptr;
129
- }
130
- }
257
+ /* On failure return, otherwise continue down the function */
258
+ if ((unsigned int)written < asyncSocketData->buffer.length()) {
131
259
 
132
- /* Cork this socket. Only one socket may ever be corked per-loop at any given time */
133
- void cork() {
134
- /* Extra check for invalid corking of others */
135
- if (getLoopData()->corkOffset && getLoopData()->corkedSocket != this) {
136
- std::cerr << "Error: Cork buffer must not be acquired without checking canCork!" << std::endl;
137
- std::terminate();
138
- }
260
+ /* Update buffering (todo: we can do better here if we keep track of
261
+ * what happens to this guy later on) */
262
+ asyncSocketData->buffer.erase((unsigned int)written);
139
263
 
140
- /* What if another socket is corked? */
141
- getLoopData()->corkedSocket = this;
142
- }
143
-
144
- /* Returns wheter we are corked or not */
145
- bool isCorked() {
146
- return getLoopData()->corkedSocket == this;
147
- }
148
-
149
- /* Returns whether we could cork (it is free) */
150
- bool canCork() {
151
- return getLoopData()->corkedSocket == nullptr;
152
- }
153
-
154
- /* Returns a suitable buffer for temporary assemblation of send data */
155
- std::pair<char *, SendBufferAttribute> getSendBuffer(size_t size) {
156
- /* First step is to determine if we already have backpressure or not */
157
- LoopData *loopData = getLoopData();
158
- BackPressure &backPressure = getAsyncSocketData()->buffer;
159
- size_t existingBackpressure = backPressure.length();
160
- if ((!existingBackpressure) && (isCorked() || canCork()) && (loopData->corkOffset + size < LoopData::CORK_BUFFER_SIZE)) {
161
- /* Cork automatically if we can */
162
- if (isCorked()) {
163
- char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
164
- loopData->corkOffset += (unsigned int) size;
165
- return {sendBuffer, SendBufferAttribute::NEEDS_NOTHING};
166
- } else {
167
- cork();
168
- char *sendBuffer = loopData->corkBuffer + loopData->corkOffset;
169
- loopData->corkOffset += (unsigned int) size;
170
- return {sendBuffer, SendBufferAttribute::NEEDS_UNCORK};
171
- }
264
+ if (optionally) {
265
+ /* Thankfully we can exit early here */
266
+ return {0, true};
172
267
  } else {
268
+ /* This path is horrible and points towards erroneous usage */
269
+ asyncSocketData->buffer.append(src, (unsigned int)length);
173
270
 
174
- /* If we are corked and there is already data in the cork buffer,
175
- mark how much is ours and reset it */
176
- unsigned int ourCorkOffset = 0;
177
- if (isCorked() && loopData->corkOffset) {
178
- ourCorkOffset = loopData->corkOffset;
179
- loopData->corkOffset = 0;
180
- }
181
-
182
- /* Fallback is to use the backpressure as buffer */
183
- backPressure.resize(ourCorkOffset + existingBackpressure + size);
184
-
185
- /* And copy corkbuffer in front */
186
- memcpy((char *) backPressure.data() + existingBackpressure, loopData->corkBuffer, ourCorkOffset);
187
-
188
- return {(char *) backPressure.data() + ourCorkOffset + existingBackpressure, SendBufferAttribute::NEEDS_DRAIN};
271
+ return {length, true};
189
272
  }
190
- }
273
+ }
191
274
 
192
- /* Returns the user space backpressure. */
193
- unsigned int getBufferedAmount() {
194
- /* We return the actual amount of bytes in backbuffer, including pendingRemoval */
195
- return (unsigned int) getAsyncSocketData()->buffer.totalLength();
275
+ /* At this point we simply have no buffer and can continue as normal */
276
+ asyncSocketData->buffer.clear();
196
277
  }
197
278
 
198
- /* Returns the text representation of an IPv4 or IPv6 address */
199
- std::string_view addressAsText(std::string_view binary) {
200
- static thread_local char buf[64];
201
- int ipLength = 0;
202
-
203
- if (!binary.length()) {
204
- return {};
205
- }
206
-
207
- unsigned char *b = (unsigned char *) binary.data();
208
-
209
- if (binary.length() == 4) {
210
- ipLength = snprintf(buf, 64, "%u.%u.%u.%u", b[0], b[1], b[2], b[3]);
279
+ if (length) {
280
+ if (loopData->corkedSocket == this) {
281
+ /* We are corked */
282
+ if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >=
283
+ (unsigned int)length) {
284
+ /* If the entire chunk fits in cork buffer */
285
+ memcpy(loopData->corkBuffer + loopData->corkOffset, src,
286
+ (unsigned int)length);
287
+ loopData->corkOffset += (unsigned int)length;
288
+ /* Fall through to default return */
211
289
  } else {
212
- ipLength = snprintf(buf, 64, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x",
213
- 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
- b[12], b[13], b[14], b[15]);
215
- }
216
-
217
- return {buf, (unsigned int) ipLength};
218
- }
219
-
220
- /* Returns the remote IP address or empty string on failure */
221
- std::string_view getRemoteAddress() {
222
- static thread_local char buf[16];
223
- int ipLength = 16;
224
- us_socket_remote_address(SSL, (us_socket_t *) this, buf, &ipLength);
225
- return std::string_view(buf, (unsigned int) ipLength);
226
- }
227
-
228
- /* Returns the text representation of IP */
229
- std::string_view getRemoteAddressAsText() {
230
- return addressAsText(getRemoteAddress());
231
- }
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 wheter or not this call resulted in the polling for
235
- * writable (or we are in a state that implies polling for writable). */
236
- 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
- if (us_socket_is_closed(SSL, (us_socket_t *) this)) {
239
- return {length, false};
240
- }
241
-
242
- LoopData *loopData = getLoopData();
243
- AsyncSocketData<SSL> *asyncSocketData = getAsyncSocketData();
244
-
245
- /* We are limited if we have a per-socket buffer */
246
- if (asyncSocketData->buffer.length()) {
247
- /* Write off as much as we can */
248
- 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
- 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
- asyncSocketData->buffer.erase((unsigned int) written);
255
-
256
- if (optionally) {
257
- /* Thankfully we can exit early here */
258
- return {0, true};
259
- } else {
260
- /* This path is horrible and points towards erroneous usage */
261
- asyncSocketData->buffer.append(src, (unsigned int) length);
262
-
263
- return {length, true};
264
- }
265
- }
266
-
267
- /* At this point we simply have no buffer and can continue as normal */
268
- asyncSocketData->buffer.clear();
290
+ /* Strategy differences between SSL and non-SSL regarding syscall
291
+ * minimizing */
292
+ if constexpr (false) {
293
+ /* Cork up as much as we can */
294
+ unsigned int stripped =
295
+ LoopData::CORK_BUFFER_SIZE - loopData->corkOffset;
296
+ memcpy(loopData->corkBuffer + loopData->corkOffset, src, stripped);
297
+ loopData->corkOffset = LoopData::CORK_BUFFER_SIZE;
298
+
299
+ auto [written, failed] =
300
+ uncork(src + stripped, length - (int)stripped, optionally);
301
+ return {written + (int)stripped, failed};
302
+ }
303
+
304
+ /* For non-SSL we take the penalty of two syscalls */
305
+ return uncork(src, length, optionally);
269
306
  }
270
-
271
- if (length) {
272
- if (loopData->corkedSocket == this) {
273
- /* We are corked */
274
- if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= (unsigned int) length) {
275
- /* If the entire chunk fits in cork buffer */
276
- memcpy(loopData->corkBuffer + loopData->corkOffset, src, (unsigned int) length);
277
- loopData->corkOffset += (unsigned int) length;
278
- /* Fall through to default return */
279
- } 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
- return uncork(src, length, optionally);
293
- }
294
- } else {
295
- /* We are not corked */
296
- int written = us_socket_write(SSL, (us_socket_t *) this, src, length, nextLength != 0);
297
-
298
- /* Did we fail? */
299
- if (written < length) {
300
- /* If the write was optional then just bail out */
301
- if (optionally) {
302
- return {written, true};
303
- }
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
- if (nextLength) {
308
- asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + (size_t) (length - written + nextLength));
309
- }
310
-
311
- /* Buffer this chunk */
312
- asyncSocketData->buffer.append(src + written, (size_t) (length - written));
313
-
314
- /* Return the failure */
315
- return {length, true};
316
- }
317
- /* Fall through to default return */
318
- }
307
+ } else {
308
+ /* We are not corked */
309
+ int written = us_socket_write(SSL, (us_socket_t *)this, src, length,
310
+ nextLength != 0);
311
+
312
+ /* Did we fail? */
313
+ if (written < length) {
314
+ /* If the write was optional then just bail out */
315
+ if (optionally) {
316
+ return {written, true};
317
+ }
318
+
319
+ /* Fall back to worst possible case (should be very rare for HTTP) */
320
+ /* At least we can reserve room for next chunk if we know it up front
321
+ */
322
+ if (nextLength) {
323
+ asyncSocketData->buffer.reserve(
324
+ asyncSocketData->buffer.length() +
325
+ (size_t)(length - written + nextLength));
326
+ }
327
+
328
+ /* Buffer this chunk */
329
+ asyncSocketData->buffer.append(src + written,
330
+ (size_t)(length - written));
331
+
332
+ /* Return the failure */
333
+ return {length, true};
319
334
  }
320
-
321
- /* Default fall through return */
322
- return {length, false};
335
+ /* Fall through to default return */
336
+ }
323
337
  }
324
338
 
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
- std::pair<int, bool> uncork(const char *src = nullptr, int length = 0, bool optionally = false) {
328
- LoopData *loopData = getLoopData();
329
-
330
- if (loopData->corkedSocket == this) {
331
- loopData->corkedSocket = nullptr;
332
-
333
- if (loopData->corkOffset) {
334
- /* Corked data is already accounted for via its write call */
335
- auto [written, failed] = write(loopData->corkBuffer, (int) loopData->corkOffset, false, length);
336
- loopData->corkOffset = 0;
337
-
338
- if (failed) {
339
- /* We do not need to care for buffering here, write does that */
340
- return {0, true};
341
- }
342
- }
343
-
344
- /* We should only return with new writes, not things written to cork already */
345
- return write(src, length, optionally, 0);
346
- } else {
347
- /* We are not even corked! */
348
- return {0, false};
339
+ /* Default fall through return */
340
+ return {length, false};
341
+ }
342
+
343
+ /* Uncork this socket and flush or buffer any corked and/or passed data. It is
344
+ * essential to remember doing this. */
345
+ /* It does NOT count bytes written from cork buffer (they are already
346
+ * accounted for in the write call responsible for its corking)! */
347
+ std::pair<int, bool> uncork(const char *src = nullptr, int length = 0,
348
+ bool optionally = false) {
349
+ LoopData *loopData = getLoopData();
350
+
351
+ if (loopData->corkedSocket == this) {
352
+ loopData->corkedSocket = nullptr;
353
+
354
+ if (loopData->corkOffset) {
355
+ /* Corked data is already accounted for via its write call */
356
+ auto [written, failed] = write(
357
+ loopData->corkBuffer, (int)loopData->corkOffset, false, length);
358
+ loopData->corkOffset = 0;
359
+
360
+ if (failed) {
361
+ /* We do not need to care for buffering here, write does that */
362
+ return {0, true};
349
363
  }
364
+ }
365
+
366
+ /* We should only return with new writes, not things written to cork
367
+ * already */
368
+ return write(src, length, optionally, 0);
369
+ } else {
370
+ /* We are not even corked! */
371
+ return {0, false};
350
372
  }
373
+ }
351
374
  };
352
375
 
353
- }
376
+ } // namespace uWS
354
377
 
355
378
  #endif // UWS_ASYNCSOCKET_H