opal-up 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +81 -28
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1374 -0
  43. data/ext/up_ext/libuwebsockets.h +260 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +278 -0
  48. data/lib/up/node/rack_env.rb +2 -2
  49. data/lib/up/ruby/cluster_cli.rb +10 -0
  50. data/lib/up/ruby/rack_cluster.rb +26 -0
  51. data/lib/up/ruby/rack_env.rb +97 -0
  52. data/lib/up/ruby/rack_server.rb +26 -0
  53. data/lib/up/ruby/server_cli.rb +10 -0
  54. data/lib/up/u_web_socket/rack_env.rb +1 -1
  55. data/lib/up/version.rb +1 -1
  56. metadata +71 -18
  57. data/.gitignore +0 -5
  58. data/Gemfile +0 -2
  59. data/example_rack_app/Gemfile +0 -3
  60. data/example_rack_app/config.ru +0 -6
  61. data/example_rack_app/rack_app.rb +0 -5
  62. data/example_roda_app/Gemfile +0 -6
  63. data/example_roda_app/config.ru +0 -6
  64. data/example_roda_app/roda_app.rb +0 -37
  65. data/example_sinatra_app/Gemfile +0 -6
  66. data/example_sinatra_app/config.ru +0 -6
  67. data/example_sinatra_app/sinatra_app.rb +0 -7
  68. data/opal-up.gemspec +0 -27
  69. data/up_logo.svg +0 -256
@@ -0,0 +1,506 @@
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_WEBSOCKETPROTOCOL_H
19
+ #define UWS_WEBSOCKETPROTOCOL_H
20
+
21
+ #include "libusockets.h"
22
+
23
+ #include <cstdint>
24
+ #include <cstring>
25
+ #include <cstdlib>
26
+ #include <string_view>
27
+
28
+ namespace uWS {
29
+
30
+ /* We should not overcomplicate these */
31
+ const std::string_view ERR_TOO_BIG_MESSAGE("Received too big message");
32
+ const std::string_view ERR_WEBSOCKET_TIMEOUT("WebSocket timed out from inactivity");
33
+ const std::string_view ERR_INVALID_TEXT("Received invalid UTF-8");
34
+ const std::string_view ERR_TOO_BIG_MESSAGE_INFLATION("Received too big message, or other inflation error");
35
+ const std::string_view ERR_INVALID_CLOSE_PAYLOAD("Received invalid close payload");
36
+
37
+ enum OpCode : unsigned char {
38
+ CONTINUATION = 0,
39
+ TEXT = 1,
40
+ BINARY = 2,
41
+ CLOSE = 8,
42
+ PING = 9,
43
+ PONG = 10
44
+ };
45
+
46
+ enum {
47
+ CLIENT,
48
+ SERVER
49
+ };
50
+
51
+ // 24 bytes perfectly
52
+ template <bool isServer>
53
+ struct WebSocketState {
54
+ public:
55
+ static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
56
+ static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
57
+ static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
58
+
59
+ // 16 bytes
60
+ struct State {
61
+ unsigned int wantsHead : 1;
62
+ unsigned int spillLength : 4;
63
+ signed int opStack : 2; // -1, 0, 1
64
+ unsigned int lastFin : 1;
65
+
66
+ // 15 bytes
67
+ unsigned char spill[LONG_MESSAGE_HEADER - 1];
68
+ OpCode opCode[2];
69
+
70
+ State() {
71
+ wantsHead = true;
72
+ spillLength = 0;
73
+ opStack = -1;
74
+ lastFin = true;
75
+ }
76
+
77
+ } state;
78
+
79
+ // 8 bytes
80
+ unsigned int remainingBytes = 0;
81
+ char mask[isServer ? 4 : 1];
82
+ };
83
+
84
+ namespace protocol {
85
+
86
+ template <typename T>
87
+ T bit_cast(char *c) {
88
+ T val;
89
+ memcpy(&val, c, sizeof(T));
90
+ return val;
91
+ }
92
+
93
+ /* Byte swap for little-endian systems */
94
+ template <typename T>
95
+ T cond_byte_swap(T value) {
96
+ uint32_t endian_test = 1;
97
+ if (*((char *)&endian_test)) {
98
+ union {
99
+ T i;
100
+ uint8_t b[sizeof(T)];
101
+ } src = { value }, dst;
102
+
103
+ for (unsigned int i = 0; i < sizeof(value); i++) {
104
+ dst.b[i] = src.b[sizeof(value) - 1 - i];
105
+ }
106
+
107
+ return dst.i;
108
+ }
109
+ return value;
110
+ }
111
+
112
+ // Based on utf8_check.c by Markus Kuhn, 2005
113
+ // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
114
+ // Optimized for predominantly 7-bit content by Alex Hultman, 2016
115
+ // Licensed as Zlib, like the rest of this project
116
+ static bool isValidUtf8(unsigned char *s, size_t length)
117
+ {
118
+ for (unsigned char *e = s + length; s != e; ) {
119
+ if (s + 4 <= e) {
120
+ uint32_t tmp;
121
+ memcpy(&tmp, s, 4);
122
+ if ((tmp & 0x80808080) == 0) {
123
+ s += 4;
124
+ continue;
125
+ }
126
+ }
127
+
128
+ while (!(*s & 0x80)) {
129
+ if (++s == e) {
130
+ return true;
131
+ }
132
+ }
133
+
134
+ if ((s[0] & 0x60) == 0x40) {
135
+ if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) {
136
+ return false;
137
+ }
138
+ s += 2;
139
+ } else if ((s[0] & 0xf0) == 0xe0) {
140
+ if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
141
+ (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) {
142
+ return false;
143
+ }
144
+ s += 3;
145
+ } else if ((s[0] & 0xf8) == 0xf0) {
146
+ if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 ||
147
+ (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) {
148
+ return false;
149
+ }
150
+ s += 4;
151
+ } else {
152
+ return false;
153
+ }
154
+ }
155
+ return true;
156
+ }
157
+
158
+ struct CloseFrame {
159
+ uint16_t code;
160
+ char *message;
161
+ size_t length;
162
+ };
163
+
164
+ static inline CloseFrame parseClosePayload(char *src, size_t length) {
165
+ /* If we get no code or message, default to reporting 1005 no status code present */
166
+ CloseFrame cf = {1005, nullptr, 0};
167
+ if (length >= 2) {
168
+ memcpy(&cf.code, src, 2);
169
+ cf = {cond_byte_swap<uint16_t>(cf.code), src + 2, length - 2};
170
+ if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) ||
171
+ (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) {
172
+ /* Even though we got a WebSocket close frame, it in itself is abnormal */
173
+ return {1006, nullptr, 0};
174
+ }
175
+ }
176
+ return cf;
177
+ }
178
+
179
+ static inline size_t formatClosePayload(char *dst, uint16_t code, const char *message, size_t length) {
180
+ /* We could have more strict checks here, but never append code 0 or 1005 or 1006 */
181
+ if (code && code != 1005 && code != 1006) {
182
+ code = cond_byte_swap<uint16_t>(code);
183
+ memcpy(dst, &code, 2);
184
+ /* It is invalid to pass nullptr to memcpy, even though length is 0 */
185
+ if (message) {
186
+ memcpy(dst + 2, message, length);
187
+ }
188
+ return length + 2;
189
+ }
190
+ return 0;
191
+ }
192
+
193
+ static inline size_t messageFrameSize(size_t messageSize) {
194
+ if (messageSize < 126) {
195
+ return 2 + messageSize;
196
+ } else if (messageSize <= UINT16_MAX) {
197
+ return 4 + messageSize;
198
+ }
199
+ return 10 + messageSize;
200
+ }
201
+
202
+ enum {
203
+ SND_CONTINUATION = 1,
204
+ SND_NO_FIN = 2,
205
+ SND_COMPRESSED = 64
206
+ };
207
+
208
+ template <bool isServer>
209
+ static inline size_t formatMessage(char *dst, const char *src, size_t length, OpCode opCode, size_t reportedLength, bool compressed, bool fin) {
210
+ size_t messageLength;
211
+ size_t headerLength;
212
+ if (reportedLength < 126) {
213
+ headerLength = 2;
214
+ dst[1] = (char) reportedLength;
215
+ } else if (reportedLength <= UINT16_MAX) {
216
+ headerLength = 4;
217
+ dst[1] = 126;
218
+ uint16_t tmp = cond_byte_swap<uint16_t>((uint16_t) reportedLength);
219
+ memcpy(&dst[2], &tmp, sizeof(uint16_t));
220
+ } else {
221
+ headerLength = 10;
222
+ dst[1] = 127;
223
+ uint64_t tmp = cond_byte_swap<uint64_t>((uint64_t) reportedLength);
224
+ memcpy(&dst[2], &tmp, sizeof(uint64_t));
225
+ }
226
+
227
+ dst[0] = (char) ((fin ? 128 : 0) | ((compressed && opCode) ? SND_COMPRESSED : 0) | (char) opCode);
228
+
229
+ //printf("%d\n", (int)dst[0]);
230
+
231
+ char mask[4];
232
+ if (!isServer) {
233
+ dst[1] |= 0x80;
234
+ uint32_t random = (uint32_t) rand();
235
+ memcpy(mask, &random, 4);
236
+ memcpy(dst + headerLength, &random, 4);
237
+ headerLength += 4;
238
+ }
239
+
240
+ messageLength = headerLength + length;
241
+ memcpy(dst + headerLength, src, length);
242
+
243
+ if (!isServer) {
244
+
245
+ // overwrites up to 3 bytes outside of the given buffer!
246
+ //WebSocketProtocol<isServer>::unmaskInplace(dst + headerLength, dst + headerLength + length, mask);
247
+
248
+ // this is not optimal
249
+ char *start = dst + headerLength;
250
+ char *stop = start + length;
251
+ int i = 0;
252
+ while (start != stop) {
253
+ (*start++) ^= mask[i++ % 4];
254
+ }
255
+ }
256
+ return messageLength;
257
+ }
258
+
259
+ }
260
+
261
+ // essentially this is only a parser
262
+ template <const bool isServer, typename Impl>
263
+ struct WebSocketProtocol {
264
+ public:
265
+ static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
266
+ static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
267
+ static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
268
+
269
+ protected:
270
+ static inline bool isFin(char *frame) {return *((unsigned char *) frame) & 128;}
271
+ static inline unsigned char getOpCode(char *frame) {return *((unsigned char *) frame) & 15;}
272
+ static inline unsigned char payloadLength(char *frame) {return ((unsigned char *) frame)[1] & 127;}
273
+ static inline bool rsv23(char *frame) {return *((unsigned char *) frame) & 48;}
274
+ static inline bool rsv1(char *frame) {return *((unsigned char *) frame) & 64;}
275
+
276
+ template <int N>
277
+ static inline void UnrolledXor(char * __restrict data, char * __restrict mask) {
278
+ if constexpr (N != 1) {
279
+ UnrolledXor<N - 1>(data, mask);
280
+ }
281
+ data[N - 1] ^= mask[(N - 1) % 4];
282
+ }
283
+
284
+ template <int DESTINATION>
285
+ static inline void unmaskImprecise8(char *src, uint64_t mask, unsigned int length) {
286
+ for (unsigned int n = (length >> 3) + 1; n; n--) {
287
+ uint64_t loaded;
288
+ memcpy(&loaded, src, 8);
289
+ loaded ^= mask;
290
+ memcpy(src - DESTINATION, &loaded, 8);
291
+ src += 8;
292
+ }
293
+ }
294
+
295
+ /* DESTINATION = 6 makes this not SIMD, DESTINATION = 4 is with SIMD but we don't want that for short messages */
296
+ template <int DESTINATION>
297
+ static inline void unmaskImprecise4(char *src, uint32_t mask, unsigned int length) {
298
+ for (unsigned int n = (length >> 2) + 1; n; n--) {
299
+ uint32_t loaded;
300
+ memcpy(&loaded, src, 4);
301
+ loaded ^= mask;
302
+ memcpy(src - DESTINATION, &loaded, 4);
303
+ src += 4;
304
+ }
305
+ }
306
+
307
+ template <int HEADER_SIZE>
308
+ static inline void unmaskImpreciseCopyMask(char *src, unsigned int length) {
309
+ if constexpr (HEADER_SIZE != 6) {
310
+ char mask[8] = {src[-4], src[-3], src[-2], src[-1], src[-4], src[-3], src[-2], src[-1]};
311
+ uint64_t maskInt;
312
+ memcpy(&maskInt, mask, 8);
313
+ unmaskImprecise8<HEADER_SIZE>(src, maskInt, length);
314
+ } else {
315
+ char mask[4] = {src[-4], src[-3], src[-2], src[-1]};
316
+ uint32_t maskInt;
317
+ memcpy(&maskInt, mask, 4);
318
+ unmaskImprecise4<HEADER_SIZE>(src, maskInt, length);
319
+ }
320
+ }
321
+
322
+ static inline void rotateMask(unsigned int offset, char *mask) {
323
+ char originalMask[4] = {mask[0], mask[1], mask[2], mask[3]};
324
+ mask[(0 + offset) % 4] = originalMask[0];
325
+ mask[(1 + offset) % 4] = originalMask[1];
326
+ mask[(2 + offset) % 4] = originalMask[2];
327
+ mask[(3 + offset) % 4] = originalMask[3];
328
+ }
329
+
330
+ static inline void unmaskInplace(char *data, char *stop, char *mask) {
331
+ while (data < stop) {
332
+ *(data++) ^= mask[0];
333
+ *(data++) ^= mask[1];
334
+ *(data++) ^= mask[2];
335
+ *(data++) ^= mask[3];
336
+ }
337
+ }
338
+
339
+ template <unsigned int MESSAGE_HEADER, typename T>
340
+ static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState<isServer> *wState, void *user) {
341
+ if (getOpCode(src)) {
342
+ if (wState->state.opStack == 1 || (!wState->state.lastFin && getOpCode(src) < 2)) {
343
+ Impl::forceClose(wState, user);
344
+ return true;
345
+ }
346
+ wState->state.opCode[++wState->state.opStack] = (OpCode) getOpCode(src);
347
+ } else if (wState->state.opStack == -1) {
348
+ Impl::forceClose(wState, user);
349
+ return true;
350
+ }
351
+ wState->state.lastFin = isFin(src);
352
+
353
+ if (Impl::refusePayloadLength(payLength, wState, user)) {
354
+ Impl::forceClose(wState, user, ERR_TOO_BIG_MESSAGE);
355
+ return true;
356
+ }
357
+
358
+ if (payLength + MESSAGE_HEADER <= length) {
359
+ bool fin = isFin(src);
360
+ if (isServer) {
361
+ /* This guy can never be assumed to be perfectly aligned since we can get multiple messages in one read */
362
+ unmaskImpreciseCopyMask<MESSAGE_HEADER>(src + MESSAGE_HEADER, (unsigned int) payLength);
363
+ if (Impl::handleFragment(src, payLength, 0, wState->state.opCode[wState->state.opStack], fin, wState, user)) {
364
+ return true;
365
+ }
366
+ } else {
367
+ if (Impl::handleFragment(src + MESSAGE_HEADER, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState, user)) {
368
+ return true;
369
+ }
370
+ }
371
+
372
+ if (fin) {
373
+ wState->state.opStack--;
374
+ }
375
+
376
+ src += payLength + MESSAGE_HEADER;
377
+ length -= (unsigned int) (payLength + MESSAGE_HEADER);
378
+ wState->state.spillLength = 0;
379
+ return false;
380
+ } else {
381
+ wState->state.spillLength = 0;
382
+ wState->state.wantsHead = false;
383
+ wState->remainingBytes = (unsigned int) (payLength - length + MESSAGE_HEADER);
384
+ bool fin = isFin(src);
385
+ if constexpr (isServer) {
386
+ memcpy(wState->mask, src + MESSAGE_HEADER - 4, 4);
387
+ uint64_t mask;
388
+ memcpy(&mask, src + MESSAGE_HEADER - 4, 4);
389
+ memcpy(((char *)&mask) + 4, src + MESSAGE_HEADER - 4, 4);
390
+ unmaskImprecise8<0>(src + MESSAGE_HEADER, mask, length);
391
+ rotateMask(4 - (length - MESSAGE_HEADER) % 4, wState->mask);
392
+ }
393
+ Impl::handleFragment(src + MESSAGE_HEADER, length - MESSAGE_HEADER, wState->remainingBytes, wState->state.opCode[wState->state.opStack], fin, wState, user);
394
+ return true;
395
+ }
396
+ }
397
+
398
+ /* This one is nicely vectorized on both ARM64 and X64 - especially with -mavx */
399
+ static inline void unmaskAll(char * __restrict data, char * __restrict mask) {
400
+ for (int i = 0; i < LIBUS_RECV_BUFFER_LENGTH; i += 16) {
401
+ UnrolledXor<16>(data + i, mask);
402
+ }
403
+ }
404
+
405
+ static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState<isServer> *wState, void *user) {
406
+ if (wState->remainingBytes <= length) {
407
+ if (isServer) {
408
+ unsigned int n = wState->remainingBytes >> 2;
409
+ unmaskInplace(src, src + n * 4, wState->mask);
410
+ for (unsigned int i = 0, s = wState->remainingBytes % 4; i < s; i++) {
411
+ src[n * 4 + i] ^= wState->mask[i];
412
+ }
413
+ }
414
+
415
+ if (Impl::handleFragment(src, wState->remainingBytes, 0, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState, user)) {
416
+ return false;
417
+ }
418
+
419
+ if (wState->state.lastFin) {
420
+ wState->state.opStack--;
421
+ }
422
+
423
+ src += wState->remainingBytes;
424
+ length -= wState->remainingBytes;
425
+ wState->state.wantsHead = true;
426
+ return true;
427
+ } else {
428
+ if (isServer) {
429
+ /* No need to unmask if mask is 0 */
430
+ uint32_t nullmask = 0;
431
+ if (memcmp(wState->mask, &nullmask, sizeof(uint32_t))) {
432
+ if /*constexpr*/ (LIBUS_RECV_BUFFER_LENGTH == length) {
433
+ unmaskAll(src, wState->mask);
434
+ } else {
435
+ // Slow path
436
+ unmaskInplace(src, src + ((length >> 2) + 1) * 4, wState->mask);
437
+ }
438
+ }
439
+ }
440
+
441
+ wState->remainingBytes -= length;
442
+ if (Impl::handleFragment(src, length, wState->remainingBytes, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState, user)) {
443
+ return false;
444
+ }
445
+
446
+ if (isServer && length % 4) {
447
+ rotateMask(4 - (length % 4), wState->mask);
448
+ }
449
+ return false;
450
+ }
451
+ }
452
+
453
+ public:
454
+ WebSocketProtocol() {
455
+
456
+ }
457
+
458
+ static inline void consume(char *src, unsigned int length, WebSocketState<isServer> *wState, void *user) {
459
+ if (wState->state.spillLength) {
460
+ src -= wState->state.spillLength;
461
+ length += wState->state.spillLength;
462
+ memcpy(src, wState->state.spill, wState->state.spillLength);
463
+ }
464
+ if (wState->state.wantsHead) {
465
+ parseNext:
466
+ while (length >= SHORT_MESSAGE_HEADER) {
467
+
468
+ // invalid reserved bits / invalid opcodes / invalid control frames / set compressed frame
469
+ if ((rsv1(src) && !Impl::setCompressed(wState, user)) || rsv23(src) || (getOpCode(src) > 2 && getOpCode(src) < 8) ||
470
+ getOpCode(src) > 10 || (getOpCode(src) > 2 && (!isFin(src) || payloadLength(src) > 125))) {
471
+ Impl::forceClose(wState, user);
472
+ return;
473
+ }
474
+
475
+ if (payloadLength(src) < 126) {
476
+ if (consumeMessage<SHORT_MESSAGE_HEADER, uint8_t>(payloadLength(src), src, length, wState, user)) {
477
+ return;
478
+ }
479
+ } else if (payloadLength(src) == 126) {
480
+ if (length < MEDIUM_MESSAGE_HEADER) {
481
+ break;
482
+ } else if(consumeMessage<MEDIUM_MESSAGE_HEADER, uint16_t>(protocol::cond_byte_swap<uint16_t>(protocol::bit_cast<uint16_t>(src + 2)), src, length, wState, user)) {
483
+ return;
484
+ }
485
+ } else if (length < LONG_MESSAGE_HEADER) {
486
+ break;
487
+ } else if (consumeMessage<LONG_MESSAGE_HEADER, uint64_t>(protocol::cond_byte_swap<uint64_t>(protocol::bit_cast<uint64_t>(src + 2)), src, length, wState, user)) {
488
+ return;
489
+ }
490
+ }
491
+ if (length) {
492
+ memcpy(wState->state.spill, src, length);
493
+ wState->state.spillLength = length & 0xf;
494
+ }
495
+ } else if (consumeContinuation(src, length, wState, user)) {
496
+ goto parseNext;
497
+ }
498
+ }
499
+
500
+ static const int CONSUME_POST_PADDING = 4;
501
+ static const int CONSUME_PRE_PADDING = LONG_MESSAGE_HEADER - 1;
502
+ };
503
+
504
+ }
505
+
506
+ #endif // UWS_WEBSOCKETPROTOCOL_H