http-parser 1.0.4 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/ext/Rakefile +0 -2
- data/ext/http-parser/http_parser.c +651 -423
- data/ext/http-parser/http_parser.h +147 -29
- data/http-parser.gemspec +6 -4
- data/lib/http-parser.rb +2 -0
- data/lib/http-parser/errors.rb +5 -2
- data/lib/http-parser/ext.rb +2 -0
- data/lib/http-parser/parser.rb +37 -3
- data/lib/http-parser/types.rb +34 -10
- data/lib/http-parser/version.rb +3 -1
- metadata +36 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: c32dcf71a6dae540e33a54338404f6ed9a7c51eef0ad0913162c75152189c041
|
4
|
+
data.tar.gz: 3bd270c5e3362c3c3ec4274e623f028d1fd5f3d5d30c24c87fa7babba9e555ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aef9b8b55d191a662cb831a51c726ed7eecaab049884523db8fe204326daa7aeb6c0ecf2e026e81eb8138274d20d44aaa6d6cfea8b7611d38e214982f860e38
|
7
|
+
data.tar.gz: 0d9bfde682dafa7e341eda1554cc7e9dc94c89de5dcff5df164a6c0bc5cde47deaa727eca04d706b138eea3e6d4a6351d81498f7c719240fd7244621c4502113
|
data/ext/Rakefile
CHANGED
@@ -3,6 +3,4 @@ require 'ffi-compiler/compile_task'
|
|
3
3
|
FFI::Compiler::CompileTask.new('http-parser-ext') do |t|
|
4
4
|
t.cflags << "-Wall -Wextra -O3"
|
5
5
|
t.cflags << "-D_GNU_SOURCE=1" if RbConfig::CONFIG["host_os"].downcase =~ /mingw/
|
6
|
-
t.cflags << "-arch x86_64 -arch i386" if t.platform.mac?
|
7
|
-
t.ldflags << "-arch x86_64 -arch i386" if t.platform.mac?
|
8
6
|
end
|
@@ -1,7 +1,4 @@
|
|
1
|
-
/*
|
2
|
-
*
|
3
|
-
* Additional changes are licensed under the same terms as NGINX and
|
4
|
-
* copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
1
|
+
/* Copyright Joyent, Inc. and other Node contributors.
|
5
2
|
*
|
6
3
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
4
|
* of this software and associated documentation files (the "Software"), to
|
@@ -25,7 +22,6 @@
|
|
25
22
|
#include <assert.h>
|
26
23
|
#include <stddef.h>
|
27
24
|
#include <ctype.h>
|
28
|
-
#include <stdlib.h>
|
29
25
|
#include <string.h>
|
30
26
|
#include <limits.h>
|
31
27
|
|
@@ -53,22 +49,45 @@
|
|
53
49
|
|
54
50
|
#define SET_ERRNO(e) \
|
55
51
|
do { \
|
52
|
+
parser->nread = nread; \
|
56
53
|
parser->http_errno = (e); \
|
57
54
|
} while(0)
|
58
55
|
|
56
|
+
#define CURRENT_STATE() p_state
|
57
|
+
#define UPDATE_STATE(V) p_state = (enum state) (V);
|
58
|
+
#define RETURN(V) \
|
59
|
+
do { \
|
60
|
+
parser->nread = nread; \
|
61
|
+
parser->state = CURRENT_STATE(); \
|
62
|
+
return (V); \
|
63
|
+
} while (0);
|
64
|
+
#define REEXECUTE() \
|
65
|
+
goto reexecute; \
|
66
|
+
|
67
|
+
|
68
|
+
#ifdef __GNUC__
|
69
|
+
# define LIKELY(X) __builtin_expect(!!(X), 1)
|
70
|
+
# define UNLIKELY(X) __builtin_expect(!!(X), 0)
|
71
|
+
#else
|
72
|
+
# define LIKELY(X) (X)
|
73
|
+
# define UNLIKELY(X) (X)
|
74
|
+
#endif
|
75
|
+
|
59
76
|
|
60
77
|
/* Run the notify callback FOR, returning ER if it fails */
|
61
78
|
#define CALLBACK_NOTIFY_(FOR, ER) \
|
62
79
|
do { \
|
63
80
|
assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
|
64
81
|
\
|
65
|
-
if (settings->on_##FOR) {
|
66
|
-
|
82
|
+
if (LIKELY(settings->on_##FOR)) { \
|
83
|
+
parser->state = CURRENT_STATE(); \
|
84
|
+
if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
|
67
85
|
SET_ERRNO(HPE_CB_##FOR); \
|
68
86
|
} \
|
87
|
+
UPDATE_STATE(parser->state); \
|
69
88
|
\
|
70
89
|
/* We either errored above or got paused; get out */ \
|
71
|
-
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
90
|
+
if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
|
72
91
|
return (ER); \
|
73
92
|
} \
|
74
93
|
} \
|
@@ -86,20 +105,23 @@ do { \
|
|
86
105
|
assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
|
87
106
|
\
|
88
107
|
if (FOR##_mark) { \
|
89
|
-
if (settings->on_##FOR) {
|
90
|
-
|
108
|
+
if (LIKELY(settings->on_##FOR)) { \
|
109
|
+
parser->state = CURRENT_STATE(); \
|
110
|
+
if (UNLIKELY(0 != \
|
111
|
+
settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
|
91
112
|
SET_ERRNO(HPE_CB_##FOR); \
|
92
113
|
} \
|
114
|
+
UPDATE_STATE(parser->state); \
|
93
115
|
\
|
94
116
|
/* We either errored above or got paused; get out */ \
|
95
|
-
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
117
|
+
if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
|
96
118
|
return (ER); \
|
97
119
|
} \
|
98
120
|
} \
|
99
121
|
FOR##_mark = NULL; \
|
100
122
|
} \
|
101
123
|
} while (0)
|
102
|
-
|
124
|
+
|
103
125
|
/* Run the data callback FOR and consume the current byte */
|
104
126
|
#define CALLBACK_DATA(FOR) \
|
105
127
|
CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
|
@@ -116,6 +138,26 @@ do { \
|
|
116
138
|
} \
|
117
139
|
} while (0)
|
118
140
|
|
141
|
+
/* Don't allow the total size of the HTTP headers (including the status
|
142
|
+
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
|
143
|
+
* embedders against denial-of-service attacks where the attacker feeds
|
144
|
+
* us a never-ending header that the embedder keeps buffering.
|
145
|
+
*
|
146
|
+
* This check is arguably the responsibility of embedders but we're doing
|
147
|
+
* it on the embedder's behalf because most won't bother and this way we
|
148
|
+
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
|
149
|
+
* than any reasonable request or response so this should never affect
|
150
|
+
* day-to-day operation.
|
151
|
+
*/
|
152
|
+
#define COUNT_HEADER_SIZE(V) \
|
153
|
+
do { \
|
154
|
+
nread += (V); \
|
155
|
+
if (UNLIKELY(nread > (HTTP_MAX_HEADER_SIZE))) { \
|
156
|
+
SET_ERRNO(HPE_HEADER_OVERFLOW); \
|
157
|
+
goto error; \
|
158
|
+
} \
|
159
|
+
} while (0)
|
160
|
+
|
119
161
|
|
120
162
|
#define PROXY_CONNECTION "proxy-connection"
|
121
163
|
#define CONNECTION "connection"
|
@@ -152,7 +194,7 @@ static const char tokens[256] = {
|
|
152
194
|
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
|
153
195
|
0, 0, 0, 0, 0, 0, 0, 0,
|
154
196
|
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
|
155
|
-
|
197
|
+
' ', '!', 0, '#', '$', '%', '&', '\'',
|
156
198
|
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
|
157
199
|
0, 0, '*', '+', 0, '-', '.', 0,
|
158
200
|
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
|
@@ -242,10 +284,10 @@ enum state
|
|
242
284
|
, s_res_HT
|
243
285
|
, s_res_HTT
|
244
286
|
, s_res_HTTP
|
245
|
-
, s_res_first_http_major
|
246
287
|
, s_res_http_major
|
247
|
-
,
|
288
|
+
, s_res_http_dot
|
248
289
|
, s_res_http_minor
|
290
|
+
, s_res_http_end
|
249
291
|
, s_res_first_status_code
|
250
292
|
, s_res_status_code
|
251
293
|
, s_res_status_start
|
@@ -272,14 +314,17 @@ enum state
|
|
272
314
|
, s_req_http_HT
|
273
315
|
, s_req_http_HTT
|
274
316
|
, s_req_http_HTTP
|
275
|
-
, s_req_first_http_major
|
276
317
|
, s_req_http_major
|
277
|
-
,
|
318
|
+
, s_req_http_dot
|
278
319
|
, s_req_http_minor
|
320
|
+
, s_req_http_end
|
279
321
|
, s_req_line_almost_done
|
280
322
|
|
281
323
|
, s_header_field_start
|
282
324
|
, s_header_field
|
325
|
+
, s_header_value_discard_ws
|
326
|
+
, s_header_value_discard_ws_almost_done
|
327
|
+
, s_header_value_discard_lws
|
283
328
|
, s_header_value_start
|
284
329
|
, s_header_value
|
285
330
|
, s_header_value_lws
|
@@ -327,16 +372,22 @@ enum header_states
|
|
327
372
|
|
328
373
|
, h_connection
|
329
374
|
, h_content_length
|
375
|
+
, h_content_length_num
|
376
|
+
, h_content_length_ws
|
330
377
|
, h_transfer_encoding
|
331
378
|
, h_upgrade
|
332
379
|
|
333
380
|
, h_matching_transfer_encoding_chunked
|
381
|
+
, h_matching_connection_token_start
|
334
382
|
, h_matching_connection_keep_alive
|
335
383
|
, h_matching_connection_close
|
384
|
+
, h_matching_connection_upgrade
|
385
|
+
, h_matching_connection_token
|
336
386
|
|
337
387
|
, h_transfer_encoding_chunked
|
338
388
|
, h_connection_keep_alive
|
339
389
|
, h_connection_close
|
390
|
+
, h_connection_upgrade
|
340
391
|
};
|
341
392
|
|
342
393
|
enum http_host_state
|
@@ -349,6 +400,8 @@ enum http_host_state
|
|
349
400
|
, s_http_host
|
350
401
|
, s_http_host_v6
|
351
402
|
, s_http_host_v6_end
|
403
|
+
, s_http_host_v6_zone_start
|
404
|
+
, s_http_host_v6_zone
|
352
405
|
, s_http_host_port_start
|
353
406
|
, s_http_host_port
|
354
407
|
};
|
@@ -368,18 +421,26 @@ enum http_host_state
|
|
368
421
|
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
369
422
|
(c) == '$' || (c) == ',')
|
370
423
|
|
424
|
+
#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])
|
425
|
+
|
371
426
|
#if HTTP_PARSER_STRICT
|
372
|
-
#define TOKEN(c) (
|
427
|
+
#define TOKEN(c) STRICT_TOKEN(c)
|
373
428
|
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
374
429
|
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
|
375
430
|
#else
|
376
|
-
#define TOKEN(c)
|
431
|
+
#define TOKEN(c) tokens[(unsigned char)c]
|
377
432
|
#define IS_URL_CHAR(c) \
|
378
433
|
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
|
379
434
|
#define IS_HOST_CHAR(c) \
|
380
435
|
(IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
|
381
436
|
#endif
|
382
437
|
|
438
|
+
/**
|
439
|
+
* Verify that a char is a valid visible (printable) US-ASCII
|
440
|
+
* character or %x80-FF
|
441
|
+
**/
|
442
|
+
#define IS_HEADER_CHAR(ch) \
|
443
|
+
(ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
|
383
444
|
|
384
445
|
#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
|
385
446
|
|
@@ -481,7 +542,7 @@ parse_url_char(enum state s, const char ch)
|
|
481
542
|
return s_dead;
|
482
543
|
}
|
483
544
|
|
484
|
-
/*
|
545
|
+
/* fall through */
|
485
546
|
case s_req_server_start:
|
486
547
|
case s_req_server:
|
487
548
|
if (ch == '/') {
|
@@ -583,6 +644,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
583
644
|
const char *url_mark = 0;
|
584
645
|
const char *body_mark = 0;
|
585
646
|
const char *status_mark = 0;
|
647
|
+
enum state p_state = (enum state) parser->state;
|
648
|
+
const unsigned int lenient = parser->lenient_http_headers;
|
649
|
+
uint32_t nread = parser->nread;
|
586
650
|
|
587
651
|
/* We're in an error state. Don't bother doing anything. */
|
588
652
|
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
@@ -590,7 +654,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
590
654
|
}
|
591
655
|
|
592
656
|
if (len == 0) {
|
593
|
-
switch (
|
657
|
+
switch (CURRENT_STATE()) {
|
594
658
|
case s_body_identity_eof:
|
595
659
|
/* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
|
596
660
|
* we got paused.
|
@@ -611,11 +675,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
611
675
|
}
|
612
676
|
|
613
677
|
|
614
|
-
if (
|
678
|
+
if (CURRENT_STATE() == s_header_field)
|
615
679
|
header_field_mark = data;
|
616
|
-
if (
|
680
|
+
if (CURRENT_STATE() == s_header_value)
|
617
681
|
header_value_mark = data;
|
618
|
-
switch (
|
682
|
+
switch (CURRENT_STATE()) {
|
619
683
|
case s_req_path:
|
620
684
|
case s_req_schema:
|
621
685
|
case s_req_schema_slash:
|
@@ -632,38 +696,24 @@ size_t http_parser_execute (http_parser *parser,
|
|
632
696
|
case s_res_status:
|
633
697
|
status_mark = data;
|
634
698
|
break;
|
699
|
+
default:
|
700
|
+
break;
|
635
701
|
}
|
636
702
|
|
637
703
|
for (p=data; p != data + len; p++) {
|
638
704
|
ch = *p;
|
639
705
|
|
640
|
-
if (PARSING_HEADER(
|
641
|
-
|
642
|
-
/* Don't allow the total size of the HTTP headers (including the status
|
643
|
-
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
|
644
|
-
* embedders against denial-of-service attacks where the attacker feeds
|
645
|
-
* us a never-ending header that the embedder keeps buffering.
|
646
|
-
*
|
647
|
-
* This check is arguably the responsibility of embedders but we're doing
|
648
|
-
* it on the embedder's behalf because most won't bother and this way we
|
649
|
-
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
|
650
|
-
* than any reasonable request or response so this should never affect
|
651
|
-
* day-to-day operation.
|
652
|
-
*/
|
653
|
-
if (parser->nread > HTTP_MAX_HEADER_SIZE) {
|
654
|
-
SET_ERRNO(HPE_HEADER_OVERFLOW);
|
655
|
-
goto error;
|
656
|
-
}
|
657
|
-
}
|
706
|
+
if (PARSING_HEADER(CURRENT_STATE()))
|
707
|
+
COUNT_HEADER_SIZE(1);
|
658
708
|
|
659
|
-
|
660
|
-
switch (
|
709
|
+
reexecute:
|
710
|
+
switch (CURRENT_STATE()) {
|
661
711
|
|
662
712
|
case s_dead:
|
663
713
|
/* this state is used after a 'Connection: close' message
|
664
714
|
* the parser will error out if it reads another message
|
665
715
|
*/
|
666
|
-
if (ch == CR || ch == LF)
|
716
|
+
if (LIKELY(ch == CR || ch == LF))
|
667
717
|
break;
|
668
718
|
|
669
719
|
SET_ERRNO(HPE_CLOSED_CONNECTION);
|
@@ -677,13 +727,13 @@ size_t http_parser_execute (http_parser *parser,
|
|
677
727
|
parser->content_length = ULLONG_MAX;
|
678
728
|
|
679
729
|
if (ch == 'H') {
|
680
|
-
|
730
|
+
UPDATE_STATE(s_res_or_resp_H);
|
681
731
|
|
682
732
|
CALLBACK_NOTIFY(message_begin);
|
683
733
|
} else {
|
684
734
|
parser->type = HTTP_REQUEST;
|
685
|
-
|
686
|
-
|
735
|
+
UPDATE_STATE(s_start_req);
|
736
|
+
REEXECUTE();
|
687
737
|
}
|
688
738
|
|
689
739
|
break;
|
@@ -692,9 +742,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
692
742
|
case s_res_or_resp_H:
|
693
743
|
if (ch == 'T') {
|
694
744
|
parser->type = HTTP_RESPONSE;
|
695
|
-
|
745
|
+
UPDATE_STATE(s_res_HT);
|
696
746
|
} else {
|
697
|
-
if (ch != 'E') {
|
747
|
+
if (UNLIKELY(ch != 'E')) {
|
698
748
|
SET_ERRNO(HPE_INVALID_CONSTANT);
|
699
749
|
goto error;
|
700
750
|
}
|
@@ -702,27 +752,22 @@ size_t http_parser_execute (http_parser *parser,
|
|
702
752
|
parser->type = HTTP_REQUEST;
|
703
753
|
parser->method = HTTP_HEAD;
|
704
754
|
parser->index = 2;
|
705
|
-
|
755
|
+
UPDATE_STATE(s_req_method);
|
706
756
|
}
|
707
757
|
break;
|
708
758
|
|
709
759
|
case s_start_res:
|
710
760
|
{
|
761
|
+
if (ch == CR || ch == LF)
|
762
|
+
break;
|
711
763
|
parser->flags = 0;
|
712
764
|
parser->content_length = ULLONG_MAX;
|
713
765
|
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
case CR:
|
720
|
-
case LF:
|
721
|
-
break;
|
722
|
-
|
723
|
-
default:
|
724
|
-
SET_ERRNO(HPE_INVALID_CONSTANT);
|
725
|
-
goto error;
|
766
|
+
if (ch == 'H') {
|
767
|
+
UPDATE_STATE(s_res_H);
|
768
|
+
} else {
|
769
|
+
SET_ERRNO(HPE_INVALID_CONSTANT);
|
770
|
+
goto error;
|
726
771
|
}
|
727
772
|
|
728
773
|
CALLBACK_NOTIFY(message_begin);
|
@@ -731,90 +776,63 @@ size_t http_parser_execute (http_parser *parser,
|
|
731
776
|
|
732
777
|
case s_res_H:
|
733
778
|
STRICT_CHECK(ch != 'T');
|
734
|
-
|
779
|
+
UPDATE_STATE(s_res_HT);
|
735
780
|
break;
|
736
781
|
|
737
782
|
case s_res_HT:
|
738
783
|
STRICT_CHECK(ch != 'T');
|
739
|
-
|
784
|
+
UPDATE_STATE(s_res_HTT);
|
740
785
|
break;
|
741
786
|
|
742
787
|
case s_res_HTT:
|
743
788
|
STRICT_CHECK(ch != 'P');
|
744
|
-
|
789
|
+
UPDATE_STATE(s_res_HTTP);
|
745
790
|
break;
|
746
791
|
|
747
792
|
case s_res_HTTP:
|
748
793
|
STRICT_CHECK(ch != '/');
|
749
|
-
|
794
|
+
UPDATE_STATE(s_res_http_major);
|
750
795
|
break;
|
751
796
|
|
752
|
-
case
|
753
|
-
if (ch
|
797
|
+
case s_res_http_major:
|
798
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
754
799
|
SET_ERRNO(HPE_INVALID_VERSION);
|
755
800
|
goto error;
|
756
801
|
}
|
757
802
|
|
758
803
|
parser->http_major = ch - '0';
|
759
|
-
|
804
|
+
UPDATE_STATE(s_res_http_dot);
|
760
805
|
break;
|
761
806
|
|
762
|
-
|
763
|
-
case s_res_http_major:
|
807
|
+
case s_res_http_dot:
|
764
808
|
{
|
765
|
-
if (ch
|
766
|
-
parser->state = s_res_first_http_minor;
|
767
|
-
break;
|
768
|
-
}
|
769
|
-
|
770
|
-
if (!IS_NUM(ch)) {
|
771
|
-
SET_ERRNO(HPE_INVALID_VERSION);
|
772
|
-
goto error;
|
773
|
-
}
|
774
|
-
|
775
|
-
parser->http_major *= 10;
|
776
|
-
parser->http_major += ch - '0';
|
777
|
-
|
778
|
-
if (parser->http_major > 999) {
|
809
|
+
if (UNLIKELY(ch != '.')) {
|
779
810
|
SET_ERRNO(HPE_INVALID_VERSION);
|
780
811
|
goto error;
|
781
812
|
}
|
782
813
|
|
814
|
+
UPDATE_STATE(s_res_http_minor);
|
783
815
|
break;
|
784
816
|
}
|
785
817
|
|
786
|
-
|
787
|
-
|
788
|
-
if (!IS_NUM(ch)) {
|
818
|
+
case s_res_http_minor:
|
819
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
789
820
|
SET_ERRNO(HPE_INVALID_VERSION);
|
790
821
|
goto error;
|
791
822
|
}
|
792
823
|
|
793
824
|
parser->http_minor = ch - '0';
|
794
|
-
|
825
|
+
UPDATE_STATE(s_res_http_end);
|
795
826
|
break;
|
796
827
|
|
797
|
-
|
798
|
-
case s_res_http_minor:
|
828
|
+
case s_res_http_end:
|
799
829
|
{
|
800
|
-
if (ch
|
801
|
-
parser->state = s_res_first_status_code;
|
802
|
-
break;
|
803
|
-
}
|
804
|
-
|
805
|
-
if (!IS_NUM(ch)) {
|
806
|
-
SET_ERRNO(HPE_INVALID_VERSION);
|
807
|
-
goto error;
|
808
|
-
}
|
809
|
-
|
810
|
-
parser->http_minor *= 10;
|
811
|
-
parser->http_minor += ch - '0';
|
812
|
-
|
813
|
-
if (parser->http_minor > 999) {
|
830
|
+
if (UNLIKELY(ch != ' ')) {
|
814
831
|
SET_ERRNO(HPE_INVALID_VERSION);
|
815
832
|
goto error;
|
816
833
|
}
|
817
834
|
|
835
|
+
UPDATE_STATE(s_res_first_status_code);
|
818
836
|
break;
|
819
837
|
}
|
820
838
|
|
@@ -829,7 +847,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
829
847
|
goto error;
|
830
848
|
}
|
831
849
|
parser->status_code = ch - '0';
|
832
|
-
|
850
|
+
UPDATE_STATE(s_res_status_code);
|
833
851
|
break;
|
834
852
|
}
|
835
853
|
|
@@ -838,13 +856,12 @@ size_t http_parser_execute (http_parser *parser,
|
|
838
856
|
if (!IS_NUM(ch)) {
|
839
857
|
switch (ch) {
|
840
858
|
case ' ':
|
841
|
-
|
859
|
+
UPDATE_STATE(s_res_status_start);
|
842
860
|
break;
|
843
861
|
case CR:
|
844
|
-
parser->state = s_res_line_almost_done;
|
845
|
-
break;
|
846
862
|
case LF:
|
847
|
-
|
863
|
+
UPDATE_STATE(s_res_status_start);
|
864
|
+
REEXECUTE();
|
848
865
|
break;
|
849
866
|
default:
|
850
867
|
SET_ERRNO(HPE_INVALID_STATUS);
|
@@ -856,7 +873,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
856
873
|
parser->status_code *= 10;
|
857
874
|
parser->status_code += ch - '0';
|
858
875
|
|
859
|
-
if (parser->status_code > 999) {
|
876
|
+
if (UNLIKELY(parser->status_code > 999)) {
|
860
877
|
SET_ERRNO(HPE_INVALID_STATUS);
|
861
878
|
goto error;
|
862
879
|
}
|
@@ -866,31 +883,25 @@ size_t http_parser_execute (http_parser *parser,
|
|
866
883
|
|
867
884
|
case s_res_status_start:
|
868
885
|
{
|
869
|
-
if (ch == CR) {
|
870
|
-
parser->state = s_res_line_almost_done;
|
871
|
-
break;
|
872
|
-
}
|
873
|
-
|
874
|
-
if (ch == LF) {
|
875
|
-
parser->state = s_header_field_start;
|
876
|
-
break;
|
877
|
-
}
|
878
|
-
|
879
886
|
MARK(status);
|
880
|
-
|
887
|
+
UPDATE_STATE(s_res_status);
|
881
888
|
parser->index = 0;
|
889
|
+
|
890
|
+
if (ch == CR || ch == LF)
|
891
|
+
REEXECUTE();
|
892
|
+
|
882
893
|
break;
|
883
894
|
}
|
884
895
|
|
885
896
|
case s_res_status:
|
886
897
|
if (ch == CR) {
|
887
|
-
|
898
|
+
UPDATE_STATE(s_res_line_almost_done);
|
888
899
|
CALLBACK_DATA(status);
|
889
900
|
break;
|
890
901
|
}
|
891
902
|
|
892
903
|
if (ch == LF) {
|
893
|
-
|
904
|
+
UPDATE_STATE(s_header_field_start);
|
894
905
|
CALLBACK_DATA(status);
|
895
906
|
break;
|
896
907
|
}
|
@@ -899,7 +910,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
899
910
|
|
900
911
|
case s_res_line_almost_done:
|
901
912
|
STRICT_CHECK(ch != LF);
|
902
|
-
|
913
|
+
UPDATE_STATE(s_header_field_start);
|
903
914
|
break;
|
904
915
|
|
905
916
|
case s_start_req:
|
@@ -909,7 +920,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
909
920
|
parser->flags = 0;
|
910
921
|
parser->content_length = ULLONG_MAX;
|
911
922
|
|
912
|
-
if (!IS_ALPHA(ch)) {
|
923
|
+
if (UNLIKELY(!IS_ALPHA(ch))) {
|
913
924
|
SET_ERRNO(HPE_INVALID_METHOD);
|
914
925
|
goto error;
|
915
926
|
}
|
@@ -917,26 +928,28 @@ size_t http_parser_execute (http_parser *parser,
|
|
917
928
|
parser->method = (enum http_method) 0;
|
918
929
|
parser->index = 1;
|
919
930
|
switch (ch) {
|
931
|
+
case 'A': parser->method = HTTP_ACL; break;
|
932
|
+
case 'B': parser->method = HTTP_BIND; break;
|
920
933
|
case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
|
921
934
|
case 'D': parser->method = HTTP_DELETE; break;
|
922
935
|
case 'G': parser->method = HTTP_GET; break;
|
923
936
|
case 'H': parser->method = HTTP_HEAD; break;
|
924
|
-
case 'L': parser->method = HTTP_LOCK; break;
|
925
|
-
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
|
937
|
+
case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
|
938
|
+
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
|
926
939
|
case 'N': parser->method = HTTP_NOTIFY; break;
|
927
940
|
case 'O': parser->method = HTTP_OPTIONS; break;
|
928
941
|
case 'P': parser->method = HTTP_POST;
|
929
942
|
/* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
|
930
943
|
break;
|
931
|
-
case 'R': parser->method = HTTP_REPORT; break;
|
932
|
-
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
|
944
|
+
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
945
|
+
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH, SOURCE */ break;
|
933
946
|
case 'T': parser->method = HTTP_TRACE; break;
|
934
|
-
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
|
947
|
+
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
935
948
|
default:
|
936
949
|
SET_ERRNO(HPE_INVALID_METHOD);
|
937
950
|
goto error;
|
938
951
|
}
|
939
|
-
|
952
|
+
UPDATE_STATE(s_req_method);
|
940
953
|
|
941
954
|
CALLBACK_NOTIFY(message_begin);
|
942
955
|
|
@@ -946,77 +959,47 @@ size_t http_parser_execute (http_parser *parser,
|
|
946
959
|
case s_req_method:
|
947
960
|
{
|
948
961
|
const char *matcher;
|
949
|
-
if (ch == '\0') {
|
962
|
+
if (UNLIKELY(ch == '\0')) {
|
950
963
|
SET_ERRNO(HPE_INVALID_METHOD);
|
951
964
|
goto error;
|
952
965
|
}
|
953
966
|
|
954
967
|
matcher = method_strings[parser->method];
|
955
968
|
if (ch == ' ' && matcher[parser->index] == '\0') {
|
956
|
-
|
969
|
+
UPDATE_STATE(s_req_spaces_before_url);
|
957
970
|
} else if (ch == matcher[parser->index]) {
|
958
971
|
; /* nada */
|
959
|
-
} else if (
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
973
|
-
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
|
978
|
-
|
979
|
-
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
}
|
988
|
-
} else if (parser->index == 1 && parser->method == HTTP_POST) {
|
989
|
-
if (ch == 'R') {
|
990
|
-
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
991
|
-
} else if (ch == 'U') {
|
992
|
-
parser->method = HTTP_PUT; /* or HTTP_PURGE */
|
993
|
-
} else if (ch == 'A') {
|
994
|
-
parser->method = HTTP_PATCH;
|
995
|
-
} else {
|
996
|
-
SET_ERRNO(HPE_INVALID_METHOD);
|
997
|
-
goto error;
|
998
|
-
}
|
999
|
-
} else if (parser->index == 2) {
|
1000
|
-
if (parser->method == HTTP_PUT) {
|
1001
|
-
if (ch == 'R') {
|
1002
|
-
parser->method = HTTP_PURGE;
|
1003
|
-
} else {
|
1004
|
-
SET_ERRNO(HPE_INVALID_METHOD);
|
1005
|
-
goto error;
|
1006
|
-
}
|
1007
|
-
} else if (parser->method == HTTP_UNLOCK) {
|
1008
|
-
if (ch == 'S') {
|
1009
|
-
parser->method = HTTP_UNSUBSCRIBE;
|
1010
|
-
} else {
|
972
|
+
} else if ((ch >= 'A' && ch <= 'Z') || ch == '-') {
|
973
|
+
|
974
|
+
switch (parser->method << 16 | parser->index << 8 | ch) {
|
975
|
+
#define XX(meth, pos, ch, new_meth) \
|
976
|
+
case (HTTP_##meth << 16 | pos << 8 | ch): \
|
977
|
+
parser->method = HTTP_##new_meth; break;
|
978
|
+
|
979
|
+
XX(POST, 1, 'U', PUT)
|
980
|
+
XX(POST, 1, 'A', PATCH)
|
981
|
+
XX(POST, 1, 'R', PROPFIND)
|
982
|
+
XX(PUT, 2, 'R', PURGE)
|
983
|
+
XX(CONNECT, 1, 'H', CHECKOUT)
|
984
|
+
XX(CONNECT, 2, 'P', COPY)
|
985
|
+
XX(MKCOL, 1, 'O', MOVE)
|
986
|
+
XX(MKCOL, 1, 'E', MERGE)
|
987
|
+
XX(MKCOL, 1, '-', MSEARCH)
|
988
|
+
XX(MKCOL, 2, 'A', MKACTIVITY)
|
989
|
+
XX(MKCOL, 3, 'A', MKCALENDAR)
|
990
|
+
XX(SUBSCRIBE, 1, 'E', SEARCH)
|
991
|
+
XX(SUBSCRIBE, 1, 'O', SOURCE)
|
992
|
+
XX(REPORT, 2, 'B', REBIND)
|
993
|
+
XX(PROPFIND, 4, 'P', PROPPATCH)
|
994
|
+
XX(LOCK, 1, 'I', LINK)
|
995
|
+
XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
|
996
|
+
XX(UNLOCK, 2, 'B', UNBIND)
|
997
|
+
XX(UNLOCK, 3, 'I', UNLINK)
|
998
|
+
#undef XX
|
999
|
+
default:
|
1011
1000
|
SET_ERRNO(HPE_INVALID_METHOD);
|
1012
1001
|
goto error;
|
1013
|
-
}
|
1014
|
-
} else {
|
1015
|
-
SET_ERRNO(HPE_INVALID_METHOD);
|
1016
|
-
goto error;
|
1017
1002
|
}
|
1018
|
-
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
1019
|
-
parser->method = HTTP_PROPPATCH;
|
1020
1003
|
} else {
|
1021
1004
|
SET_ERRNO(HPE_INVALID_METHOD);
|
1022
1005
|
goto error;
|
@@ -1032,11 +1015,11 @@ size_t http_parser_execute (http_parser *parser,
|
|
1032
1015
|
|
1033
1016
|
MARK(url);
|
1034
1017
|
if (parser->method == HTTP_CONNECT) {
|
1035
|
-
|
1018
|
+
UPDATE_STATE(s_req_server_start);
|
1036
1019
|
}
|
1037
1020
|
|
1038
|
-
|
1039
|
-
if (
|
1021
|
+
UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
|
1022
|
+
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
|
1040
1023
|
SET_ERRNO(HPE_INVALID_URL);
|
1041
1024
|
goto error;
|
1042
1025
|
}
|
@@ -1057,8 +1040,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
1057
1040
|
SET_ERRNO(HPE_INVALID_URL);
|
1058
1041
|
goto error;
|
1059
1042
|
default:
|
1060
|
-
|
1061
|
-
if (
|
1043
|
+
UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
|
1044
|
+
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
|
1062
1045
|
SET_ERRNO(HPE_INVALID_URL);
|
1063
1046
|
goto error;
|
1064
1047
|
}
|
@@ -1077,21 +1060,21 @@ size_t http_parser_execute (http_parser *parser,
|
|
1077
1060
|
{
|
1078
1061
|
switch (ch) {
|
1079
1062
|
case ' ':
|
1080
|
-
|
1063
|
+
UPDATE_STATE(s_req_http_start);
|
1081
1064
|
CALLBACK_DATA(url);
|
1082
1065
|
break;
|
1083
1066
|
case CR:
|
1084
1067
|
case LF:
|
1085
1068
|
parser->http_major = 0;
|
1086
1069
|
parser->http_minor = 9;
|
1087
|
-
|
1070
|
+
UPDATE_STATE((ch == CR) ?
|
1088
1071
|
s_req_line_almost_done :
|
1089
|
-
s_header_field_start;
|
1072
|
+
s_header_field_start);
|
1090
1073
|
CALLBACK_DATA(url);
|
1091
1074
|
break;
|
1092
1075
|
default:
|
1093
|
-
|
1094
|
-
if (
|
1076
|
+
UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
|
1077
|
+
if (UNLIKELY(CURRENT_STATE() == s_dead)) {
|
1095
1078
|
SET_ERRNO(HPE_INVALID_URL);
|
1096
1079
|
goto error;
|
1097
1080
|
}
|
@@ -1102,7 +1085,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1102
1085
|
case s_req_http_start:
|
1103
1086
|
switch (ch) {
|
1104
1087
|
case 'H':
|
1105
|
-
|
1088
|
+
UPDATE_STATE(s_req_http_H);
|
1106
1089
|
break;
|
1107
1090
|
case ' ':
|
1108
1091
|
break;
|
@@ -1114,130 +1097,101 @@ size_t http_parser_execute (http_parser *parser,
|
|
1114
1097
|
|
1115
1098
|
case s_req_http_H:
|
1116
1099
|
STRICT_CHECK(ch != 'T');
|
1117
|
-
|
1100
|
+
UPDATE_STATE(s_req_http_HT);
|
1118
1101
|
break;
|
1119
1102
|
|
1120
1103
|
case s_req_http_HT:
|
1121
1104
|
STRICT_CHECK(ch != 'T');
|
1122
|
-
|
1105
|
+
UPDATE_STATE(s_req_http_HTT);
|
1123
1106
|
break;
|
1124
1107
|
|
1125
1108
|
case s_req_http_HTT:
|
1126
1109
|
STRICT_CHECK(ch != 'P');
|
1127
|
-
|
1110
|
+
UPDATE_STATE(s_req_http_HTTP);
|
1128
1111
|
break;
|
1129
1112
|
|
1130
1113
|
case s_req_http_HTTP:
|
1131
1114
|
STRICT_CHECK(ch != '/');
|
1132
|
-
|
1115
|
+
UPDATE_STATE(s_req_http_major);
|
1133
1116
|
break;
|
1134
1117
|
|
1135
|
-
|
1136
|
-
|
1137
|
-
if (ch < '1' || ch > '9') {
|
1118
|
+
case s_req_http_major:
|
1119
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
1138
1120
|
SET_ERRNO(HPE_INVALID_VERSION);
|
1139
1121
|
goto error;
|
1140
1122
|
}
|
1141
1123
|
|
1142
1124
|
parser->http_major = ch - '0';
|
1143
|
-
|
1125
|
+
UPDATE_STATE(s_req_http_dot);
|
1144
1126
|
break;
|
1145
1127
|
|
1146
|
-
|
1147
|
-
case s_req_http_major:
|
1128
|
+
case s_req_http_dot:
|
1148
1129
|
{
|
1149
|
-
if (ch
|
1150
|
-
parser->state = s_req_first_http_minor;
|
1151
|
-
break;
|
1152
|
-
}
|
1153
|
-
|
1154
|
-
if (!IS_NUM(ch)) {
|
1155
|
-
SET_ERRNO(HPE_INVALID_VERSION);
|
1156
|
-
goto error;
|
1157
|
-
}
|
1158
|
-
|
1159
|
-
parser->http_major *= 10;
|
1160
|
-
parser->http_major += ch - '0';
|
1161
|
-
|
1162
|
-
if (parser->http_major > 999) {
|
1130
|
+
if (UNLIKELY(ch != '.')) {
|
1163
1131
|
SET_ERRNO(HPE_INVALID_VERSION);
|
1164
1132
|
goto error;
|
1165
1133
|
}
|
1166
1134
|
|
1135
|
+
UPDATE_STATE(s_req_http_minor);
|
1167
1136
|
break;
|
1168
1137
|
}
|
1169
1138
|
|
1170
|
-
|
1171
|
-
|
1172
|
-
if (!IS_NUM(ch)) {
|
1139
|
+
case s_req_http_minor:
|
1140
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
1173
1141
|
SET_ERRNO(HPE_INVALID_VERSION);
|
1174
1142
|
goto error;
|
1175
1143
|
}
|
1176
1144
|
|
1177
1145
|
parser->http_minor = ch - '0';
|
1178
|
-
|
1146
|
+
UPDATE_STATE(s_req_http_end);
|
1179
1147
|
break;
|
1180
1148
|
|
1181
|
-
|
1182
|
-
case s_req_http_minor:
|
1149
|
+
case s_req_http_end:
|
1183
1150
|
{
|
1184
1151
|
if (ch == CR) {
|
1185
|
-
|
1152
|
+
UPDATE_STATE(s_req_line_almost_done);
|
1186
1153
|
break;
|
1187
1154
|
}
|
1188
1155
|
|
1189
1156
|
if (ch == LF) {
|
1190
|
-
|
1157
|
+
UPDATE_STATE(s_header_field_start);
|
1191
1158
|
break;
|
1192
1159
|
}
|
1193
1160
|
|
1194
|
-
|
1195
|
-
|
1196
|
-
if (!IS_NUM(ch)) {
|
1197
|
-
SET_ERRNO(HPE_INVALID_VERSION);
|
1198
|
-
goto error;
|
1199
|
-
}
|
1200
|
-
|
1201
|
-
parser->http_minor *= 10;
|
1202
|
-
parser->http_minor += ch - '0';
|
1203
|
-
|
1204
|
-
if (parser->http_minor > 999) {
|
1205
|
-
SET_ERRNO(HPE_INVALID_VERSION);
|
1206
|
-
goto error;
|
1207
|
-
}
|
1208
|
-
|
1161
|
+
SET_ERRNO(HPE_INVALID_VERSION);
|
1162
|
+
goto error;
|
1209
1163
|
break;
|
1210
1164
|
}
|
1211
1165
|
|
1212
1166
|
/* end of request line */
|
1213
1167
|
case s_req_line_almost_done:
|
1214
1168
|
{
|
1215
|
-
if (ch != LF) {
|
1169
|
+
if (UNLIKELY(ch != LF)) {
|
1216
1170
|
SET_ERRNO(HPE_LF_EXPECTED);
|
1217
1171
|
goto error;
|
1218
1172
|
}
|
1219
1173
|
|
1220
|
-
|
1174
|
+
UPDATE_STATE(s_header_field_start);
|
1221
1175
|
break;
|
1222
1176
|
}
|
1223
1177
|
|
1224
1178
|
case s_header_field_start:
|
1225
1179
|
{
|
1226
1180
|
if (ch == CR) {
|
1227
|
-
|
1181
|
+
UPDATE_STATE(s_headers_almost_done);
|
1228
1182
|
break;
|
1229
1183
|
}
|
1230
1184
|
|
1231
1185
|
if (ch == LF) {
|
1232
1186
|
/* they might be just sending \n instead of \r\n so this would be
|
1233
1187
|
* the second \n to denote the end of headers*/
|
1234
|
-
|
1235
|
-
|
1188
|
+
UPDATE_STATE(s_headers_almost_done);
|
1189
|
+
REEXECUTE();
|
1236
1190
|
}
|
1237
1191
|
|
1238
1192
|
c = TOKEN(ch);
|
1239
1193
|
|
1240
|
-
if (!c) {
|
1194
|
+
if (UNLIKELY(!c)) {
|
1241
1195
|
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
1242
1196
|
goto error;
|
1243
1197
|
}
|
@@ -1245,7 +1199,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1245
1199
|
MARK(header_field);
|
1246
1200
|
|
1247
1201
|
parser->index = 0;
|
1248
|
-
|
1202
|
+
UPDATE_STATE(s_header_field);
|
1249
1203
|
|
1250
1204
|
switch (c) {
|
1251
1205
|
case 'c':
|
@@ -1273,12 +1227,23 @@ size_t http_parser_execute (http_parser *parser,
|
|
1273
1227
|
|
1274
1228
|
case s_header_field:
|
1275
1229
|
{
|
1276
|
-
|
1230
|
+
const char* start = p;
|
1231
|
+
for (; p != data + len; p++) {
|
1232
|
+
ch = *p;
|
1233
|
+
c = TOKEN(ch);
|
1234
|
+
|
1235
|
+
if (!c)
|
1236
|
+
break;
|
1277
1237
|
|
1278
|
-
if (c) {
|
1279
1238
|
switch (parser->header_state) {
|
1280
|
-
case h_general:
|
1239
|
+
case h_general: {
|
1240
|
+
size_t limit = data + len - p;
|
1241
|
+
limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
|
1242
|
+
while (p+1 < data + limit && TOKEN(p[1])) {
|
1243
|
+
p++;
|
1244
|
+
}
|
1281
1245
|
break;
|
1246
|
+
}
|
1282
1247
|
|
1283
1248
|
case h_C:
|
1284
1249
|
parser->index++;
|
@@ -1376,23 +1341,18 @@ size_t http_parser_execute (http_parser *parser,
|
|
1376
1341
|
assert(0 && "Unknown header_state");
|
1377
1342
|
break;
|
1378
1343
|
}
|
1379
|
-
break;
|
1380
1344
|
}
|
1381
1345
|
|
1382
|
-
if (
|
1383
|
-
|
1384
|
-
|
1346
|
+
if (p == data + len) {
|
1347
|
+
--p;
|
1348
|
+
COUNT_HEADER_SIZE(p - start);
|
1385
1349
|
break;
|
1386
1350
|
}
|
1387
1351
|
|
1388
|
-
|
1389
|
-
parser->state = s_header_almost_done;
|
1390
|
-
CALLBACK_DATA(header_field);
|
1391
|
-
break;
|
1392
|
-
}
|
1352
|
+
COUNT_HEADER_SIZE(p - start);
|
1393
1353
|
|
1394
|
-
if (ch ==
|
1395
|
-
|
1354
|
+
if (ch == ':') {
|
1355
|
+
UPDATE_STATE(s_header_value_discard_ws);
|
1396
1356
|
CALLBACK_DATA(header_field);
|
1397
1357
|
break;
|
1398
1358
|
}
|
@@ -1401,28 +1361,28 @@ size_t http_parser_execute (http_parser *parser,
|
|
1401
1361
|
goto error;
|
1402
1362
|
}
|
1403
1363
|
|
1404
|
-
case
|
1405
|
-
{
|
1364
|
+
case s_header_value_discard_ws:
|
1406
1365
|
if (ch == ' ' || ch == '\t') break;
|
1407
1366
|
|
1408
|
-
MARK(header_value);
|
1409
|
-
|
1410
|
-
parser->state = s_header_value;
|
1411
|
-
parser->index = 0;
|
1412
|
-
|
1413
1367
|
if (ch == CR) {
|
1414
|
-
|
1415
|
-
parser->state = s_header_almost_done;
|
1416
|
-
CALLBACK_DATA(header_value);
|
1368
|
+
UPDATE_STATE(s_header_value_discard_ws_almost_done);
|
1417
1369
|
break;
|
1418
1370
|
}
|
1419
1371
|
|
1420
1372
|
if (ch == LF) {
|
1421
|
-
|
1422
|
-
CALLBACK_DATA(header_value);
|
1373
|
+
UPDATE_STATE(s_header_value_discard_lws);
|
1423
1374
|
break;
|
1424
1375
|
}
|
1425
1376
|
|
1377
|
+
/* fall through */
|
1378
|
+
|
1379
|
+
case s_header_value_start:
|
1380
|
+
{
|
1381
|
+
MARK(header_value);
|
1382
|
+
|
1383
|
+
UPDATE_STATE(s_header_value);
|
1384
|
+
parser->index = 0;
|
1385
|
+
|
1426
1386
|
c = LOWER(ch);
|
1427
1387
|
|
1428
1388
|
switch (parser->header_state) {
|
@@ -1441,12 +1401,19 @@ size_t http_parser_execute (http_parser *parser,
|
|
1441
1401
|
break;
|
1442
1402
|
|
1443
1403
|
case h_content_length:
|
1444
|
-
if (!IS_NUM(ch)) {
|
1404
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
1445
1405
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1446
1406
|
goto error;
|
1447
1407
|
}
|
1448
1408
|
|
1409
|
+
if (parser->flags & F_CONTENTLENGTH) {
|
1410
|
+
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
1411
|
+
goto error;
|
1412
|
+
}
|
1413
|
+
|
1414
|
+
parser->flags |= F_CONTENTLENGTH;
|
1449
1415
|
parser->content_length = ch - '0';
|
1416
|
+
parser->header_state = h_content_length_num;
|
1450
1417
|
break;
|
1451
1418
|
|
1452
1419
|
case h_connection:
|
@@ -1456,11 +1423,17 @@ size_t http_parser_execute (http_parser *parser,
|
|
1456
1423
|
/* looking for 'Connection: close' */
|
1457
1424
|
} else if (c == 'c') {
|
1458
1425
|
parser->header_state = h_matching_connection_close;
|
1426
|
+
} else if (c == 'u') {
|
1427
|
+
parser->header_state = h_matching_connection_upgrade;
|
1459
1428
|
} else {
|
1460
|
-
parser->header_state =
|
1429
|
+
parser->header_state = h_matching_connection_token;
|
1461
1430
|
}
|
1462
1431
|
break;
|
1463
1432
|
|
1433
|
+
/* Multi-value `Connection` header */
|
1434
|
+
case h_matching_connection_token_start:
|
1435
|
+
break;
|
1436
|
+
|
1464
1437
|
default:
|
1465
1438
|
parser->header_state = h_general;
|
1466
1439
|
break;
|
@@ -1470,107 +1443,227 @@ size_t http_parser_execute (http_parser *parser,
|
|
1470
1443
|
|
1471
1444
|
case s_header_value:
|
1472
1445
|
{
|
1446
|
+
const char* start = p;
|
1447
|
+
enum header_states h_state = (enum header_states) parser->header_state;
|
1448
|
+
for (; p != data + len; p++) {
|
1449
|
+
ch = *p;
|
1450
|
+
if (ch == CR) {
|
1451
|
+
UPDATE_STATE(s_header_almost_done);
|
1452
|
+
parser->header_state = h_state;
|
1453
|
+
CALLBACK_DATA(header_value);
|
1454
|
+
break;
|
1455
|
+
}
|
1473
1456
|
|
1474
|
-
|
1475
|
-
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1457
|
+
if (ch == LF) {
|
1458
|
+
UPDATE_STATE(s_header_almost_done);
|
1459
|
+
COUNT_HEADER_SIZE(p - start);
|
1460
|
+
parser->header_state = h_state;
|
1461
|
+
CALLBACK_DATA_NOADVANCE(header_value);
|
1462
|
+
REEXECUTE();
|
1463
|
+
}
|
1479
1464
|
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1484
|
-
}
|
1465
|
+
if (!lenient && !IS_HEADER_CHAR(ch)) {
|
1466
|
+
SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
|
1467
|
+
goto error;
|
1468
|
+
}
|
1485
1469
|
|
1486
|
-
|
1470
|
+
c = LOWER(ch);
|
1487
1471
|
|
1488
|
-
|
1489
|
-
|
1490
|
-
|
1472
|
+
switch (h_state) {
|
1473
|
+
case h_general:
|
1474
|
+
{
|
1475
|
+
const char* p_cr;
|
1476
|
+
const char* p_lf;
|
1477
|
+
size_t limit = data + len - p;
|
1478
|
+
|
1479
|
+
limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
|
1480
|
+
|
1481
|
+
p_cr = (const char*) memchr(p, CR, limit);
|
1482
|
+
p_lf = (const char*) memchr(p, LF, limit);
|
1483
|
+
if (p_cr != NULL) {
|
1484
|
+
if (p_lf != NULL && p_cr >= p_lf)
|
1485
|
+
p = p_lf;
|
1486
|
+
else
|
1487
|
+
p = p_cr;
|
1488
|
+
} else if (UNLIKELY(p_lf != NULL)) {
|
1489
|
+
p = p_lf;
|
1490
|
+
} else {
|
1491
|
+
p = data + len;
|
1492
|
+
}
|
1493
|
+
--p;
|
1494
|
+
break;
|
1495
|
+
}
|
1491
1496
|
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1495
|
-
|
1497
|
+
case h_connection:
|
1498
|
+
case h_transfer_encoding:
|
1499
|
+
assert(0 && "Shouldn't get here.");
|
1500
|
+
break;
|
1496
1501
|
|
1497
|
-
|
1498
|
-
|
1499
|
-
|
1502
|
+
case h_content_length:
|
1503
|
+
if (ch == ' ') break;
|
1504
|
+
h_state = h_content_length_num;
|
1505
|
+
/* fall through */
|
1500
1506
|
|
1501
|
-
|
1507
|
+
case h_content_length_num:
|
1508
|
+
{
|
1509
|
+
uint64_t t;
|
1502
1510
|
|
1503
|
-
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1511
|
+
if (ch == ' ') {
|
1512
|
+
h_state = h_content_length_ws;
|
1513
|
+
break;
|
1514
|
+
}
|
1515
|
+
|
1516
|
+
if (UNLIKELY(!IS_NUM(ch))) {
|
1517
|
+
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1518
|
+
parser->header_state = h_state;
|
1519
|
+
goto error;
|
1520
|
+
}
|
1521
|
+
|
1522
|
+
t = parser->content_length;
|
1523
|
+
t *= 10;
|
1524
|
+
t += ch - '0';
|
1507
1525
|
|
1508
|
-
|
1509
|
-
|
1510
|
-
|
1526
|
+
/* Overflow? Test against a conservative limit for simplicity. */
|
1527
|
+
if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
|
1528
|
+
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1529
|
+
parser->header_state = h_state;
|
1530
|
+
goto error;
|
1531
|
+
}
|
1511
1532
|
|
1512
|
-
|
1513
|
-
|
1533
|
+
parser->content_length = t;
|
1534
|
+
break;
|
1535
|
+
}
|
1536
|
+
|
1537
|
+
case h_content_length_ws:
|
1538
|
+
if (ch == ' ') break;
|
1514
1539
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1540
|
+
parser->header_state = h_state;
|
1515
1541
|
goto error;
|
1516
|
-
}
|
1517
1542
|
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1543
|
+
/* Transfer-Encoding: chunked */
|
1544
|
+
case h_matching_transfer_encoding_chunked:
|
1545
|
+
parser->index++;
|
1546
|
+
if (parser->index > sizeof(CHUNKED)-1
|
1547
|
+
|| c != CHUNKED[parser->index]) {
|
1548
|
+
h_state = h_general;
|
1549
|
+
} else if (parser->index == sizeof(CHUNKED)-2) {
|
1550
|
+
h_state = h_transfer_encoding_chunked;
|
1551
|
+
}
|
1552
|
+
break;
|
1521
1553
|
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
|
1554
|
+
case h_matching_connection_token_start:
|
1555
|
+
/* looking for 'Connection: keep-alive' */
|
1556
|
+
if (c == 'k') {
|
1557
|
+
h_state = h_matching_connection_keep_alive;
|
1558
|
+
/* looking for 'Connection: close' */
|
1559
|
+
} else if (c == 'c') {
|
1560
|
+
h_state = h_matching_connection_close;
|
1561
|
+
} else if (c == 'u') {
|
1562
|
+
h_state = h_matching_connection_upgrade;
|
1563
|
+
} else if (STRICT_TOKEN(c)) {
|
1564
|
+
h_state = h_matching_connection_token;
|
1565
|
+
} else if (c == ' ' || c == '\t') {
|
1566
|
+
/* Skip lws */
|
1567
|
+
} else {
|
1568
|
+
h_state = h_general;
|
1569
|
+
}
|
1570
|
+
break;
|
1532
1571
|
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1542
|
-
|
1572
|
+
/* looking for 'Connection: keep-alive' */
|
1573
|
+
case h_matching_connection_keep_alive:
|
1574
|
+
parser->index++;
|
1575
|
+
if (parser->index > sizeof(KEEP_ALIVE)-1
|
1576
|
+
|| c != KEEP_ALIVE[parser->index]) {
|
1577
|
+
h_state = h_matching_connection_token;
|
1578
|
+
} else if (parser->index == sizeof(KEEP_ALIVE)-2) {
|
1579
|
+
h_state = h_connection_keep_alive;
|
1580
|
+
}
|
1581
|
+
break;
|
1543
1582
|
|
1544
|
-
|
1545
|
-
|
1546
|
-
|
1547
|
-
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1583
|
+
/* looking for 'Connection: close' */
|
1584
|
+
case h_matching_connection_close:
|
1585
|
+
parser->index++;
|
1586
|
+
if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
|
1587
|
+
h_state = h_matching_connection_token;
|
1588
|
+
} else if (parser->index == sizeof(CLOSE)-2) {
|
1589
|
+
h_state = h_connection_close;
|
1590
|
+
}
|
1591
|
+
break;
|
1553
1592
|
|
1554
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
1593
|
+
/* looking for 'Connection: upgrade' */
|
1594
|
+
case h_matching_connection_upgrade:
|
1595
|
+
parser->index++;
|
1596
|
+
if (parser->index > sizeof(UPGRADE) - 1 ||
|
1597
|
+
c != UPGRADE[parser->index]) {
|
1598
|
+
h_state = h_matching_connection_token;
|
1599
|
+
} else if (parser->index == sizeof(UPGRADE)-2) {
|
1600
|
+
h_state = h_connection_upgrade;
|
1601
|
+
}
|
1602
|
+
break;
|
1559
1603
|
|
1560
|
-
|
1561
|
-
|
1562
|
-
|
1563
|
-
|
1604
|
+
case h_matching_connection_token:
|
1605
|
+
if (ch == ',') {
|
1606
|
+
h_state = h_matching_connection_token_start;
|
1607
|
+
parser->index = 0;
|
1608
|
+
}
|
1609
|
+
break;
|
1610
|
+
|
1611
|
+
case h_transfer_encoding_chunked:
|
1612
|
+
if (ch != ' ') h_state = h_general;
|
1613
|
+
break;
|
1614
|
+
|
1615
|
+
case h_connection_keep_alive:
|
1616
|
+
case h_connection_close:
|
1617
|
+
case h_connection_upgrade:
|
1618
|
+
if (ch == ',') {
|
1619
|
+
if (h_state == h_connection_keep_alive) {
|
1620
|
+
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
1621
|
+
} else if (h_state == h_connection_close) {
|
1622
|
+
parser->flags |= F_CONNECTION_CLOSE;
|
1623
|
+
} else if (h_state == h_connection_upgrade) {
|
1624
|
+
parser->flags |= F_CONNECTION_UPGRADE;
|
1625
|
+
}
|
1626
|
+
h_state = h_matching_connection_token_start;
|
1627
|
+
parser->index = 0;
|
1628
|
+
} else if (ch != ' ') {
|
1629
|
+
h_state = h_matching_connection_token;
|
1630
|
+
}
|
1631
|
+
break;
|
1632
|
+
|
1633
|
+
default:
|
1634
|
+
UPDATE_STATE(s_header_value);
|
1635
|
+
h_state = h_general;
|
1636
|
+
break;
|
1637
|
+
}
|
1564
1638
|
}
|
1639
|
+
parser->header_state = h_state;
|
1640
|
+
|
1641
|
+
if (p == data + len)
|
1642
|
+
--p;
|
1643
|
+
|
1644
|
+
COUNT_HEADER_SIZE(p - start);
|
1565
1645
|
break;
|
1566
1646
|
}
|
1567
1647
|
|
1568
1648
|
case s_header_almost_done:
|
1569
1649
|
{
|
1570
|
-
|
1650
|
+
if (UNLIKELY(ch != LF)) {
|
1651
|
+
SET_ERRNO(HPE_LF_EXPECTED);
|
1652
|
+
goto error;
|
1653
|
+
}
|
1571
1654
|
|
1572
|
-
|
1655
|
+
UPDATE_STATE(s_header_value_lws);
|
1656
|
+
break;
|
1657
|
+
}
|
1573
1658
|
|
1659
|
+
case s_header_value_lws:
|
1660
|
+
{
|
1661
|
+
if (ch == ' ' || ch == '\t') {
|
1662
|
+
UPDATE_STATE(s_header_value_start);
|
1663
|
+
REEXECUTE();
|
1664
|
+
}
|
1665
|
+
|
1666
|
+
/* finished the header */
|
1574
1667
|
switch (parser->header_state) {
|
1575
1668
|
case h_connection_keep_alive:
|
1576
1669
|
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
@@ -1581,23 +1674,53 @@ size_t http_parser_execute (http_parser *parser,
|
|
1581
1674
|
case h_transfer_encoding_chunked:
|
1582
1675
|
parser->flags |= F_CHUNKED;
|
1583
1676
|
break;
|
1677
|
+
case h_connection_upgrade:
|
1678
|
+
parser->flags |= F_CONNECTION_UPGRADE;
|
1679
|
+
break;
|
1584
1680
|
default:
|
1585
1681
|
break;
|
1586
1682
|
}
|
1587
1683
|
|
1684
|
+
UPDATE_STATE(s_header_field_start);
|
1685
|
+
REEXECUTE();
|
1686
|
+
}
|
1687
|
+
|
1688
|
+
case s_header_value_discard_ws_almost_done:
|
1689
|
+
{
|
1690
|
+
STRICT_CHECK(ch != LF);
|
1691
|
+
UPDATE_STATE(s_header_value_discard_lws);
|
1588
1692
|
break;
|
1589
1693
|
}
|
1590
1694
|
|
1591
|
-
case
|
1695
|
+
case s_header_value_discard_lws:
|
1592
1696
|
{
|
1593
|
-
if (ch == ' ' || ch == '\t')
|
1594
|
-
|
1595
|
-
|
1596
|
-
{
|
1597
|
-
parser->
|
1598
|
-
|
1697
|
+
if (ch == ' ' || ch == '\t') {
|
1698
|
+
UPDATE_STATE(s_header_value_discard_ws);
|
1699
|
+
break;
|
1700
|
+
} else {
|
1701
|
+
switch (parser->header_state) {
|
1702
|
+
case h_connection_keep_alive:
|
1703
|
+
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
1704
|
+
break;
|
1705
|
+
case h_connection_close:
|
1706
|
+
parser->flags |= F_CONNECTION_CLOSE;
|
1707
|
+
break;
|
1708
|
+
case h_connection_upgrade:
|
1709
|
+
parser->flags |= F_CONNECTION_UPGRADE;
|
1710
|
+
break;
|
1711
|
+
case h_transfer_encoding_chunked:
|
1712
|
+
parser->flags |= F_CHUNKED;
|
1713
|
+
break;
|
1714
|
+
default:
|
1715
|
+
break;
|
1716
|
+
}
|
1717
|
+
|
1718
|
+
/* header value was empty */
|
1719
|
+
MARK(header_value);
|
1720
|
+
UPDATE_STATE(s_header_field_start);
|
1721
|
+
CALLBACK_DATA_NOADVANCE(header_value);
|
1722
|
+
REEXECUTE();
|
1599
1723
|
}
|
1600
|
-
break;
|
1601
1724
|
}
|
1602
1725
|
|
1603
1726
|
case s_headers_almost_done:
|
@@ -1606,16 +1729,33 @@ size_t http_parser_execute (http_parser *parser,
|
|
1606
1729
|
|
1607
1730
|
if (parser->flags & F_TRAILING) {
|
1608
1731
|
/* End of a chunked request */
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1732
|
+
UPDATE_STATE(s_message_done);
|
1733
|
+
CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
|
1734
|
+
REEXECUTE();
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
/* Cannot use chunked encoding and a content-length header together
|
1738
|
+
per the HTTP specification. */
|
1739
|
+
if ((parser->flags & F_CHUNKED) &&
|
1740
|
+
(parser->flags & F_CONTENTLENGTH)) {
|
1741
|
+
SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
|
1742
|
+
goto error;
|
1612
1743
|
}
|
1613
1744
|
|
1614
|
-
|
1745
|
+
UPDATE_STATE(s_headers_done);
|
1615
1746
|
|
1616
1747
|
/* Set this here so that on_headers_complete() callbacks can see it */
|
1617
|
-
parser->
|
1618
|
-
|
1748
|
+
if ((parser->flags & F_UPGRADE) &&
|
1749
|
+
(parser->flags & F_CONNECTION_UPGRADE)) {
|
1750
|
+
/* For responses, "Upgrade: foo" and "Connection: upgrade" are
|
1751
|
+
* mandatory only when it is a 101 Switching Protocols response,
|
1752
|
+
* otherwise it is purely informational, to announce support.
|
1753
|
+
*/
|
1754
|
+
parser->upgrade =
|
1755
|
+
(parser->type == HTTP_REQUEST || parser->status_code == 101);
|
1756
|
+
} else {
|
1757
|
+
parser->upgrade = (parser->method == HTTP_CONNECT);
|
1758
|
+
}
|
1619
1759
|
|
1620
1760
|
/* Here we call the headers_complete callback. This is somewhat
|
1621
1761
|
* different than other callbacks because if the user returns 1, we
|
@@ -1631,59 +1771,67 @@ size_t http_parser_execute (http_parser *parser,
|
|
1631
1771
|
case 0:
|
1632
1772
|
break;
|
1633
1773
|
|
1774
|
+
case 2:
|
1775
|
+
parser->upgrade = 1;
|
1776
|
+
|
1777
|
+
/* fall through */
|
1634
1778
|
case 1:
|
1635
1779
|
parser->flags |= F_SKIPBODY;
|
1636
1780
|
break;
|
1637
1781
|
|
1638
1782
|
default:
|
1639
1783
|
SET_ERRNO(HPE_CB_headers_complete);
|
1640
|
-
|
1784
|
+
RETURN(p - data); /* Error */
|
1641
1785
|
}
|
1642
1786
|
}
|
1643
1787
|
|
1644
1788
|
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
|
1645
|
-
|
1789
|
+
RETURN(p - data);
|
1646
1790
|
}
|
1647
1791
|
|
1648
|
-
|
1792
|
+
REEXECUTE();
|
1649
1793
|
}
|
1650
1794
|
|
1651
1795
|
case s_headers_done:
|
1652
1796
|
{
|
1797
|
+
int hasBody;
|
1653
1798
|
STRICT_CHECK(ch != LF);
|
1654
1799
|
|
1655
1800
|
parser->nread = 0;
|
1656
|
-
|
1657
|
-
|
1658
|
-
|
1659
|
-
parser->
|
1801
|
+
nread = 0;
|
1802
|
+
|
1803
|
+
hasBody = parser->flags & F_CHUNKED ||
|
1804
|
+
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
|
1805
|
+
if (parser->upgrade && (parser->method == HTTP_CONNECT ||
|
1806
|
+
(parser->flags & F_SKIPBODY) || !hasBody)) {
|
1807
|
+
/* Exit, the rest of the message is in a different protocol. */
|
1808
|
+
UPDATE_STATE(NEW_MESSAGE());
|
1660
1809
|
CALLBACK_NOTIFY(message_complete);
|
1661
|
-
|
1810
|
+
RETURN((p - data) + 1);
|
1662
1811
|
}
|
1663
1812
|
|
1664
1813
|
if (parser->flags & F_SKIPBODY) {
|
1665
|
-
|
1814
|
+
UPDATE_STATE(NEW_MESSAGE());
|
1666
1815
|
CALLBACK_NOTIFY(message_complete);
|
1667
1816
|
} else if (parser->flags & F_CHUNKED) {
|
1668
1817
|
/* chunked encoding - ignore Content-Length header */
|
1669
|
-
|
1818
|
+
UPDATE_STATE(s_chunk_size_start);
|
1670
1819
|
} else {
|
1671
1820
|
if (parser->content_length == 0) {
|
1672
1821
|
/* Content-Length header given but zero: Content-Length: 0\r\n */
|
1673
|
-
|
1822
|
+
UPDATE_STATE(NEW_MESSAGE());
|
1674
1823
|
CALLBACK_NOTIFY(message_complete);
|
1675
1824
|
} else if (parser->content_length != ULLONG_MAX) {
|
1676
1825
|
/* Content-Length header given and non-zero */
|
1677
|
-
|
1826
|
+
UPDATE_STATE(s_body_identity);
|
1678
1827
|
} else {
|
1679
|
-
if (parser
|
1680
|
-
!http_message_needs_eof(parser)) {
|
1828
|
+
if (!http_message_needs_eof(parser)) {
|
1681
1829
|
/* Assume content-length 0 - read the next */
|
1682
|
-
|
1830
|
+
UPDATE_STATE(NEW_MESSAGE());
|
1683
1831
|
CALLBACK_NOTIFY(message_complete);
|
1684
1832
|
} else {
|
1685
1833
|
/* Read body until EOF */
|
1686
|
-
|
1834
|
+
UPDATE_STATE(s_body_identity_eof);
|
1687
1835
|
}
|
1688
1836
|
}
|
1689
1837
|
}
|
@@ -1709,7 +1857,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1709
1857
|
p += to_read - 1;
|
1710
1858
|
|
1711
1859
|
if (parser->content_length == 0) {
|
1712
|
-
|
1860
|
+
UPDATE_STATE(s_message_done);
|
1713
1861
|
|
1714
1862
|
/* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
|
1715
1863
|
*
|
@@ -1721,7 +1869,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1721
1869
|
* important for applications, but let's keep it for now.
|
1722
1870
|
*/
|
1723
1871
|
CALLBACK_DATA_(body, p - body_mark + 1, p - data);
|
1724
|
-
|
1872
|
+
REEXECUTE();
|
1725
1873
|
}
|
1726
1874
|
|
1727
1875
|
break;
|
@@ -1735,23 +1883,27 @@ size_t http_parser_execute (http_parser *parser,
|
|
1735
1883
|
break;
|
1736
1884
|
|
1737
1885
|
case s_message_done:
|
1738
|
-
|
1886
|
+
UPDATE_STATE(NEW_MESSAGE());
|
1739
1887
|
CALLBACK_NOTIFY(message_complete);
|
1888
|
+
if (parser->upgrade) {
|
1889
|
+
/* Exit, the rest of the message is in a different protocol. */
|
1890
|
+
RETURN((p - data) + 1);
|
1891
|
+
}
|
1740
1892
|
break;
|
1741
1893
|
|
1742
1894
|
case s_chunk_size_start:
|
1743
1895
|
{
|
1744
|
-
assert(
|
1896
|
+
assert(nread == 1);
|
1745
1897
|
assert(parser->flags & F_CHUNKED);
|
1746
1898
|
|
1747
1899
|
unhex_val = unhex[(unsigned char)ch];
|
1748
|
-
if (unhex_val == -1) {
|
1900
|
+
if (UNLIKELY(unhex_val == -1)) {
|
1749
1901
|
SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
|
1750
1902
|
goto error;
|
1751
1903
|
}
|
1752
1904
|
|
1753
1905
|
parser->content_length = unhex_val;
|
1754
|
-
|
1906
|
+
UPDATE_STATE(s_chunk_size);
|
1755
1907
|
break;
|
1756
1908
|
}
|
1757
1909
|
|
@@ -1762,7 +1914,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1762
1914
|
assert(parser->flags & F_CHUNKED);
|
1763
1915
|
|
1764
1916
|
if (ch == CR) {
|
1765
|
-
|
1917
|
+
UPDATE_STATE(s_chunk_size_almost_done);
|
1766
1918
|
break;
|
1767
1919
|
}
|
1768
1920
|
|
@@ -1770,7 +1922,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1770
1922
|
|
1771
1923
|
if (unhex_val == -1) {
|
1772
1924
|
if (ch == ';' || ch == ' ') {
|
1773
|
-
|
1925
|
+
UPDATE_STATE(s_chunk_parameters);
|
1774
1926
|
break;
|
1775
1927
|
}
|
1776
1928
|
|
@@ -1782,8 +1934,8 @@ size_t http_parser_execute (http_parser *parser,
|
|
1782
1934
|
t *= 16;
|
1783
1935
|
t += unhex_val;
|
1784
1936
|
|
1785
|
-
/* Overflow? */
|
1786
|
-
if (
|
1937
|
+
/* Overflow? Test against a conservative limit for simplicity. */
|
1938
|
+
if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
|
1787
1939
|
SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
|
1788
1940
|
goto error;
|
1789
1941
|
}
|
@@ -1797,7 +1949,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1797
1949
|
assert(parser->flags & F_CHUNKED);
|
1798
1950
|
/* just ignore this shit. TODO check for overflow */
|
1799
1951
|
if (ch == CR) {
|
1800
|
-
|
1952
|
+
UPDATE_STATE(s_chunk_size_almost_done);
|
1801
1953
|
break;
|
1802
1954
|
}
|
1803
1955
|
break;
|
@@ -1809,13 +1961,15 @@ size_t http_parser_execute (http_parser *parser,
|
|
1809
1961
|
STRICT_CHECK(ch != LF);
|
1810
1962
|
|
1811
1963
|
parser->nread = 0;
|
1964
|
+
nread = 0;
|
1812
1965
|
|
1813
1966
|
if (parser->content_length == 0) {
|
1814
1967
|
parser->flags |= F_TRAILING;
|
1815
|
-
|
1968
|
+
UPDATE_STATE(s_header_field_start);
|
1816
1969
|
} else {
|
1817
|
-
|
1970
|
+
UPDATE_STATE(s_chunk_data);
|
1818
1971
|
}
|
1972
|
+
CALLBACK_NOTIFY(chunk_header);
|
1819
1973
|
break;
|
1820
1974
|
}
|
1821
1975
|
|
@@ -1836,7 +1990,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1836
1990
|
p += to_read - 1;
|
1837
1991
|
|
1838
1992
|
if (parser->content_length == 0) {
|
1839
|
-
|
1993
|
+
UPDATE_STATE(s_chunk_data_almost_done);
|
1840
1994
|
}
|
1841
1995
|
|
1842
1996
|
break;
|
@@ -1846,7 +2000,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1846
2000
|
assert(parser->flags & F_CHUNKED);
|
1847
2001
|
assert(parser->content_length == 0);
|
1848
2002
|
STRICT_CHECK(ch != CR);
|
1849
|
-
|
2003
|
+
UPDATE_STATE(s_chunk_data_done);
|
1850
2004
|
CALLBACK_DATA(body);
|
1851
2005
|
break;
|
1852
2006
|
|
@@ -1854,7 +2008,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
1854
2008
|
assert(parser->flags & F_CHUNKED);
|
1855
2009
|
STRICT_CHECK(ch != LF);
|
1856
2010
|
parser->nread = 0;
|
1857
|
-
|
2011
|
+
nread = 0;
|
2012
|
+
UPDATE_STATE(s_chunk_size_start);
|
2013
|
+
CALLBACK_NOTIFY(chunk_complete);
|
1858
2014
|
break;
|
1859
2015
|
|
1860
2016
|
default:
|
@@ -1864,7 +2020,7 @@ size_t http_parser_execute (http_parser *parser,
|
|
1864
2020
|
}
|
1865
2021
|
}
|
1866
2022
|
|
1867
|
-
/* Run callbacks for any marks that we have leftover after we ran
|
2023
|
+
/* Run callbacks for any marks that we have leftover after we ran out of
|
1868
2024
|
* bytes. There should be at most one of these set, so it's OK to invoke
|
1869
2025
|
* them in series (unset marks will not result in callbacks).
|
1870
2026
|
*
|
@@ -1886,14 +2042,14 @@ size_t http_parser_execute (http_parser *parser,
|
|
1886
2042
|
CALLBACK_DATA_NOADVANCE(body);
|
1887
2043
|
CALLBACK_DATA_NOADVANCE(status);
|
1888
2044
|
|
1889
|
-
|
2045
|
+
RETURN(len);
|
1890
2046
|
|
1891
2047
|
error:
|
1892
2048
|
if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
|
1893
2049
|
SET_ERRNO(HPE_UNKNOWN);
|
1894
2050
|
}
|
1895
2051
|
|
1896
|
-
|
2052
|
+
RETURN(p - data);
|
1897
2053
|
}
|
1898
2054
|
|
1899
2055
|
|
@@ -1946,6 +2102,16 @@ http_method_str (enum http_method m)
|
|
1946
2102
|
return ELEM_AT(method_strings, m, "<unknown>");
|
1947
2103
|
}
|
1948
2104
|
|
2105
|
+
const char *
|
2106
|
+
http_status_str (enum http_status s)
|
2107
|
+
{
|
2108
|
+
switch (s) {
|
2109
|
+
#define XX(num, name, string) case HTTP_STATUS_##name: return #string;
|
2110
|
+
HTTP_STATUS_MAP(XX)
|
2111
|
+
#undef XX
|
2112
|
+
default: return "<unknown>";
|
2113
|
+
}
|
2114
|
+
}
|
1949
2115
|
|
1950
2116
|
void
|
1951
2117
|
http_parser_init (http_parser *parser, enum http_parser_type t)
|
@@ -1958,15 +2124,21 @@ http_parser_init (http_parser *parser, enum http_parser_type t)
|
|
1958
2124
|
parser->http_errno = HPE_OK;
|
1959
2125
|
}
|
1960
2126
|
|
2127
|
+
void
|
2128
|
+
http_parser_settings_init(http_parser_settings *settings)
|
2129
|
+
{
|
2130
|
+
memset(settings, 0, sizeof(*settings));
|
2131
|
+
}
|
2132
|
+
|
1961
2133
|
const char *
|
1962
2134
|
http_errno_name(enum http_errno err) {
|
1963
|
-
assert(err < (
|
2135
|
+
assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
|
1964
2136
|
return http_strerror_tab[err].name;
|
1965
2137
|
}
|
1966
2138
|
|
1967
2139
|
const char *
|
1968
2140
|
http_errno_description(enum http_errno err) {
|
1969
|
-
assert(err < (
|
2141
|
+
assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
|
1970
2142
|
return http_strerror_tab[err].description;
|
1971
2143
|
}
|
1972
2144
|
|
@@ -2000,7 +2172,7 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
|
2000
2172
|
return s_http_host;
|
2001
2173
|
}
|
2002
2174
|
|
2003
|
-
/*
|
2175
|
+
/* fall through */
|
2004
2176
|
case s_http_host_v6_end:
|
2005
2177
|
if (ch == ':') {
|
2006
2178
|
return s_http_host_port_start;
|
@@ -2013,12 +2185,29 @@ http_parse_host_char(enum http_host_state s, const char ch) {
|
|
2013
2185
|
return s_http_host_v6_end;
|
2014
2186
|
}
|
2015
2187
|
|
2016
|
-
/*
|
2188
|
+
/* fall through */
|
2017
2189
|
case s_http_host_v6_start:
|
2018
2190
|
if (IS_HEX(ch) || ch == ':' || ch == '.') {
|
2019
2191
|
return s_http_host_v6;
|
2020
2192
|
}
|
2021
2193
|
|
2194
|
+
if (s == s_http_host_v6 && ch == '%') {
|
2195
|
+
return s_http_host_v6_zone_start;
|
2196
|
+
}
|
2197
|
+
break;
|
2198
|
+
|
2199
|
+
case s_http_host_v6_zone:
|
2200
|
+
if (ch == ']') {
|
2201
|
+
return s_http_host_v6_end;
|
2202
|
+
}
|
2203
|
+
|
2204
|
+
/* fall through */
|
2205
|
+
case s_http_host_v6_zone_start:
|
2206
|
+
/* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
|
2207
|
+
if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
|
2208
|
+
ch == '~') {
|
2209
|
+
return s_http_host_v6_zone;
|
2210
|
+
}
|
2022
2211
|
break;
|
2023
2212
|
|
2024
2213
|
case s_http_host_port:
|
@@ -2042,6 +2231,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
|
2042
2231
|
const char *p;
|
2043
2232
|
size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
|
2044
2233
|
|
2234
|
+
assert(u->field_set & (1 << UF_HOST));
|
2235
|
+
|
2045
2236
|
u->field_data[UF_HOST].len = 0;
|
2046
2237
|
|
2047
2238
|
s = found_at ? s_http_userinfo_start : s_http_host_start;
|
@@ -2068,6 +2259,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
|
2068
2259
|
u->field_data[UF_HOST].len++;
|
2069
2260
|
break;
|
2070
2261
|
|
2262
|
+
case s_http_host_v6_zone_start:
|
2263
|
+
case s_http_host_v6_zone:
|
2264
|
+
u->field_data[UF_HOST].len++;
|
2265
|
+
break;
|
2266
|
+
|
2071
2267
|
case s_http_host_port:
|
2072
2268
|
if (s != s_http_host_port) {
|
2073
2269
|
u->field_data[UF_PORT].off = p - buf;
|
@@ -2097,6 +2293,8 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
|
2097
2293
|
case s_http_host_start:
|
2098
2294
|
case s_http_host_v6_start:
|
2099
2295
|
case s_http_host_v6:
|
2296
|
+
case s_http_host_v6_zone_start:
|
2297
|
+
case s_http_host_v6_zone:
|
2100
2298
|
case s_http_host_port_start:
|
2101
2299
|
case s_http_userinfo:
|
2102
2300
|
case s_http_userinfo_start:
|
@@ -2108,6 +2306,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
|
2108
2306
|
return 0;
|
2109
2307
|
}
|
2110
2308
|
|
2309
|
+
void
|
2310
|
+
http_parser_url_init(struct http_parser_url *u) {
|
2311
|
+
memset(u, 0, sizeof(*u));
|
2312
|
+
}
|
2313
|
+
|
2111
2314
|
int
|
2112
2315
|
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
2113
2316
|
struct http_parser_url *u)
|
@@ -2117,9 +2320,13 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
|
2117
2320
|
enum http_parser_url_fields uf, old_uf;
|
2118
2321
|
int found_at = 0;
|
2119
2322
|
|
2323
|
+
if (buflen == 0) {
|
2324
|
+
return 1;
|
2325
|
+
}
|
2326
|
+
|
2120
2327
|
u->port = u->field_set = 0;
|
2121
2328
|
s = is_connect ? s_req_server_start : s_req_spaces_before_url;
|
2122
|
-
|
2329
|
+
old_uf = UF_MAX;
|
2123
2330
|
|
2124
2331
|
for (p = buf; p < buf + buflen; p++) {
|
2125
2332
|
s = parse_url_char(s, *p);
|
@@ -2144,7 +2351,7 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
|
2144
2351
|
case s_req_server_with_at:
|
2145
2352
|
found_at = 1;
|
2146
2353
|
|
2147
|
-
/*
|
2354
|
+
/* fall through */
|
2148
2355
|
case s_req_server:
|
2149
2356
|
uf = UF_HOST;
|
2150
2357
|
break;
|
@@ -2181,7 +2388,12 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
|
2181
2388
|
|
2182
2389
|
/* host must be present if there is a schema */
|
2183
2390
|
/* parsing http:///toto will fail */
|
2184
|
-
if ((u->field_set & (
|
2391
|
+
if ((u->field_set & (1 << UF_SCHEMA)) &&
|
2392
|
+
(u->field_set & (1 << UF_HOST)) == 0) {
|
2393
|
+
return 1;
|
2394
|
+
}
|
2395
|
+
|
2396
|
+
if (u->field_set & (1 << UF_HOST)) {
|
2185
2397
|
if (http_parse_host(buf, u, found_at) != 0) {
|
2186
2398
|
return 1;
|
2187
2399
|
}
|
@@ -2193,12 +2405,27 @@ http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
|
2193
2405
|
}
|
2194
2406
|
|
2195
2407
|
if (u->field_set & (1 << UF_PORT)) {
|
2196
|
-
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2201
|
-
|
2408
|
+
uint16_t off;
|
2409
|
+
uint16_t len;
|
2410
|
+
const char* p;
|
2411
|
+
const char* end;
|
2412
|
+
unsigned long v;
|
2413
|
+
|
2414
|
+
off = u->field_data[UF_PORT].off;
|
2415
|
+
len = u->field_data[UF_PORT].len;
|
2416
|
+
end = buf + off + len;
|
2417
|
+
|
2418
|
+
/* NOTE: The characters are already validated and are in the [0-9] range */
|
2419
|
+
assert(off + len <= buflen && "Port number overflow");
|
2420
|
+
v = 0;
|
2421
|
+
for (p = buf + off; p < end; p++) {
|
2422
|
+
v *= 10;
|
2423
|
+
v += *p - '0';
|
2424
|
+
|
2425
|
+
/* Ports have a max value of 2^16 */
|
2426
|
+
if (v > 0xffff) {
|
2427
|
+
return 1;
|
2428
|
+
}
|
2202
2429
|
}
|
2203
2430
|
|
2204
2431
|
u->port = (uint16_t) v;
|
@@ -2215,6 +2442,7 @@ http_parser_pause(http_parser *parser, int paused) {
|
|
2215
2442
|
*/
|
2216
2443
|
if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
|
2217
2444
|
HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
|
2445
|
+
uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
|
2218
2446
|
SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
|
2219
2447
|
} else {
|
2220
2448
|
assert(0 && "Attempting to pause parser in error state");
|