mwrap 2.3.0 → 3.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,670 @@
1
+ /*
2
+ * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase,
3
+ * Shigeo Mitsunari
4
+ *
5
+ * The software is licensed under either the MIT License (below) or the Perl
6
+ * license.
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to
10
+ * deal in the Software without restriction, including without limitation the
11
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12
+ * sell copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in
16
+ * all copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24
+ * IN THE SOFTWARE.
25
+ */
26
+
27
+ #include <assert.h>
28
+ #include <stddef.h>
29
+ #include <string.h>
30
+ #ifdef __SSE4_2__
31
+ #ifdef _MSC_VER
32
+ #include <nmmintrin.h>
33
+ #else
34
+ #include <x86intrin.h>
35
+ #endif
36
+ #endif
37
+ #include "picohttpparser.h"
38
+
39
+ #if __GNUC__ >= 3
40
+ #define likely(x) __builtin_expect(!!(x), 1)
41
+ #define unlikely(x) __builtin_expect(!!(x), 0)
42
+ #else
43
+ #define likely(x) (x)
44
+ #define unlikely(x) (x)
45
+ #endif
46
+
47
+ #ifdef _MSC_VER
48
+ #define ALIGNED(n) _declspec(align(n))
49
+ #else
50
+ #define ALIGNED(n) __attribute__((aligned(n)))
51
+ #endif
52
+
53
+ #define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u)
54
+
55
+ #define CHECK_EOF() \
56
+ if (buf == buf_end) { \
57
+ *ret = -2; \
58
+ return NULL; \
59
+ }
60
+
61
+ #define EXPECT_CHAR_NO_CHECK(ch) \
62
+ if (*buf++ != ch) { \
63
+ *ret = -1; \
64
+ return NULL; \
65
+ }
66
+
67
+ #define EXPECT_CHAR(ch) \
68
+ CHECK_EOF(); \
69
+ EXPECT_CHAR_NO_CHECK(ch);
70
+
71
+ #define ADVANCE_TOKEN(tok, toklen) \
72
+ do { \
73
+ const char *tok_start = buf; \
74
+ static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75
+ int found2; \
76
+ buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77
+ if (!found2) { \
78
+ CHECK_EOF(); \
79
+ } \
80
+ while (1) { \
81
+ if (*buf == ' ') { \
82
+ break; \
83
+ } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84
+ if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85
+ *ret = -1; \
86
+ return NULL; \
87
+ } \
88
+ } \
89
+ ++buf; \
90
+ CHECK_EOF(); \
91
+ } \
92
+ tok = tok_start; \
93
+ toklen = buf - tok_start; \
94
+ } while (0)
95
+
96
+ static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
97
+ "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
98
+ "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
99
+ "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
100
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
101
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
104
+
105
+ static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found)
106
+ {
107
+ *found = 0;
108
+ #ifdef __SSE4_2__
109
+ if (likely(buf_end - buf >= 16)) {
110
+ __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111
+
112
+ size_t left = (buf_end - buf) & ~15;
113
+ do {
114
+ __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115
+ int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
116
+ if (unlikely(r != 16)) {
117
+ buf += r;
118
+ *found = 1;
119
+ break;
120
+ }
121
+ buf += 16;
122
+ left -= 16;
123
+ } while (likely(left != 0));
124
+ }
125
+ #else
126
+ /* suppress unused parameter warning */
127
+ (void)buf_end;
128
+ (void)ranges;
129
+ (void)ranges_size;
130
+ #endif
131
+ return buf;
132
+ }
133
+
134
+ static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret)
135
+ {
136
+ const char *token_start = buf;
137
+
138
+ #ifdef __SSE4_2__
139
+ static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
140
+ "\012\037" /* allow SP and up to but not including DEL */
141
+ "\177\177"; /* allow chars w. MSB set */
142
+ int found;
143
+ buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
144
+ if (found)
145
+ goto FOUND_CTL;
146
+ #else
147
+ /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */
148
+ while (likely(buf_end - buf >= 8)) {
149
+ #define DOIT() \
150
+ do { \
151
+ if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
152
+ goto NonPrintable; \
153
+ ++buf; \
154
+ } while (0)
155
+ DOIT();
156
+ DOIT();
157
+ DOIT();
158
+ DOIT();
159
+ DOIT();
160
+ DOIT();
161
+ DOIT();
162
+ DOIT();
163
+ #undef DOIT
164
+ continue;
165
+ NonPrintable:
166
+ if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
167
+ goto FOUND_CTL;
168
+ }
169
+ ++buf;
170
+ }
171
+ #endif
172
+ for (;; ++buf) {
173
+ CHECK_EOF();
174
+ if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175
+ if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
176
+ goto FOUND_CTL;
177
+ }
178
+ }
179
+ }
180
+ FOUND_CTL:
181
+ if (likely(*buf == '\015')) {
182
+ ++buf;
183
+ EXPECT_CHAR('\012');
184
+ *token_len = buf - 2 - token_start;
185
+ } else if (*buf == '\012') {
186
+ *token_len = buf - token_start;
187
+ ++buf;
188
+ } else {
189
+ *ret = -1;
190
+ return NULL;
191
+ }
192
+ *token = token_start;
193
+
194
+ return buf;
195
+ }
196
+
197
+ static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret)
198
+ {
199
+ int ret_cnt = 0;
200
+ buf = last_len < 3 ? buf : buf + last_len - 3;
201
+
202
+ while (1) {
203
+ CHECK_EOF();
204
+ if (*buf == '\015') {
205
+ ++buf;
206
+ CHECK_EOF();
207
+ EXPECT_CHAR('\012');
208
+ ++ret_cnt;
209
+ } else if (*buf == '\012') {
210
+ ++buf;
211
+ ++ret_cnt;
212
+ } else {
213
+ ++buf;
214
+ ret_cnt = 0;
215
+ }
216
+ if (ret_cnt == 2) {
217
+ return buf;
218
+ }
219
+ }
220
+
221
+ *ret = -2;
222
+ return NULL;
223
+ }
224
+
225
+ #define PARSE_INT(valp_, mul_) \
226
+ if (*buf < '0' || '9' < *buf) { \
227
+ buf++; \
228
+ *ret = -1; \
229
+ return NULL; \
230
+ } \
231
+ *(valp_) = (mul_) * (*buf++ - '0');
232
+
233
+ #define PARSE_INT_3(valp_) \
234
+ do { \
235
+ int res_ = 0; \
236
+ PARSE_INT(&res_, 100) \
237
+ *valp_ = res_; \
238
+ PARSE_INT(&res_, 10) \
239
+ *valp_ += res_; \
240
+ PARSE_INT(&res_, 1) \
241
+ *valp_ += res_; \
242
+ } while (0)
243
+
244
+ /* returned pointer is always within [buf, buf_end), or null */
245
+ static const char *parse_token(const char *buf, const char *buf_end, const char **token, size_t *token_len, char next_char,
246
+ int *ret)
247
+ {
248
+ /* We use pcmpestri to detect non-token characters. This instruction can take no more than eight character ranges (8*2*8=128
249
+ * bits that is the size of a SSE register). Due to this restriction, characters `|` and `~` are handled in the slow loop. */
250
+ static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */
251
+ "\"\"" /* 0x22 */
252
+ "()" /* 0x28,0x29 */
253
+ ",," /* 0x2c */
254
+ "//" /* 0x2f */
255
+ ":@" /* 0x3a-0x40 */
256
+ "[]" /* 0x5b-0x5d */
257
+ "{\xff"; /* 0x7b-0xff */
258
+ const char *buf_start = buf;
259
+ int found;
260
+ buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
261
+ if (!found) {
262
+ CHECK_EOF();
263
+ }
264
+ while (1) {
265
+ if (*buf == next_char) {
266
+ break;
267
+ } else if (!token_char_map[(unsigned char)*buf]) {
268
+ *ret = -1;
269
+ return NULL;
270
+ }
271
+ ++buf;
272
+ CHECK_EOF();
273
+ }
274
+ *token = buf_start;
275
+ *token_len = buf - buf_start;
276
+ return buf;
277
+ }
278
+
279
+ /* returned pointer is always within [buf, buf_end), or null */
280
+ static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret)
281
+ {
282
+ /* we want at least [HTTP/1.<two chars>] to try to parse */
283
+ if (buf_end - buf < 9) {
284
+ *ret = -2;
285
+ return NULL;
286
+ }
287
+ EXPECT_CHAR_NO_CHECK('H');
288
+ EXPECT_CHAR_NO_CHECK('T');
289
+ EXPECT_CHAR_NO_CHECK('T');
290
+ EXPECT_CHAR_NO_CHECK('P');
291
+ EXPECT_CHAR_NO_CHECK('/');
292
+ EXPECT_CHAR_NO_CHECK('1');
293
+ EXPECT_CHAR_NO_CHECK('.');
294
+ PARSE_INT(minor_version, 1);
295
+ return buf;
296
+ }
297
+
298
+ static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers,
299
+ size_t max_headers, int *ret)
300
+ {
301
+ for (;; ++*num_headers) {
302
+ CHECK_EOF();
303
+ if (*buf == '\015') {
304
+ ++buf;
305
+ EXPECT_CHAR('\012');
306
+ break;
307
+ } else if (*buf == '\012') {
308
+ ++buf;
309
+ break;
310
+ }
311
+ if (*num_headers == max_headers) {
312
+ *ret = -1;
313
+ return NULL;
314
+ }
315
+ if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
316
+ /* parsing name, but do not discard SP before colon, see
317
+ * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
318
+ if ((buf = parse_token(buf, buf_end, &headers[*num_headers].name, &headers[*num_headers].name_len, ':', ret)) == NULL) {
319
+ return NULL;
320
+ }
321
+ if (headers[*num_headers].name_len == 0) {
322
+ *ret = -1;
323
+ return NULL;
324
+ }
325
+ ++buf;
326
+ for (;; ++buf) {
327
+ CHECK_EOF();
328
+ if (!(*buf == ' ' || *buf == '\t')) {
329
+ break;
330
+ }
331
+ }
332
+ } else {
333
+ headers[*num_headers].name = NULL;
334
+ headers[*num_headers].name_len = 0;
335
+ }
336
+ const char *value;
337
+ size_t value_len;
338
+ if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
339
+ return NULL;
340
+ }
341
+ /* remove trailing SPs and HTABs */
342
+ const char *value_end = value + value_len;
343
+ for (; value_end != value; --value_end) {
344
+ const char c = *(value_end - 1);
345
+ if (!(c == ' ' || c == '\t')) {
346
+ break;
347
+ }
348
+ }
349
+ headers[*num_headers].value = value;
350
+ headers[*num_headers].value_len = value_end - value;
351
+ }
352
+ return buf;
353
+ }
354
+
355
+ static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path,
356
+ size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers,
357
+ size_t max_headers, int *ret)
358
+ {
359
+ /* skip first empty line (some clients add CRLF after POST content) */
360
+ CHECK_EOF();
361
+ if (*buf == '\015') {
362
+ ++buf;
363
+ EXPECT_CHAR('\012');
364
+ } else if (*buf == '\012') {
365
+ ++buf;
366
+ }
367
+
368
+ /* parse request line */
369
+ if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
370
+ return NULL;
371
+ }
372
+ do {
373
+ ++buf;
374
+ CHECK_EOF();
375
+ } while (*buf == ' ');
376
+ ADVANCE_TOKEN(*path, *path_len);
377
+ do {
378
+ ++buf;
379
+ CHECK_EOF();
380
+ } while (*buf == ' ');
381
+ if (*method_len == 0 || *path_len == 0) {
382
+ *ret = -1;
383
+ return NULL;
384
+ }
385
+ if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
386
+ return NULL;
387
+ }
388
+ if (*buf == '\015') {
389
+ ++buf;
390
+ EXPECT_CHAR('\012');
391
+ } else if (*buf == '\012') {
392
+ ++buf;
393
+ } else {
394
+ *ret = -1;
395
+ return NULL;
396
+ }
397
+
398
+ return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
399
+ }
400
+
401
+ static
402
+ int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path,
403
+ size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len)
404
+ {
405
+ const char *buf = buf_start, *buf_end = buf_start + len;
406
+ size_t max_headers = *num_headers;
407
+ int r;
408
+
409
+ *method = NULL;
410
+ *method_len = 0;
411
+ *path = NULL;
412
+ *path_len = 0;
413
+ *minor_version = -1;
414
+ *num_headers = 0;
415
+
416
+ /* if last_len != 0, check if the request is complete (a fast countermeasure
417
+ againt slowloris */
418
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
419
+ return r;
420
+ }
421
+
422
+ if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
423
+ &r)) == NULL) {
424
+ return r;
425
+ }
426
+
427
+ return (int)(buf - buf_start);
428
+ }
429
+
430
+ static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg,
431
+ size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret)
432
+ {
433
+ /* parse "HTTP/1.x" */
434
+ if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
435
+ return NULL;
436
+ }
437
+ /* skip space */
438
+ if (*buf != ' ') {
439
+ *ret = -1;
440
+ return NULL;
441
+ }
442
+ do {
443
+ ++buf;
444
+ CHECK_EOF();
445
+ } while (*buf == ' ');
446
+ /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */
447
+ if (buf_end - buf < 4) {
448
+ *ret = -2;
449
+ return NULL;
450
+ }
451
+ PARSE_INT_3(status);
452
+
453
+ /* get message including preceding space */
454
+ if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
455
+ return NULL;
456
+ }
457
+ if (*msg_len == 0) {
458
+ /* ok */
459
+ } else if (**msg == ' ') {
460
+ /* Remove preceding space. Successful return from `get_token_to_eol` guarantees that we would hit something other than SP
461
+ * before running past the end of the given buffer. */
462
+ do {
463
+ ++*msg;
464
+ --*msg_len;
465
+ } while (**msg == ' ');
466
+ } else {
467
+ /* garbage found after status code */
468
+ *ret = -1;
469
+ return NULL;
470
+ }
471
+
472
+ return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
473
+ }
474
+
475
+ static inline
476
+ int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len,
477
+ struct phr_header *headers, size_t *num_headers, size_t last_len)
478
+ {
479
+ const char *buf = buf_start, *buf_end = buf + len;
480
+ size_t max_headers = *num_headers;
481
+ int r;
482
+
483
+ *minor_version = -1;
484
+ *status = 0;
485
+ *msg = NULL;
486
+ *msg_len = 0;
487
+ *num_headers = 0;
488
+
489
+ /* if last_len != 0, check if the response is complete (a fast countermeasure
490
+ against slowloris */
491
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
492
+ return r;
493
+ }
494
+
495
+ if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) {
496
+ return r;
497
+ }
498
+
499
+ return (int)(buf - buf_start);
500
+ }
501
+
502
+ static inline
503
+ int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len)
504
+ {
505
+ const char *buf = buf_start, *buf_end = buf + len;
506
+ size_t max_headers = *num_headers;
507
+ int r;
508
+
509
+ *num_headers = 0;
510
+
511
+ /* if last_len != 0, check if the response is complete (a fast countermeasure
512
+ against slowloris */
513
+ if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
514
+ return r;
515
+ }
516
+
517
+ if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
518
+ return r;
519
+ }
520
+
521
+ return (int)(buf - buf_start);
522
+ }
523
+
524
+ enum {
525
+ CHUNKED_IN_CHUNK_SIZE,
526
+ CHUNKED_IN_CHUNK_EXT,
527
+ CHUNKED_IN_CHUNK_DATA,
528
+ CHUNKED_IN_CHUNK_CRLF,
529
+ CHUNKED_IN_TRAILERS_LINE_HEAD,
530
+ CHUNKED_IN_TRAILERS_LINE_MIDDLE
531
+ };
532
+
533
+ static int decode_hex(int ch)
534
+ {
535
+ if ('0' <= ch && ch <= '9') {
536
+ return ch - '0';
537
+ } else if ('A' <= ch && ch <= 'F') {
538
+ return ch - 'A' + 0xa;
539
+ } else if ('a' <= ch && ch <= 'f') {
540
+ return ch - 'a' + 0xa;
541
+ } else {
542
+ return -1;
543
+ }
544
+ }
545
+
546
+ static inline
547
+ ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz)
548
+ {
549
+ size_t dst = 0, src = 0, bufsz = *_bufsz;
550
+ ssize_t ret = -2; /* incomplete */
551
+
552
+ while (1) {
553
+ switch (decoder->_state) {
554
+ case CHUNKED_IN_CHUNK_SIZE:
555
+ for (;; ++src) {
556
+ int v;
557
+ if (src == bufsz)
558
+ goto Exit;
559
+ if ((v = decode_hex(buf[src])) == -1) {
560
+ if (decoder->_hex_count == 0) {
561
+ ret = -1;
562
+ goto Exit;
563
+ }
564
+ break;
565
+ }
566
+ if (decoder->_hex_count == sizeof(size_t) * 2) {
567
+ ret = -1;
568
+ goto Exit;
569
+ }
570
+ decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
571
+ ++decoder->_hex_count;
572
+ }
573
+ decoder->_hex_count = 0;
574
+ decoder->_state = CHUNKED_IN_CHUNK_EXT;
575
+ /* fallthru */
576
+ case CHUNKED_IN_CHUNK_EXT:
577
+ /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
578
+ for (;; ++src) {
579
+ if (src == bufsz)
580
+ goto Exit;
581
+ if (buf[src] == '\012')
582
+ break;
583
+ }
584
+ ++src;
585
+ if (decoder->bytes_left_in_chunk == 0) {
586
+ if (decoder->consume_trailer) {
587
+ decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
588
+ break;
589
+ } else {
590
+ goto Complete;
591
+ }
592
+ }
593
+ decoder->_state = CHUNKED_IN_CHUNK_DATA;
594
+ /* fallthru */
595
+ case CHUNKED_IN_CHUNK_DATA: {
596
+ size_t avail = bufsz - src;
597
+ if (avail < decoder->bytes_left_in_chunk) {
598
+ if (dst != src)
599
+ memmove(buf + dst, buf + src, avail);
600
+ src += avail;
601
+ dst += avail;
602
+ decoder->bytes_left_in_chunk -= avail;
603
+ goto Exit;
604
+ }
605
+ if (dst != src)
606
+ memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
607
+ src += decoder->bytes_left_in_chunk;
608
+ dst += decoder->bytes_left_in_chunk;
609
+ decoder->bytes_left_in_chunk = 0;
610
+ decoder->_state = CHUNKED_IN_CHUNK_CRLF;
611
+ }
612
+ /* fallthru */
613
+ case CHUNKED_IN_CHUNK_CRLF:
614
+ for (;; ++src) {
615
+ if (src == bufsz)
616
+ goto Exit;
617
+ if (buf[src] != '\015')
618
+ break;
619
+ }
620
+ if (buf[src] != '\012') {
621
+ ret = -1;
622
+ goto Exit;
623
+ }
624
+ ++src;
625
+ decoder->_state = CHUNKED_IN_CHUNK_SIZE;
626
+ break;
627
+ case CHUNKED_IN_TRAILERS_LINE_HEAD:
628
+ for (;; ++src) {
629
+ if (src == bufsz)
630
+ goto Exit;
631
+ if (buf[src] != '\015')
632
+ break;
633
+ }
634
+ if (buf[src++] == '\012')
635
+ goto Complete;
636
+ decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
637
+ /* fallthru */
638
+ case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
639
+ for (;; ++src) {
640
+ if (src == bufsz)
641
+ goto Exit;
642
+ if (buf[src] == '\012')
643
+ break;
644
+ }
645
+ ++src;
646
+ decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
647
+ break;
648
+ default:
649
+ assert(!"decoder is corrupt");
650
+ }
651
+ }
652
+
653
+ Complete:
654
+ ret = bufsz - src;
655
+ Exit:
656
+ if (dst != src)
657
+ memmove(buf + dst, buf + src, bufsz - src);
658
+ *_bufsz = dst;
659
+ return ret;
660
+ }
661
+
662
+ static inline
663
+ int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder)
664
+ {
665
+ return decoder->_state == CHUNKED_IN_CHUNK_DATA;
666
+ }
667
+
668
+ #undef CHECK_EOF
669
+ #undef EXPECT_CHAR
670
+ #undef ADVANCE_TOKEN
data/lib/mwrap/version.rb CHANGED
@@ -1 +1 @@
1
- module Mwrap; VERSION = '2.3.0'.freeze; end
1
+ module Mwrap; VERSION = '3.0.0.pre1'.freeze; end