isomorfeus-iodine 0.7.44

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
  3. data/.gitignore +20 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +32 -0
  6. data/.yardopts +8 -0
  7. data/CHANGELOG.md +1038 -0
  8. data/Gemfile +11 -0
  9. data/LICENSE.txt +21 -0
  10. data/LIMITS.md +41 -0
  11. data/README.md +782 -0
  12. data/Rakefile +44 -0
  13. data/SPEC-PubSub-Draft.md +159 -0
  14. data/SPEC-WebSocket-Draft.md +239 -0
  15. data/bin/console +22 -0
  16. data/bin/info.md +353 -0
  17. data/bin/mustache_bench.rb +100 -0
  18. data/bin/poc/Gemfile.lock +23 -0
  19. data/bin/poc/README.md +37 -0
  20. data/bin/poc/config.ru +66 -0
  21. data/bin/poc/gemfile +1 -0
  22. data/bin/poc/www/index.html +57 -0
  23. data/examples/async_task.ru +92 -0
  24. data/examples/config.ru +56 -0
  25. data/examples/echo.ru +59 -0
  26. data/examples/hello.ru +29 -0
  27. data/examples/pubsub_engine.ru +81 -0
  28. data/examples/redis.ru +70 -0
  29. data/examples/shootout.ru +73 -0
  30. data/examples/sub-protocols.ru +90 -0
  31. data/examples/tcp_client.rb +66 -0
  32. data/examples/x-sendfile.ru +14 -0
  33. data/exe/iodine +277 -0
  34. data/ext/iodine/extconf.rb +109 -0
  35. data/ext/iodine/fio.c +11985 -0
  36. data/ext/iodine/fio.h +6373 -0
  37. data/ext/iodine/fio_cli.c +431 -0
  38. data/ext/iodine/fio_cli.h +189 -0
  39. data/ext/iodine/fio_json_parser.h +687 -0
  40. data/ext/iodine/fio_siphash.c +157 -0
  41. data/ext/iodine/fio_siphash.h +37 -0
  42. data/ext/iodine/fio_tls.h +129 -0
  43. data/ext/iodine/fio_tls_missing.c +649 -0
  44. data/ext/iodine/fio_tls_openssl.c +1056 -0
  45. data/ext/iodine/fio_tmpfile.h +50 -0
  46. data/ext/iodine/fiobj.h +44 -0
  47. data/ext/iodine/fiobj4fio.h +21 -0
  48. data/ext/iodine/fiobj_ary.c +333 -0
  49. data/ext/iodine/fiobj_ary.h +139 -0
  50. data/ext/iodine/fiobj_data.c +1185 -0
  51. data/ext/iodine/fiobj_data.h +167 -0
  52. data/ext/iodine/fiobj_hash.c +409 -0
  53. data/ext/iodine/fiobj_hash.h +176 -0
  54. data/ext/iodine/fiobj_json.c +622 -0
  55. data/ext/iodine/fiobj_json.h +68 -0
  56. data/ext/iodine/fiobj_mem.h +71 -0
  57. data/ext/iodine/fiobj_mustache.c +317 -0
  58. data/ext/iodine/fiobj_mustache.h +62 -0
  59. data/ext/iodine/fiobj_numbers.c +344 -0
  60. data/ext/iodine/fiobj_numbers.h +127 -0
  61. data/ext/iodine/fiobj_str.c +433 -0
  62. data/ext/iodine/fiobj_str.h +172 -0
  63. data/ext/iodine/fiobject.c +620 -0
  64. data/ext/iodine/fiobject.h +654 -0
  65. data/ext/iodine/hpack.h +1923 -0
  66. data/ext/iodine/http.c +2754 -0
  67. data/ext/iodine/http.h +1002 -0
  68. data/ext/iodine/http1.c +912 -0
  69. data/ext/iodine/http1.h +29 -0
  70. data/ext/iodine/http1_parser.h +873 -0
  71. data/ext/iodine/http_internal.c +1278 -0
  72. data/ext/iodine/http_internal.h +237 -0
  73. data/ext/iodine/http_mime_parser.h +350 -0
  74. data/ext/iodine/iodine.c +1430 -0
  75. data/ext/iodine/iodine.h +63 -0
  76. data/ext/iodine/iodine_caller.c +218 -0
  77. data/ext/iodine/iodine_caller.h +27 -0
  78. data/ext/iodine/iodine_connection.c +933 -0
  79. data/ext/iodine/iodine_connection.h +55 -0
  80. data/ext/iodine/iodine_defer.c +420 -0
  81. data/ext/iodine/iodine_defer.h +6 -0
  82. data/ext/iodine/iodine_fiobj2rb.h +120 -0
  83. data/ext/iodine/iodine_helpers.c +282 -0
  84. data/ext/iodine/iodine_helpers.h +12 -0
  85. data/ext/iodine/iodine_http.c +1171 -0
  86. data/ext/iodine/iodine_http.h +23 -0
  87. data/ext/iodine/iodine_json.c +302 -0
  88. data/ext/iodine/iodine_json.h +6 -0
  89. data/ext/iodine/iodine_mustache.c +567 -0
  90. data/ext/iodine/iodine_mustache.h +6 -0
  91. data/ext/iodine/iodine_pubsub.c +580 -0
  92. data/ext/iodine/iodine_pubsub.h +26 -0
  93. data/ext/iodine/iodine_rack_io.c +281 -0
  94. data/ext/iodine/iodine_rack_io.h +20 -0
  95. data/ext/iodine/iodine_store.c +142 -0
  96. data/ext/iodine/iodine_store.h +20 -0
  97. data/ext/iodine/iodine_tcp.c +346 -0
  98. data/ext/iodine/iodine_tcp.h +13 -0
  99. data/ext/iodine/iodine_tls.c +261 -0
  100. data/ext/iodine/iodine_tls.h +13 -0
  101. data/ext/iodine/mustache_parser.h +1546 -0
  102. data/ext/iodine/redis_engine.c +957 -0
  103. data/ext/iodine/redis_engine.h +79 -0
  104. data/ext/iodine/resp_parser.h +317 -0
  105. data/ext/iodine/websocket_parser.h +505 -0
  106. data/ext/iodine/websockets.c +735 -0
  107. data/ext/iodine/websockets.h +185 -0
  108. data/isomorfeus-iodine.gemspec +42 -0
  109. data/lib/iodine/connection.rb +61 -0
  110. data/lib/iodine/json.rb +42 -0
  111. data/lib/iodine/mustache.rb +113 -0
  112. data/lib/iodine/pubsub.rb +55 -0
  113. data/lib/iodine/rack_utils.rb +43 -0
  114. data/lib/iodine/tls.rb +16 -0
  115. data/lib/iodine/version.rb +3 -0
  116. data/lib/iodine.rb +274 -0
  117. data/lib/rack/handler/iodine.rb +33 -0
  118. data/logo.png +0 -0
  119. metadata +271 -0
@@ -0,0 +1,505 @@
1
+ /*
2
+ copyright: Boaz Segev, 2017-2019
3
+ license: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license specified.
6
+ */
7
+ #ifndef H_WEBSOCKET_PARSER_H
8
+ /**\file
9
+
10
+ A single file WebSocket message parser and WebSocket message wrapper, decoupled
11
+ from any IO layer.
12
+
13
+ Notice that this header file library includes static funnction declerations that
14
+ must be implemented by the including file (the callbacks).
15
+
16
+ */
17
+ #define H_WEBSOCKET_PARSER_H
18
+ #include <stdint.h>
19
+ #include <stdlib.h>
20
+ #include <string.h>
21
+ #if DEBUG
22
+ #include <stdio.h>
23
+ #endif
24
+ /* *****************************************************************************
25
+ API - Message Wrapping
26
+ ***************************************************************************** */
27
+
28
+ /** returns the length of the buffer required to wrap a message `len` long */
29
+ static inline __attribute__((unused)) uint64_t
30
+ websocket_wrapped_len(uint64_t len);
31
+
32
+ /**
33
+ * Wraps a WebSocket server message and writes it to the target buffer.
34
+ *
35
+ * The `first` and `last` flags can be used to support message fragmentation.
36
+ *
37
+ * * target: the target buffer to write to.
38
+ * * msg: the message to be wrapped.
39
+ * * len: the message length.
40
+ * * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
41
+ * * first: set to 1 if `msg` points the beginning of the message.
42
+ * * last: set to 1 if `msg + len` ends the message.
43
+ * * client: set to 1 to use client mode (data masking).
44
+ *
45
+ * Further opcode values:
46
+ * * %x0 denotes a continuation frame
47
+ * * %x1 denotes a text frame
48
+ * * %x2 denotes a binary frame
49
+ * * %x3-7 are reserved for further non-control frames
50
+ * * %x8 denotes a connection close
51
+ * * %x9 denotes a ping
52
+ * * %xA denotes a pong
53
+ * * %xB-F are reserved for further control frames
54
+ *
55
+ * Returns the number of bytes written. Always `websocket_wrapped_len(len)`
56
+ */
57
+ inline static uint64_t __attribute__((unused))
58
+ websocket_server_wrap(void *target, void *msg, uint64_t len,
59
+ unsigned char opcode, unsigned char first,
60
+ unsigned char last, unsigned char rsv);
61
+
62
+ /**
63
+ * Wraps a WebSocket client message and writes it to the target buffer.
64
+ *
65
+ * The `first` and `last` flags can be used to support message fragmentation.
66
+ *
67
+ * * target: the target buffer to write to.
68
+ * * msg: the message to be wrapped.
69
+ * * len: the message length.
70
+ * * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
71
+ * * first: set to 1 if `msg` points the beginning of the message.
72
+ * * last: set to 1 if `msg + len` ends the message.
73
+ * * client: set to 1 to use client mode (data masking).
74
+ *
75
+ * Returns the number of bytes written. Always `websocket_wrapped_len(len) + 4`
76
+ */
77
+ inline static __attribute__((unused)) uint64_t
78
+ websocket_client_wrap(void *target, void *msg, uint64_t len,
79
+ unsigned char opcode, unsigned char first,
80
+ unsigned char last, unsigned char rsv);
81
+
82
+ /* *****************************************************************************
83
+ Callbacks - Required functions that must be inplemented to use this header
84
+ ***************************************************************************** */
85
+
86
+ static void websocket_on_unwrapped(void *udata, void *msg, uint64_t len,
87
+ char first, char last, char text,
88
+ unsigned char rsv);
89
+ static void websocket_on_protocol_ping(void *udata, void *msg, uint64_t len);
90
+ static void websocket_on_protocol_pong(void *udata, void *msg, uint64_t len);
91
+ static void websocket_on_protocol_close(void *udata);
92
+ static void websocket_on_protocol_error(void *udata);
93
+
94
+ /* *****************************************************************************
95
+ API - Parsing (unwrapping)
96
+ ***************************************************************************** */
97
+
98
+ /** the returned value for `websocket_buffer_required` */
99
+ struct websocket_packet_info_s {
100
+ /** the expected packet length */
101
+ uint64_t packet_length;
102
+ /** the packet's "head" size (before the data) */
103
+ uint8_t head_length;
104
+ /** a flag indicating if the packet is masked */
105
+ uint8_t masked;
106
+ };
107
+
108
+ /**
109
+ * Returns all known information regarding the upcoming message.
110
+ *
111
+ * @returns a struct websocket_packet_info_s.
112
+ *
113
+ * On protocol error, the `head_length` value is 0 (no valid head detected).
114
+ */
115
+ inline static struct websocket_packet_info_s
116
+ websocket_buffer_peek(void *buffer, uint64_t len);
117
+
118
+ /**
119
+ * Consumes the data in the buffer, calling any callbacks required.
120
+ *
121
+ * Returns the remaining data in the existing buffer (can be 0).
122
+ *
123
+ * Notice: if there's any data in the buffer that can't be parsed
124
+ * just yet, `memmove` is used to place the data at the beginning of the buffer.
125
+ */
126
+ inline static __attribute__((unused)) uint64_t
127
+ websocket_consume(void *buffer, uint64_t len, void *udata,
128
+ uint8_t require_masking);
129
+
130
+ /* *****************************************************************************
131
+ API - Internal Helpers
132
+ ***************************************************************************** */
133
+
134
+ /** used internally to mask and unmask client messages. */
135
+ inline static void websocket_xmask(void *msg, uint64_t len, uint32_t mask);
136
+
137
+ /* *****************************************************************************
138
+
139
+ Implementation
140
+
141
+ ***************************************************************************** */
142
+
143
+ /* *****************************************************************************
144
+ Message masking
145
+ ***************************************************************************** */
146
+ /** used internally to mask and unmask client messages. */
147
+ void websocket_xmask(void *msg, uint64_t len, uint32_t mask) {
148
+ if (len > 7) {
149
+ { /* XOR any unaligned memory (4 byte alignment) */
150
+ const uintptr_t offset = 4 - ((uintptr_t)msg & 3);
151
+ switch (offset) {
152
+ case 3:
153
+ ((uint8_t *)msg)[2] ^= ((uint8_t *)(&mask))[2];
154
+ /* fallthrough */
155
+ case 2:
156
+ ((uint8_t *)msg)[1] ^= ((uint8_t *)(&mask))[1];
157
+ /* fallthrough */
158
+ case 1:
159
+ ((uint8_t *)msg)[0] ^= ((uint8_t *)(&mask))[0];
160
+ /* rotate mask and move pointer to first 4 byte alignment */
161
+ uint64_t comb = mask | ((uint64_t)mask << 32);
162
+ ((uint8_t *)(&mask))[0] = ((uint8_t *)(&comb))[0 + offset];
163
+ ((uint8_t *)(&mask))[1] = ((uint8_t *)(&comb))[1 + offset];
164
+ ((uint8_t *)(&mask))[2] = ((uint8_t *)(&comb))[2 + offset];
165
+ ((uint8_t *)(&mask))[3] = ((uint8_t *)(&comb))[3 + offset];
166
+ msg = (void *)((uintptr_t)msg + offset);
167
+ len -= offset;
168
+ }
169
+ }
170
+ #if UINTPTR_MAX <= 0xFFFFFFFF
171
+ /* handle 4 byte XOR alignment in 32 bit mnachine*/
172
+ while (len >= 4) {
173
+ *((uint32_t *)msg) ^= mask;
174
+ len -= 4;
175
+ msg = (void *)((uintptr_t)msg + 4);
176
+ }
177
+ #else
178
+ /* handle first 4 byte XOR alignment and move on to 64 bits */
179
+ if ((uintptr_t)msg & 7) {
180
+ *((uint32_t *)msg) ^= mask;
181
+ len -= 4;
182
+ msg = (void *)((uintptr_t)msg + 4);
183
+ }
184
+ /* intrinsic / XOR by 8 byte block, memory aligned */
185
+ const uint64_t xmask = (((uint64_t)mask) << 32) | mask;
186
+ while (len >= 8) {
187
+ *((uint64_t *)msg) ^= xmask;
188
+ len -= 8;
189
+ msg = (void *)((uintptr_t)msg + 8);
190
+ }
191
+ #endif
192
+ }
193
+
194
+ /* XOR any leftover bytes (might be non aligned) */
195
+ switch (len) {
196
+ case 7:
197
+ ((uint8_t *)msg)[6] ^= ((uint8_t *)(&mask))[2];
198
+ /* fallthrough */
199
+ case 6:
200
+ ((uint8_t *)msg)[5] ^= ((uint8_t *)(&mask))[1];
201
+ /* fallthrough */
202
+ case 5:
203
+ ((uint8_t *)msg)[4] ^= ((uint8_t *)(&mask))[0];
204
+ /* fallthrough */
205
+ case 4:
206
+ ((uint8_t *)msg)[3] ^= ((uint8_t *)(&mask))[3];
207
+ /* fallthrough */
208
+ case 3:
209
+ ((uint8_t *)msg)[2] ^= ((uint8_t *)(&mask))[2];
210
+ /* fallthrough */
211
+ case 2:
212
+ ((uint8_t *)msg)[1] ^= ((uint8_t *)(&mask))[1];
213
+ /* fallthrough */
214
+ case 1:
215
+ ((uint8_t *)msg)[0] ^= ((uint8_t *)(&mask))[0];
216
+ /* fallthrough */
217
+ }
218
+ }
219
+
220
+ /* *****************************************************************************
221
+ Message wrapping
222
+ ***************************************************************************** */
223
+
224
+ /** Converts an unaligned network ordered byte stream to a 16 bit number. */
225
+ #define websocket_str2u16(c) \
226
+ ((uint16_t)(((uint16_t)(((uint8_t *)(c))[0]) << 8) | \
227
+ (uint16_t)(((uint8_t *)(c))[1])))
228
+
229
+ /** Converts an unaligned network ordered byte stream to a 64 bit number. */
230
+ #define websocket_str2u64(c) \
231
+ ((uint64_t)((((uint64_t)((uint8_t *)(c))[0]) << 56) | \
232
+ (((uint64_t)((uint8_t *)(c))[1]) << 48) | \
233
+ (((uint64_t)((uint8_t *)(c))[2]) << 40) | \
234
+ (((uint64_t)((uint8_t *)(c))[3]) << 32) | \
235
+ (((uint64_t)((uint8_t *)(c))[4]) << 24) | \
236
+ (((uint64_t)((uint8_t *)(c))[5]) << 16) | \
237
+ (((uint64_t)((uint8_t *)(c))[6]) << 8) | (((uint8_t *)(c))[7])))
238
+
239
+ /** Writes a local 16 bit number to an unaligned buffer in network order. */
240
+ #define websocket_u2str16(buffer, i) \
241
+ do { \
242
+ ((uint8_t *)(buffer))[0] = ((uint16_t)(i) >> 8) & 0xFF; \
243
+ ((uint8_t *)(buffer))[1] = ((uint16_t)(i)) & 0xFF; \
244
+ } while (0);
245
+
246
+ /** Writes a local 64 bit number to an unaligned buffer in network order. */
247
+ #define websocket_u2str64(buffer, i) \
248
+ do { \
249
+ ((uint8_t *)(buffer))[0] = ((uint64_t)(i) >> 56) & 0xFF; \
250
+ ((uint8_t *)(buffer))[1] = ((uint64_t)(i) >> 48) & 0xFF; \
251
+ ((uint8_t *)(buffer))[2] = ((uint64_t)(i) >> 40) & 0xFF; \
252
+ ((uint8_t *)(buffer))[3] = ((uint64_t)(i) >> 32) & 0xFF; \
253
+ ((uint8_t *)(buffer))[4] = ((uint64_t)(i) >> 24) & 0xFF; \
254
+ ((uint8_t *)(buffer))[5] = ((uint64_t)(i) >> 16) & 0xFF; \
255
+ ((uint8_t *)(buffer))[6] = ((uint64_t)(i) >> 8) & 0xFF; \
256
+ ((uint8_t *)(buffer))[7] = ((uint64_t)(i)) & 0xFF; \
257
+ } while (0);
258
+
259
+ /** returns the length of the buffer required to wrap a message `len` long */
260
+ static inline uint64_t websocket_wrapped_len(uint64_t len) {
261
+ if (len < 126)
262
+ return len + 2;
263
+ if (len < (1UL << 16))
264
+ return len + 4;
265
+ return len + 10;
266
+ }
267
+
268
+ /**
269
+ * Wraps a WebSocket server message and writes it to the target buffer.
270
+ *
271
+ * The `first` and `last` flags can be used to support message fragmentation.
272
+ *
273
+ * * target: the target buffer to write to.
274
+ * * msg: the message to be wrapped.
275
+ * * len: the message length.
276
+ * * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
277
+ * * first: set to 1 if `msg` points the beginning of the message.
278
+ * * last: set to 1 if `msg + len` ends the message.
279
+ * * client: set to 1 to use client mode (data masking).
280
+ *
281
+ * Further opcode values:
282
+ * * %x0 denotes a continuation frame
283
+ * * %x1 denotes a text frame
284
+ * * %x2 denotes a binary frame
285
+ * * %x3-7 are reserved for further non-control frames
286
+ * * %x8 denotes a connection close
287
+ * * %x9 denotes a ping
288
+ * * %xA denotes a pong
289
+ * * %xB-F are reserved for further control frames
290
+ *
291
+ * Returns the number of bytes written. Always `websocket_wrapped_len(len)`
292
+ */
293
+ static uint64_t websocket_server_wrap(void *target, void *msg, uint64_t len,
294
+ unsigned char opcode, unsigned char first,
295
+ unsigned char last, unsigned char rsv) {
296
+ ((uint8_t *)target)[0] = 0 |
297
+ /* opcode */ (((first ? opcode : 0) & 15)) |
298
+ /* rsv */ ((rsv & 7) << 4) |
299
+ /*fin*/ ((last & 1) << 7);
300
+ if (len < 126) {
301
+ ((uint8_t *)target)[1] = len;
302
+ memcpy(((uint8_t *)target) + 2, msg, len);
303
+ return len + 2;
304
+ } else if (len < (1UL << 16)) {
305
+ /* head is 4 bytes */
306
+ ((uint8_t *)target)[1] = 126;
307
+ websocket_u2str16(((uint8_t *)target + 2), len);
308
+ memcpy((uint8_t *)target + 4, msg, len);
309
+ return len + 4;
310
+ }
311
+ /* Really Long Message */
312
+ ((uint8_t *)target)[1] = 127;
313
+ websocket_u2str64(((uint8_t *)target + 2), len);
314
+ memcpy((uint8_t *)target + 10, msg, len);
315
+ return len + 10;
316
+ }
317
+
318
+ /**
319
+ * Wraps a WebSocket client message and writes it to the target buffer.
320
+ *
321
+ * The `first` and `last` flags can be used to support message fragmentation.
322
+ *
323
+ * * target: the target buffer to write to.
324
+ * * msg: the message to be wrapped.
325
+ * * len: the message length.
326
+ * * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
327
+ * * first: set to 1 if `msg` points the beginning of the message.
328
+ * * last: set to 1 if `msg + len` ends the message.
329
+ *
330
+ * Returns the number of bytes written. Always `websocket_wrapped_len(len) +
331
+ * 4`
332
+ */
333
+ static uint64_t websocket_client_wrap(void *target, void *msg, uint64_t len,
334
+ unsigned char opcode, unsigned char first,
335
+ unsigned char last, unsigned char rsv) {
336
+ uint32_t mask = rand() | 0x01020408;
337
+ ((uint8_t *)target)[0] = 0 |
338
+ /* opcode */ (((first ? opcode : 0) & 15)) |
339
+ /* rsv */ ((rsv & 7) << 4) |
340
+ /*fin*/ ((last & 1) << 7);
341
+ if (len < 126) {
342
+ ((uint8_t *)target)[1] = len | 128;
343
+ ((uint8_t *)target)[2] = ((uint8_t *)(&mask))[0];
344
+ ((uint8_t *)target)[3] = ((uint8_t *)(&mask))[1];
345
+ ((uint8_t *)target)[4] = ((uint8_t *)(&mask))[2];
346
+ ((uint8_t *)target)[5] = ((uint8_t *)(&mask))[3];
347
+ memcpy(((uint8_t *)target) + 6, msg, len);
348
+ websocket_xmask((uint8_t *)target + 6, len, mask);
349
+ return len + 6;
350
+ } else if (len < (1UL << 16)) {
351
+ /* head is 4 bytes */
352
+ ((uint8_t *)target)[1] = 126 | 128;
353
+ websocket_u2str16(((uint8_t *)target + 2), len);
354
+ ((uint8_t *)target)[4] = ((uint8_t *)(&mask))[0];
355
+ ((uint8_t *)target)[5] = ((uint8_t *)(&mask))[1];
356
+ ((uint8_t *)target)[6] = ((uint8_t *)(&mask))[2];
357
+ ((uint8_t *)target)[7] = ((uint8_t *)(&mask))[3];
358
+ memcpy((uint8_t *)target + 8, msg, len);
359
+ websocket_xmask((uint8_t *)target + 8, len, mask);
360
+ return len + 8;
361
+ }
362
+ /* Really Long Message */
363
+ ((uint8_t *)target)[1] = 255;
364
+ websocket_u2str64(((uint8_t *)target + 2), len);
365
+ ((uint8_t *)target)[10] = ((uint8_t *)(&mask))[0];
366
+ ((uint8_t *)target)[11] = ((uint8_t *)(&mask))[1];
367
+ ((uint8_t *)target)[12] = ((uint8_t *)(&mask))[2];
368
+ ((uint8_t *)target)[13] = ((uint8_t *)(&mask))[3];
369
+ memcpy((uint8_t *)target + 14, msg, len);
370
+ websocket_xmask((uint8_t *)target + 14, len, mask);
371
+ return len + 14;
372
+ }
373
+
374
+ /* *****************************************************************************
375
+ Message unwrapping
376
+ ***************************************************************************** */
377
+
378
+ /**
379
+ * Returns all known information regarding the upcoming message.
380
+ *
381
+ * @returns a struct websocket_packet_info_s.
382
+ *
383
+ * On protocol error, the `head_length` value is 0 (no valid head detected).
384
+ */
385
+ inline static struct websocket_packet_info_s
386
+ websocket_buffer_peek(void *buffer, uint64_t len) {
387
+ if (len < 2) {
388
+ const struct websocket_packet_info_s info = {0 /* packet */, 2 /* head */,
389
+ 0 /* masked? */};
390
+ return info;
391
+ }
392
+ const uint8_t mask_f = (((uint8_t *)buffer)[1] >> 7) & 1;
393
+ const uint8_t mask_l = (mask_f << 2);
394
+ uint8_t len_indicator = ((((uint8_t *)buffer)[1]) & 127);
395
+ switch (len_indicator) {
396
+ case 126:
397
+ if (len < 4)
398
+ return (struct websocket_packet_info_s){0, (uint8_t)(4 + mask_l), mask_f};
399
+ return (struct websocket_packet_info_s){
400
+ (uint64_t)websocket_str2u16(((uint8_t *)buffer + 2)),
401
+ (uint8_t)(4 + mask_l), mask_f};
402
+ case 127:
403
+ if (len < 10)
404
+ return (struct websocket_packet_info_s){0, (uint8_t)(10 + mask_l),
405
+ mask_f};
406
+ {
407
+ uint64_t msg_len = websocket_str2u64(((uint8_t *)buffer + 2));
408
+ if (msg_len >> 62)
409
+ return (struct websocket_packet_info_s){0, 0, 0};
410
+ return (struct websocket_packet_info_s){msg_len, (uint8_t)(10 + mask_l),
411
+ mask_f};
412
+ }
413
+ default:
414
+ return (struct websocket_packet_info_s){len_indicator,
415
+ (uint8_t)(2 + mask_l), mask_f};
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Consumes the data in the buffer, calling any callbacks required.
421
+ *
422
+ * Returns the remaining data in the existing buffer (can be 0).
423
+ */
424
+ static uint64_t websocket_consume(void *buffer, uint64_t len, void *udata,
425
+ uint8_t require_masking) {
426
+ volatile struct websocket_packet_info_s info =
427
+ websocket_buffer_peek(buffer, len);
428
+ if (!info.head_length) {
429
+ #if DEBUG
430
+ fprintf(stderr, "ERROR: WebSocket protocol error - malicious header.\n");
431
+ #endif
432
+ websocket_on_protocol_error(udata);
433
+ return 0;
434
+ }
435
+ if (info.head_length + info.packet_length > len)
436
+ return len;
437
+ uint64_t reminder = len;
438
+ uint8_t *pos = (uint8_t *)buffer;
439
+ while (info.head_length + info.packet_length <= reminder) {
440
+ /* parse head */
441
+ void *payload = (void *)(pos + info.head_length);
442
+ /* unmask? */
443
+ if (info.masked) {
444
+ /* masked */
445
+ uint32_t mask; // = ((uint32_t *)payload)[-1];
446
+ ((uint8_t *)(&mask))[0] = ((uint8_t *)(payload))[-4];
447
+ ((uint8_t *)(&mask))[1] = ((uint8_t *)(payload))[-3];
448
+ ((uint8_t *)(&mask))[2] = ((uint8_t *)(payload))[-2];
449
+ ((uint8_t *)(&mask))[3] = ((uint8_t *)(payload))[-1];
450
+ websocket_xmask(payload, info.packet_length, mask);
451
+ } else if (require_masking && info.packet_length) {
452
+ #if DEBUG
453
+ fprintf(stderr, "ERROR: WebSocket protocol error - unmasked data.\n");
454
+ #endif
455
+ websocket_on_protocol_error(udata);
456
+ }
457
+ /* call callback */
458
+ switch (pos[0] & 15) {
459
+ case 0:
460
+ /* continuation frame */
461
+ websocket_on_unwrapped(udata, payload, info.packet_length, 0,
462
+ ((pos[0] >> 7) & 1), 0, ((pos[0] >> 4) & 7));
463
+ break;
464
+ case 1:
465
+ /* text frame */
466
+ websocket_on_unwrapped(udata, payload, info.packet_length, 1,
467
+ ((pos[0] >> 7) & 1), 1, ((pos[0] >> 4) & 7));
468
+ break;
469
+ case 2:
470
+ /* data frame */
471
+ websocket_on_unwrapped(udata, payload, info.packet_length, 1,
472
+ ((pos[0] >> 7) & 1), 0, ((pos[0] >> 4) & 7));
473
+ break;
474
+ case 8:
475
+ /* close frame */
476
+ websocket_on_protocol_close(udata);
477
+ break;
478
+ case 9:
479
+ /* ping frame */
480
+ websocket_on_protocol_ping(udata, payload, info.packet_length);
481
+ break;
482
+ case 10:
483
+ /* pong frame */
484
+ websocket_on_protocol_pong(udata, payload, info.packet_length);
485
+ break;
486
+ default:
487
+ #if DEBUG
488
+ fprintf(stderr, "ERROR: WebSocket protocol error - unknown opcode %u\n",
489
+ (unsigned int)(pos[0] & 15));
490
+ #endif
491
+ websocket_on_protocol_error(udata);
492
+ }
493
+ /* step forward */
494
+ reminder = reminder - (info.head_length + info.packet_length);
495
+ if (!reminder)
496
+ return 0;
497
+ pos += info.head_length + info.packet_length;
498
+ info = websocket_buffer_peek(pos, reminder);
499
+ }
500
+ /* reset buffer state - support pipelining */
501
+ memmove(buffer, (uint8_t *)buffer + len - reminder, reminder);
502
+ return reminder;
503
+ }
504
+
505
+ #endif