noderb-http 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/noderb_http_extension/Makefile +187 -0
- data/ext/noderb_http_extension/extconf.rb +1 -1
- data/ext/noderb_http_extension/http_parser.c +239 -95
- data/ext/noderb_http_extension/http_parser.h +88 -6
- data/ext/noderb_http_extension/http_parser.o +0 -0
- data/ext/noderb_http_extension/noderb_http.c +41 -50
- data/ext/noderb_http_extension/noderb_http.o +0 -0
- data/ext/noderb_http_extension/noderb_http_extension.bundle +0 -0
- data/lib/noderb/modules/http/parser.rb +112 -0
- data/lib/noderb/modules/http/version.rb +9 -0
- data/lib/noderb/modules/http.rb +2 -99
- data/lib/noderb-http.rb +2 -2
- data/test/cases/curl_get.rb +25 -0
- data/test/cases/patch_request.rb +29 -0
- data/test/cases/response.rb +38 -0
- data/test/cases/upgrade_request.rb +34 -0
- data/test/init.rb +62 -0
- metadata +13 -5
- data/ext/http_parser.o +0 -0
- data/ext/noderb_http.o +0 -0
@@ -31,10 +31,27 @@
|
|
31
31
|
#endif
|
32
32
|
|
33
33
|
|
34
|
+
#if HTTP_PARSER_DEBUG
|
35
|
+
#define SET_ERRNO(e) \
|
36
|
+
do { \
|
37
|
+
parser->http_errno = (e); \
|
38
|
+
parser->error_lineno = __LINE__; \
|
39
|
+
} while (0)
|
40
|
+
#else
|
41
|
+
#define SET_ERRNO(e) \
|
42
|
+
do { \
|
43
|
+
parser->http_errno = (e); \
|
44
|
+
} while(0)
|
45
|
+
#endif
|
46
|
+
|
47
|
+
|
34
48
|
#define CALLBACK2(FOR) \
|
35
49
|
do { \
|
36
50
|
if (settings->on_##FOR) { \
|
37
|
-
if (0 != settings->on_##FOR(parser))
|
51
|
+
if (0 != settings->on_##FOR(parser)) { \
|
52
|
+
SET_ERRNO(HPE_CB_##FOR); \
|
53
|
+
return (p - data); \
|
54
|
+
} \
|
38
55
|
} \
|
39
56
|
} while (0)
|
40
57
|
|
@@ -44,7 +61,7 @@ do { \
|
|
44
61
|
FOR##_mark = p; \
|
45
62
|
} while (0)
|
46
63
|
|
47
|
-
#define
|
64
|
+
#define CALLBACK(FOR) \
|
48
65
|
do { \
|
49
66
|
if (FOR##_mark) { \
|
50
67
|
if (settings->on_##FOR) { \
|
@@ -52,20 +69,15 @@ do { \
|
|
52
69
|
FOR##_mark, \
|
53
70
|
p - FOR##_mark)) \
|
54
71
|
{ \
|
72
|
+
SET_ERRNO(HPE_CB_##FOR); \
|
55
73
|
return (p - data); \
|
56
74
|
} \
|
57
75
|
} \
|
76
|
+
FOR##_mark = NULL; \
|
58
77
|
} \
|
59
78
|
} while (0)
|
60
79
|
|
61
80
|
|
62
|
-
#define CALLBACK(FOR) \
|
63
|
-
do { \
|
64
|
-
CALLBACK_NOCLEAR(FOR); \
|
65
|
-
FOR##_mark = NULL; \
|
66
|
-
} while (0)
|
67
|
-
|
68
|
-
|
69
81
|
#define PROXY_CONNECTION "proxy-connection"
|
70
82
|
#define CONNECTION "connection"
|
71
83
|
#define CONTENT_LENGTH "content-length"
|
@@ -241,6 +253,7 @@ enum state
|
|
241
253
|
, s_header_field
|
242
254
|
, s_header_value_start
|
243
255
|
, s_header_value
|
256
|
+
, s_header_value_lws
|
244
257
|
|
245
258
|
, s_header_almost_done
|
246
259
|
|
@@ -299,7 +312,7 @@ enum header_states
|
|
299
312
|
#define LF '\n'
|
300
313
|
#define LOWER(c) (unsigned char)(c | 0x20)
|
301
314
|
#define TOKEN(c) (tokens[(unsigned char)c])
|
302
|
-
#define IS_ALPHA(c) ((c) >= 'a' && (c) <= 'z')
|
315
|
+
#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
|
303
316
|
#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
|
304
317
|
#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
|
305
318
|
|
@@ -318,7 +331,13 @@ enum header_states
|
|
318
331
|
|
319
332
|
|
320
333
|
#if HTTP_PARSER_STRICT
|
321
|
-
# define STRICT_CHECK(cond)
|
334
|
+
# define STRICT_CHECK(cond) \
|
335
|
+
do { \
|
336
|
+
if (cond) { \
|
337
|
+
SET_ERRNO(HPE_STRICT); \
|
338
|
+
goto error; \
|
339
|
+
} \
|
340
|
+
} while (0)
|
322
341
|
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
|
323
342
|
#else
|
324
343
|
# define STRICT_CHECK(cond)
|
@@ -326,20 +345,39 @@ enum header_states
|
|
326
345
|
#endif
|
327
346
|
|
328
347
|
|
348
|
+
/* Map errno values to strings for human-readable output */
|
349
|
+
#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
|
350
|
+
static struct {
|
351
|
+
const char *name;
|
352
|
+
const char *description;
|
353
|
+
} http_strerror_tab[] = {
|
354
|
+
HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
|
355
|
+
};
|
356
|
+
#undef HTTP_STRERROR_GEN
|
357
|
+
|
358
|
+
|
329
359
|
size_t http_parser_execute (http_parser *parser,
|
330
360
|
const http_parser_settings *settings,
|
331
361
|
const char *data,
|
332
362
|
size_t len)
|
333
363
|
{
|
334
364
|
char c, ch;
|
365
|
+
int8_t unhex_val;
|
335
366
|
const char *p = data, *pe;
|
336
367
|
int64_t to_read;
|
337
|
-
|
338
|
-
enum
|
339
|
-
enum header_states header_state = (enum header_states) parser->header_state;
|
368
|
+
enum state state;
|
369
|
+
enum header_states header_state;
|
340
370
|
uint64_t index = parser->index;
|
341
371
|
uint64_t nread = parser->nread;
|
342
372
|
|
373
|
+
/* We're in an error state. Don't bother doing anything. */
|
374
|
+
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
375
|
+
return 0;
|
376
|
+
}
|
377
|
+
|
378
|
+
state = (enum state) parser->state;
|
379
|
+
header_state = (enum header_states) parser->header_state;
|
380
|
+
|
343
381
|
if (len == 0) {
|
344
382
|
switch (state) {
|
345
383
|
case s_body_identity_eof:
|
@@ -353,7 +391,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
353
391
|
return 0;
|
354
392
|
|
355
393
|
default:
|
356
|
-
|
394
|
+
SET_ERRNO(HPE_INVALID_EOF_STATE);
|
395
|
+
return 1;
|
357
396
|
}
|
358
397
|
}
|
359
398
|
|
@@ -362,21 +401,12 @@ size_t http_parser_execute (http_parser *parser,
|
|
362
401
|
separated. */
|
363
402
|
const char *header_field_mark = 0;
|
364
403
|
const char *header_value_mark = 0;
|
365
|
-
const char *fragment_mark = 0;
|
366
|
-
const char *query_string_mark = 0;
|
367
|
-
const char *path_mark = 0;
|
368
404
|
const char *url_mark = 0;
|
369
405
|
|
370
406
|
if (state == s_header_field)
|
371
407
|
header_field_mark = data;
|
372
408
|
if (state == s_header_value)
|
373
409
|
header_value_mark = data;
|
374
|
-
if (state == s_req_fragment)
|
375
|
-
fragment_mark = data;
|
376
|
-
if (state == s_req_query_string)
|
377
|
-
query_string_mark = data;
|
378
|
-
if (state == s_req_path)
|
379
|
-
path_mark = data;
|
380
410
|
if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
|
381
411
|
|| state == s_req_schema_slash_slash || state == s_req_port
|
382
412
|
|| state == s_req_query_string_start || state == s_req_query_string
|
@@ -390,7 +420,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
390
420
|
if (PARSING_HEADER(state)) {
|
391
421
|
++nread;
|
392
422
|
/* Buffer overflow attack */
|
393
|
-
if (nread > HTTP_MAX_HEADER_SIZE)
|
423
|
+
if (nread > HTTP_MAX_HEADER_SIZE) {
|
424
|
+
SET_ERRNO(HPE_HEADER_OVERFLOW);
|
425
|
+
goto error;
|
426
|
+
}
|
394
427
|
}
|
395
428
|
|
396
429
|
switch (state) {
|
@@ -399,6 +432,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
399
432
|
/* this state is used after a 'Connection: close' message
|
400
433
|
* the parser will error out if it reads another message
|
401
434
|
*/
|
435
|
+
SET_ERRNO(HPE_CLOSED_CONNECTION);
|
402
436
|
goto error;
|
403
437
|
|
404
438
|
case s_start_req_or_res:
|
@@ -424,7 +458,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
424
458
|
parser->type = HTTP_RESPONSE;
|
425
459
|
state = s_res_HT;
|
426
460
|
} else {
|
427
|
-
if (ch != 'E')
|
461
|
+
if (ch != 'E') {
|
462
|
+
SET_ERRNO(HPE_INVALID_CONSTANT);
|
463
|
+
goto error;
|
464
|
+
}
|
465
|
+
|
428
466
|
parser->type = HTTP_REQUEST;
|
429
467
|
parser->method = HTTP_HEAD;
|
430
468
|
index = 2;
|
@@ -449,6 +487,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
449
487
|
break;
|
450
488
|
|
451
489
|
default:
|
490
|
+
SET_ERRNO(HPE_INVALID_CONSTANT);
|
452
491
|
goto error;
|
453
492
|
}
|
454
493
|
break;
|
@@ -475,7 +514,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
475
514
|
break;
|
476
515
|
|
477
516
|
case s_res_first_http_major:
|
478
|
-
if (ch < '1' || ch > '9')
|
517
|
+
if (ch < '1' || ch > '9') {
|
518
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
519
|
+
goto error;
|
520
|
+
}
|
521
|
+
|
479
522
|
parser->http_major = ch - '0';
|
480
523
|
state = s_res_http_major;
|
481
524
|
break;
|
@@ -488,18 +531,29 @@ size_t http_parser_execute (http_parser *parser,
|
|
488
531
|
break;
|
489
532
|
}
|
490
533
|
|
491
|
-
if (!IS_NUM(ch))
|
534
|
+
if (!IS_NUM(ch)) {
|
535
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
536
|
+
goto error;
|
537
|
+
}
|
492
538
|
|
493
539
|
parser->http_major *= 10;
|
494
540
|
parser->http_major += ch - '0';
|
495
541
|
|
496
|
-
if (parser->http_major > 999)
|
542
|
+
if (parser->http_major > 999) {
|
543
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
544
|
+
goto error;
|
545
|
+
}
|
546
|
+
|
497
547
|
break;
|
498
548
|
}
|
499
549
|
|
500
550
|
/* first digit of minor HTTP version */
|
501
551
|
case s_res_first_http_minor:
|
502
|
-
if (!IS_NUM(ch))
|
552
|
+
if (!IS_NUM(ch)) {
|
553
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
554
|
+
goto error;
|
555
|
+
}
|
556
|
+
|
503
557
|
parser->http_minor = ch - '0';
|
504
558
|
state = s_res_http_minor;
|
505
559
|
break;
|
@@ -512,12 +566,19 @@ size_t http_parser_execute (http_parser *parser,
|
|
512
566
|
break;
|
513
567
|
}
|
514
568
|
|
515
|
-
if (!IS_NUM(ch))
|
569
|
+
if (!IS_NUM(ch)) {
|
570
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
571
|
+
goto error;
|
572
|
+
}
|
516
573
|
|
517
574
|
parser->http_minor *= 10;
|
518
575
|
parser->http_minor += ch - '0';
|
519
576
|
|
520
|
-
if (parser->http_minor > 999)
|
577
|
+
if (parser->http_minor > 999) {
|
578
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
579
|
+
goto error;
|
580
|
+
}
|
581
|
+
|
521
582
|
break;
|
522
583
|
}
|
523
584
|
|
@@ -527,6 +588,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
527
588
|
if (ch == ' ') {
|
528
589
|
break;
|
529
590
|
}
|
591
|
+
|
592
|
+
SET_ERRNO(HPE_INVALID_STATUS);
|
530
593
|
goto error;
|
531
594
|
}
|
532
595
|
parser->status_code = ch - '0';
|
@@ -548,6 +611,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
548
611
|
state = s_header_field_start;
|
549
612
|
break;
|
550
613
|
default:
|
614
|
+
SET_ERRNO(HPE_INVALID_STATUS);
|
551
615
|
goto error;
|
552
616
|
}
|
553
617
|
break;
|
@@ -556,7 +620,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
556
620
|
parser->status_code *= 10;
|
557
621
|
parser->status_code += ch - '0';
|
558
622
|
|
559
|
-
if (parser->status_code > 999)
|
623
|
+
if (parser->status_code > 999) {
|
624
|
+
SET_ERRNO(HPE_INVALID_STATUS);
|
625
|
+
goto error;
|
626
|
+
}
|
627
|
+
|
560
628
|
break;
|
561
629
|
}
|
562
630
|
|
@@ -588,7 +656,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
588
656
|
|
589
657
|
CALLBACK2(message_begin);
|
590
658
|
|
591
|
-
if (!IS_ALPHA(
|
659
|
+
if (!IS_ALPHA(ch)) {
|
660
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
661
|
+
goto error;
|
662
|
+
}
|
592
663
|
|
593
664
|
start_req_method_assign:
|
594
665
|
parser->method = (enum http_method) 0;
|
@@ -609,7 +680,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
609
680
|
case 'S': parser->method = HTTP_SUBSCRIBE; break;
|
610
681
|
case 'T': parser->method = HTTP_TRACE; break;
|
611
682
|
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
|
612
|
-
default:
|
683
|
+
default:
|
684
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
685
|
+
goto error;
|
613
686
|
}
|
614
687
|
state = s_req_method;
|
615
688
|
break;
|
@@ -617,8 +690,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
617
690
|
|
618
691
|
case s_req_method:
|
619
692
|
{
|
620
|
-
if (ch == '\0')
|
693
|
+
if (ch == '\0') {
|
694
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
621
695
|
goto error;
|
696
|
+
}
|
622
697
|
|
623
698
|
const char *matcher = method_strings[parser->method];
|
624
699
|
if (ch == ' ' && matcher[index] == '\0') {
|
@@ -630,6 +705,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
630
705
|
parser->method = HTTP_CHECKOUT;
|
631
706
|
} else if (index == 2 && ch == 'P') {
|
632
707
|
parser->method = HTTP_COPY;
|
708
|
+
} else {
|
709
|
+
goto error;
|
633
710
|
}
|
634
711
|
} else if (parser->method == HTTP_MKCOL) {
|
635
712
|
if (index == 1 && ch == 'O') {
|
@@ -640,18 +717,25 @@ size_t http_parser_execute (http_parser *parser,
|
|
640
717
|
parser->method = HTTP_MSEARCH;
|
641
718
|
} else if (index == 2 && ch == 'A') {
|
642
719
|
parser->method = HTTP_MKACTIVITY;
|
720
|
+
} else {
|
721
|
+
goto error;
|
722
|
+
}
|
723
|
+
} else if (index == 1 && parser->method == HTTP_POST) {
|
724
|
+
if (ch == 'R') {
|
725
|
+
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
726
|
+
} else if (ch == 'U') {
|
727
|
+
parser->method = HTTP_PUT;
|
728
|
+
} else if (ch == 'A') {
|
729
|
+
parser->method = HTTP_PATCH;
|
730
|
+
} else {
|
731
|
+
goto error;
|
643
732
|
}
|
644
|
-
} else if (index == 1 && parser->method == HTTP_POST && ch == 'R') {
|
645
|
-
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
646
|
-
} else if (index == 1 && parser->method == HTTP_POST && ch == 'U') {
|
647
|
-
parser->method = HTTP_PUT;
|
648
|
-
} else if (index == 1 && parser->method == HTTP_POST && ch == 'A') {
|
649
|
-
parser->method = HTTP_PATCH;
|
650
733
|
} else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
|
651
734
|
parser->method = HTTP_UNSUBSCRIBE;
|
652
735
|
} else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
653
736
|
parser->method = HTTP_PROPPATCH;
|
654
737
|
} else {
|
738
|
+
SET_ERRNO(HPE_INVALID_METHOD);
|
655
739
|
goto error;
|
656
740
|
}
|
657
741
|
|
@@ -664,13 +748,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
664
748
|
|
665
749
|
if (ch == '/' || ch == '*') {
|
666
750
|
MARK(url);
|
667
|
-
MARK(path);
|
668
751
|
state = s_req_path;
|
669
752
|
break;
|
670
753
|
}
|
671
754
|
|
672
|
-
c = LOWER(ch);
|
673
|
-
|
674
755
|
/* Proxied requests are followed by scheme of an absolute URI (alpha).
|
675
756
|
* CONNECT is followed by a hostname, which begins with alphanum.
|
676
757
|
* All other methods are followed by '/' or '*' (handled above).
|
@@ -681,20 +762,20 @@ size_t http_parser_execute (http_parser *parser,
|
|
681
762
|
break;
|
682
763
|
}
|
683
764
|
|
765
|
+
SET_ERRNO(HPE_INVALID_URL);
|
684
766
|
goto error;
|
685
767
|
}
|
686
768
|
|
687
769
|
case s_req_schema:
|
688
770
|
{
|
689
|
-
|
690
|
-
|
691
|
-
if (IS_ALPHA(c)) break;
|
771
|
+
if (IS_ALPHA(ch)) break;
|
692
772
|
|
693
773
|
if (ch == ':') {
|
694
774
|
state = s_req_schema_slash;
|
695
775
|
break;
|
696
776
|
}
|
697
777
|
|
778
|
+
SET_ERRNO(HPE_INVALID_URL);
|
698
779
|
goto error;
|
699
780
|
}
|
700
781
|
|
@@ -710,14 +791,12 @@ size_t http_parser_execute (http_parser *parser,
|
|
710
791
|
|
711
792
|
case s_req_host:
|
712
793
|
{
|
713
|
-
c = LOWER(ch);
|
714
794
|
if (IS_HOST_CHAR(ch)) break;
|
715
795
|
switch (ch) {
|
716
796
|
case ':':
|
717
797
|
state = s_req_port;
|
718
798
|
break;
|
719
799
|
case '/':
|
720
|
-
MARK(path);
|
721
800
|
state = s_req_path;
|
722
801
|
break;
|
723
802
|
case ' ':
|
@@ -732,6 +811,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
732
811
|
state = s_req_query_string_start;
|
733
812
|
break;
|
734
813
|
default:
|
814
|
+
SET_ERRNO(HPE_INVALID_HOST);
|
735
815
|
goto error;
|
736
816
|
}
|
737
817
|
break;
|
@@ -742,7 +822,6 @@ size_t http_parser_execute (http_parser *parser,
|
|
742
822
|
if (IS_NUM(ch)) break;
|
743
823
|
switch (ch) {
|
744
824
|
case '/':
|
745
|
-
MARK(path);
|
746
825
|
state = s_req_path;
|
747
826
|
break;
|
748
827
|
case ' ':
|
@@ -757,6 +836,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
757
836
|
state = s_req_query_string_start;
|
758
837
|
break;
|
759
838
|
default:
|
839
|
+
SET_ERRNO(HPE_INVALID_PORT);
|
760
840
|
goto error;
|
761
841
|
}
|
762
842
|
break;
|
@@ -769,32 +849,28 @@ size_t http_parser_execute (http_parser *parser,
|
|
769
849
|
switch (ch) {
|
770
850
|
case ' ':
|
771
851
|
CALLBACK(url);
|
772
|
-
CALLBACK(path);
|
773
852
|
state = s_req_http_start;
|
774
853
|
break;
|
775
854
|
case CR:
|
776
855
|
CALLBACK(url);
|
777
|
-
CALLBACK(path);
|
778
856
|
parser->http_major = 0;
|
779
857
|
parser->http_minor = 9;
|
780
858
|
state = s_req_line_almost_done;
|
781
859
|
break;
|
782
860
|
case LF:
|
783
861
|
CALLBACK(url);
|
784
|
-
CALLBACK(path);
|
785
862
|
parser->http_major = 0;
|
786
863
|
parser->http_minor = 9;
|
787
864
|
state = s_header_field_start;
|
788
865
|
break;
|
789
866
|
case '?':
|
790
|
-
CALLBACK(path);
|
791
867
|
state = s_req_query_string_start;
|
792
868
|
break;
|
793
869
|
case '#':
|
794
|
-
CALLBACK(path);
|
795
870
|
state = s_req_fragment_start;
|
796
871
|
break;
|
797
872
|
default:
|
873
|
+
SET_ERRNO(HPE_INVALID_PATH);
|
798
874
|
goto error;
|
799
875
|
}
|
800
876
|
break;
|
@@ -803,7 +879,6 @@ size_t http_parser_execute (http_parser *parser,
|
|
803
879
|
case s_req_query_string_start:
|
804
880
|
{
|
805
881
|
if (IS_URL_CHAR(ch)) {
|
806
|
-
MARK(query_string);
|
807
882
|
state = s_req_query_string;
|
808
883
|
break;
|
809
884
|
}
|
@@ -831,6 +906,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
831
906
|
state = s_req_fragment_start;
|
832
907
|
break;
|
833
908
|
default:
|
909
|
+
SET_ERRNO(HPE_INVALID_QUERY_STRING);
|
834
910
|
goto error;
|
835
911
|
}
|
836
912
|
break;
|
@@ -846,28 +922,25 @@ size_t http_parser_execute (http_parser *parser,
|
|
846
922
|
break;
|
847
923
|
case ' ':
|
848
924
|
CALLBACK(url);
|
849
|
-
CALLBACK(query_string);
|
850
925
|
state = s_req_http_start;
|
851
926
|
break;
|
852
927
|
case CR:
|
853
928
|
CALLBACK(url);
|
854
|
-
CALLBACK(query_string);
|
855
929
|
parser->http_major = 0;
|
856
930
|
parser->http_minor = 9;
|
857
931
|
state = s_req_line_almost_done;
|
858
932
|
break;
|
859
933
|
case LF:
|
860
934
|
CALLBACK(url);
|
861
|
-
CALLBACK(query_string);
|
862
935
|
parser->http_major = 0;
|
863
936
|
parser->http_minor = 9;
|
864
937
|
state = s_header_field_start;
|
865
938
|
break;
|
866
939
|
case '#':
|
867
|
-
CALLBACK(query_string);
|
868
940
|
state = s_req_fragment_start;
|
869
941
|
break;
|
870
942
|
default:
|
943
|
+
SET_ERRNO(HPE_INVALID_QUERY_STRING);
|
871
944
|
goto error;
|
872
945
|
}
|
873
946
|
break;
|
@@ -876,7 +949,6 @@ size_t http_parser_execute (http_parser *parser,
|
|
876
949
|
case s_req_fragment_start:
|
877
950
|
{
|
878
951
|
if (IS_URL_CHAR(ch)) {
|
879
|
-
MARK(fragment);
|
880
952
|
state = s_req_fragment;
|
881
953
|
break;
|
882
954
|
}
|
@@ -899,12 +971,12 @@ size_t http_parser_execute (http_parser *parser,
|
|
899
971
|
state = s_header_field_start;
|
900
972
|
break;
|
901
973
|
case '?':
|
902
|
-
MARK(fragment);
|
903
974
|
state = s_req_fragment;
|
904
975
|
break;
|
905
976
|
case '#':
|
906
977
|
break;
|
907
978
|
default:
|
979
|
+
SET_ERRNO(HPE_INVALID_FRAGMENT);
|
908
980
|
goto error;
|
909
981
|
}
|
910
982
|
break;
|
@@ -917,19 +989,16 @@ size_t http_parser_execute (http_parser *parser,
|
|
917
989
|
switch (ch) {
|
918
990
|
case ' ':
|
919
991
|
CALLBACK(url);
|
920
|
-
CALLBACK(fragment);
|
921
992
|
state = s_req_http_start;
|
922
993
|
break;
|
923
994
|
case CR:
|
924
995
|
CALLBACK(url);
|
925
|
-
CALLBACK(fragment);
|
926
996
|
parser->http_major = 0;
|
927
997
|
parser->http_minor = 9;
|
928
998
|
state = s_req_line_almost_done;
|
929
999
|
break;
|
930
1000
|
case LF:
|
931
1001
|
CALLBACK(url);
|
932
|
-
CALLBACK(fragment);
|
933
1002
|
parser->http_major = 0;
|
934
1003
|
parser->http_minor = 9;
|
935
1004
|
state = s_header_field_start;
|
@@ -938,6 +1007,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
938
1007
|
case '#':
|
939
1008
|
break;
|
940
1009
|
default:
|
1010
|
+
SET_ERRNO(HPE_INVALID_FRAGMENT);
|
941
1011
|
goto error;
|
942
1012
|
}
|
943
1013
|
break;
|
@@ -951,6 +1021,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
951
1021
|
case ' ':
|
952
1022
|
break;
|
953
1023
|
default:
|
1024
|
+
SET_ERRNO(HPE_INVALID_CONSTANT);
|
954
1025
|
goto error;
|
955
1026
|
}
|
956
1027
|
break;
|
@@ -977,7 +1048,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
977
1048
|
|
978
1049
|
/* first digit of major HTTP version */
|
979
1050
|
case s_req_first_http_major:
|
980
|
-
if (ch < '1' || ch > '9')
|
1051
|
+
if (ch < '1' || ch > '9') {
|
1052
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1053
|
+
goto error;
|
1054
|
+
}
|
1055
|
+
|
981
1056
|
parser->http_major = ch - '0';
|
982
1057
|
state = s_req_http_major;
|
983
1058
|
break;
|
@@ -990,18 +1065,29 @@ size_t http_parser_execute (http_parser *parser,
|
|
990
1065
|
break;
|
991
1066
|
}
|
992
1067
|
|
993
|
-
if (!IS_NUM(ch))
|
1068
|
+
if (!IS_NUM(ch)) {
|
1069
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1070
|
+
goto error;
|
1071
|
+
}
|
994
1072
|
|
995
1073
|
parser->http_major *= 10;
|
996
1074
|
parser->http_major += ch - '0';
|
997
1075
|
|
998
|
-
if (parser->http_major > 999)
|
1076
|
+
if (parser->http_major > 999) {
|
1077
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1078
|
+
goto error;
|
1079
|
+
}
|
1080
|
+
|
999
1081
|
break;
|
1000
1082
|
}
|
1001
1083
|
|
1002
1084
|
/* first digit of minor HTTP version */
|
1003
1085
|
case s_req_first_http_minor:
|
1004
|
-
if (!IS_NUM(ch))
|
1086
|
+
if (!IS_NUM(ch)) {
|
1087
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1088
|
+
goto error;
|
1089
|
+
}
|
1090
|
+
|
1005
1091
|
parser->http_minor = ch - '0';
|
1006
1092
|
state = s_req_http_minor;
|
1007
1093
|
break;
|
@@ -1021,24 +1107,36 @@ size_t http_parser_execute (http_parser *parser,
|
|
1021
1107
|
|
1022
1108
|
/* XXX allow spaces after digit? */
|
1023
1109
|
|
1024
|
-
if (!IS_NUM(ch))
|
1110
|
+
if (!IS_NUM(ch)) {
|
1111
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1112
|
+
goto error;
|
1113
|
+
}
|
1025
1114
|
|
1026
1115
|
parser->http_minor *= 10;
|
1027
1116
|
parser->http_minor += ch - '0';
|
1028
1117
|
|
1029
|
-
if (parser->http_minor > 999)
|
1118
|
+
if (parser->http_minor > 999) {
|
1119
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1120
|
+
goto error;
|
1121
|
+
}
|
1122
|
+
|
1030
1123
|
break;
|
1031
1124
|
}
|
1032
1125
|
|
1033
1126
|
/* end of request line */
|
1034
1127
|
case s_req_line_almost_done:
|
1035
1128
|
{
|
1036
|
-
if (ch != LF)
|
1129
|
+
if (ch != LF) {
|
1130
|
+
SET_ERRNO(HPE_LF_EXPECTED);
|
1131
|
+
goto error;
|
1132
|
+
}
|
1133
|
+
|
1037
1134
|
state = s_header_field_start;
|
1038
1135
|
break;
|
1039
1136
|
}
|
1040
1137
|
|
1041
1138
|
case s_header_field_start:
|
1139
|
+
header_field_start:
|
1042
1140
|
{
|
1043
1141
|
if (ch == CR) {
|
1044
1142
|
state = s_headers_almost_done;
|
@@ -1054,7 +1152,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
1054
1152
|
|
1055
1153
|
c = TOKEN(ch);
|
1056
1154
|
|
1057
|
-
if (!c)
|
1155
|
+
if (!c) {
|
1156
|
+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
1157
|
+
goto error;
|
1158
|
+
}
|
1058
1159
|
|
1059
1160
|
MARK(header_field);
|
1060
1161
|
|
@@ -1211,20 +1312,19 @@ size_t http_parser_execute (http_parser *parser,
|
|
1211
1312
|
break;
|
1212
1313
|
}
|
1213
1314
|
|
1315
|
+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
1214
1316
|
goto error;
|
1215
1317
|
}
|
1216
1318
|
|
1217
1319
|
case s_header_value_start:
|
1218
1320
|
{
|
1219
|
-
if (ch == ' ') break;
|
1321
|
+
if (ch == ' ' || ch == '\t') break;
|
1220
1322
|
|
1221
1323
|
MARK(header_value);
|
1222
1324
|
|
1223
1325
|
state = s_header_value;
|
1224
1326
|
index = 0;
|
1225
1327
|
|
1226
|
-
c = LOWER(ch);
|
1227
|
-
|
1228
1328
|
if (ch == CR) {
|
1229
1329
|
CALLBACK(header_value);
|
1230
1330
|
header_state = h_general;
|
@@ -1238,6 +1338,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
1238
1338
|
break;
|
1239
1339
|
}
|
1240
1340
|
|
1341
|
+
c = LOWER(ch);
|
1342
|
+
|
1241
1343
|
switch (header_state) {
|
1242
1344
|
case h_upgrade:
|
1243
1345
|
parser->flags |= F_UPGRADE;
|
@@ -1254,7 +1356,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
1254
1356
|
break;
|
1255
1357
|
|
1256
1358
|
case h_content_length:
|
1257
|
-
if (!IS_NUM(ch))
|
1359
|
+
if (!IS_NUM(ch)) {
|
1360
|
+
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1361
|
+
goto error;
|
1362
|
+
}
|
1363
|
+
|
1258
1364
|
parser->content_length = ch - '0';
|
1259
1365
|
break;
|
1260
1366
|
|
@@ -1279,7 +1385,6 @@ size_t http_parser_execute (http_parser *parser,
|
|
1279
1385
|
|
1280
1386
|
case s_header_value:
|
1281
1387
|
{
|
1282
|
-
c = LOWER(ch);
|
1283
1388
|
|
1284
1389
|
if (ch == CR) {
|
1285
1390
|
CALLBACK(header_value);
|
@@ -1292,6 +1397,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
1292
1397
|
goto header_almost_done;
|
1293
1398
|
}
|
1294
1399
|
|
1400
|
+
c = LOWER(ch);
|
1401
|
+
|
1295
1402
|
switch (header_state) {
|
1296
1403
|
case h_general:
|
1297
1404
|
break;
|
@@ -1303,7 +1410,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
1303
1410
|
|
1304
1411
|
case h_content_length:
|
1305
1412
|
if (ch == ' ') break;
|
1306
|
-
if (!IS_NUM(ch))
|
1413
|
+
if (!IS_NUM(ch)) {
|
1414
|
+
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1415
|
+
goto error;
|
1416
|
+
}
|
1417
|
+
|
1307
1418
|
parser->content_length *= 10;
|
1308
1419
|
parser->content_length += ch - '0';
|
1309
1420
|
break;
|
@@ -1359,7 +1470,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1359
1470
|
{
|
1360
1471
|
STRICT_CHECK(ch != LF);
|
1361
1472
|
|
1362
|
-
state =
|
1473
|
+
state = s_header_value_lws;
|
1363
1474
|
|
1364
1475
|
switch (header_state) {
|
1365
1476
|
case h_connection_keep_alive:
|
@@ -1377,6 +1488,18 @@ size_t http_parser_execute (http_parser *parser,
|
|
1377
1488
|
break;
|
1378
1489
|
}
|
1379
1490
|
|
1491
|
+
case s_header_value_lws:
|
1492
|
+
{
|
1493
|
+
if (ch == ' ' || ch == '\t')
|
1494
|
+
state = s_header_value_start;
|
1495
|
+
else
|
1496
|
+
{
|
1497
|
+
state = s_header_field_start;
|
1498
|
+
goto header_field_start;
|
1499
|
+
}
|
1500
|
+
break;
|
1501
|
+
}
|
1502
|
+
|
1380
1503
|
case s_headers_almost_done:
|
1381
1504
|
headers_almost_done:
|
1382
1505
|
{
|
@@ -1412,6 +1535,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1412
1535
|
|
1413
1536
|
default:
|
1414
1537
|
parser->state = state;
|
1538
|
+
SET_ERRNO(HPE_CB_headers_complete);
|
1415
1539
|
return p - data; /* Error */
|
1416
1540
|
}
|
1417
1541
|
}
|
@@ -1419,7 +1543,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1419
1543
|
/* Exit, the rest of the connect is in a different protocol. */
|
1420
1544
|
if (parser->upgrade) {
|
1421
1545
|
CALLBACK2(message_complete);
|
1422
|
-
return (p - data);
|
1546
|
+
return (p - data) + 1;
|
1423
1547
|
}
|
1424
1548
|
|
1425
1549
|
if (parser->flags & F_SKIPBODY) {
|
@@ -1478,9 +1602,13 @@ size_t http_parser_execute (http_parser *parser,
|
|
1478
1602
|
assert(nread == 1);
|
1479
1603
|
assert(parser->flags & F_CHUNKED);
|
1480
1604
|
|
1481
|
-
|
1482
|
-
if (
|
1483
|
-
|
1605
|
+
unhex_val = unhex[(unsigned char)ch];
|
1606
|
+
if (unhex_val == -1) {
|
1607
|
+
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
|
1608
|
+
goto error;
|
1609
|
+
}
|
1610
|
+
|
1611
|
+
parser->content_length = unhex_val;
|
1484
1612
|
state = s_chunk_size;
|
1485
1613
|
break;
|
1486
1614
|
}
|
@@ -1494,18 +1622,20 @@ size_t http_parser_execute (http_parser *parser,
|
|
1494
1622
|
break;
|
1495
1623
|
}
|
1496
1624
|
|
1497
|
-
|
1625
|
+
unhex_val = unhex[(unsigned char)ch];
|
1498
1626
|
|
1499
|
-
if (
|
1627
|
+
if (unhex_val == -1) {
|
1500
1628
|
if (ch == ';' || ch == ' ') {
|
1501
1629
|
state = s_chunk_parameters;
|
1502
1630
|
break;
|
1503
1631
|
}
|
1632
|
+
|
1633
|
+
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
|
1504
1634
|
goto error;
|
1505
1635
|
}
|
1506
1636
|
|
1507
1637
|
parser->content_length *= 16;
|
1508
|
-
parser->content_length +=
|
1638
|
+
parser->content_length += unhex_val;
|
1509
1639
|
break;
|
1510
1640
|
}
|
1511
1641
|
|
@@ -1569,16 +1699,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
1569
1699
|
|
1570
1700
|
default:
|
1571
1701
|
assert(0 && "unhandled state");
|
1702
|
+
SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
|
1572
1703
|
goto error;
|
1573
1704
|
}
|
1574
1705
|
}
|
1575
1706
|
|
1576
|
-
|
1577
|
-
|
1578
|
-
|
1579
|
-
CALLBACK_NOCLEAR(query_string);
|
1580
|
-
CALLBACK_NOCLEAR(path);
|
1581
|
-
CALLBACK_NOCLEAR(url);
|
1707
|
+
CALLBACK(header_field);
|
1708
|
+
CALLBACK(header_value);
|
1709
|
+
CALLBACK(url);
|
1582
1710
|
|
1583
1711
|
parser->state = state;
|
1584
1712
|
parser->header_state = header_state;
|
@@ -1588,7 +1716,10 @@ size_t http_parser_execute (http_parser *parser,
|
|
1588
1716
|
return len;
|
1589
1717
|
|
1590
1718
|
error:
|
1591
|
-
parser
|
1719
|
+
if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
|
1720
|
+
SET_ERRNO(HPE_UNKNOWN);
|
1721
|
+
}
|
1722
|
+
|
1592
1723
|
return (p - data);
|
1593
1724
|
}
|
1594
1725
|
|
@@ -1629,4 +1760,17 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
|
|
1629
1760
|
parser->upgrade = 0;
|
1630
1761
|
parser->flags = 0;
|
1631
1762
|
parser->method = 0;
|
1763
|
+
parser->http_errno = 0;
|
1764
|
+
}
|
1765
|
+
|
1766
|
+
const char *
|
1767
|
+
http_errno_name(enum http_errno err) {
|
1768
|
+
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
|
1769
|
+
return http_strerror_tab[err].name;
|
1770
|
+
}
|
1771
|
+
|
1772
|
+
const char *
|
1773
|
+
http_errno_description(enum http_errno err) {
|
1774
|
+
assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
|
1775
|
+
return http_strerror_tab[err].description;
|
1632
1776
|
}
|