summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrivardhan Hebbar <sri.hebbar@samsung.com>2015-11-09 15:52:11 -0800
committerCedric BAIL <cedric@osg.samsung.com>2015-11-09 15:52:15 -0800
commite5a9c7844fafce44105e064a1733dcb00cb46d5f (patch)
tree1cd7da56e257ddc28f8801a9e17d1e0ad60319be
parentb9913d52e81e3e7fe0d50268a3bfd259d1d9de9f (diff)
ecore_con: add http_parser static lib.
Summary: This lib would be used in efl_network_websocket. Signed-off-by: Srivardhan Hebbar <sri.hebbar@samsung.com> Reviewers: cedric Reviewed By: cedric Differential Revision: https://phab.enlightenment.org/D3244 Signed-off-by: Cedric BAIL <cedric@osg.samsung.com>
Diffstat (limited to '')
-rw-r--r--Makefile.am1
-rw-r--r--licenses/COPYING.NGINX-MIT23
-rw-r--r--src/Makefile_Ecore_Con.am12
-rw-r--r--src/static_libs/http-parser/AUTHORS41
-rw-r--r--src/static_libs/http-parser/CONTRIBUTIONS4
-rw-r--r--src/static_libs/http-parser/README.md183
-rw-r--r--src/static_libs/http-parser/contrib/parsertrace.c156
-rw-r--r--src/static_libs/http-parser/contrib/url_parser.c44
-rw-r--r--src/static_libs/http-parser/http_parser.c2209
-rw-r--r--src/static_libs/http-parser/http_parser.h318
-rw-r--r--src/static_libs/http-parser/test.c3476
11 files changed, 6466 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index 56c298cb0b..c1d0a84300 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -61,6 +61,7 @@ licenses/COPYING.GPL \
61licenses/COPYING.BSD \ 61licenses/COPYING.BSD \
62licenses/COPYING.SMALL \ 62licenses/COPYING.SMALL \
63licenses/COPYING.FTL \ 63licenses/COPYING.FTL \
64licenses/COPYING.NGINX-MIT \
64old/ChangeLog.ecore \ 65old/ChangeLog.ecore \
65old/ChangeLog.edje \ 66old/ChangeLog.edje \
66old/ChangeLog.eet \ 67old/ChangeLog.eet \
diff --git a/licenses/COPYING.NGINX-MIT b/licenses/COPYING.NGINX-MIT
new file mode 100644
index 0000000000..58010b3889
--- /dev/null
+++ b/licenses/COPYING.NGINX-MIT
@@ -0,0 +1,23 @@
1http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
2Igor Sysoev.
3
4Additional changes are licensed under the same terms as NGINX and
5copyright Joyent, Inc. and other Node contributors. All rights reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to
9deal in the Software without restriction, including without limitation the
10rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11sell copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23IN THE SOFTWARE.
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index d5badfc83a..25569ab295 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -53,6 +53,8 @@ lib/ecore_con/ecore_con_ssl.c \
53lib/ecore_con/ecore_con_url.c \ 53lib/ecore_con/ecore_con_url.c \
54lib/ecore_con/ecore_con_url_curl.c \ 54lib/ecore_con/ecore_con_url_curl.c \
55lib/ecore_con/ecore_con_url_curl.h \ 55lib/ecore_con/ecore_con_url_curl.h \
56static_libs/http-parser/http_parser.c \
57static_libs/http-parser/http_parser.h \
56lib/ecore_con/ecore_con_private.h 58lib/ecore_con/ecore_con_private.h
57 59
58EXTRA_DIST += lib/ecore_con/ecore_con_legacy.c 60EXTRA_DIST += lib/ecore_con/ecore_con_legacy.c
@@ -78,9 +80,17 @@ lib_ecore_con_libecore_con_la_LIBADD = @ECORE_CON_LIBS@ @EVIL_LIBS@
78lib_ecore_con_libecore_con_la_DEPENDENCIES = @ECORE_CON_INTERNAL_LIBS@ 80lib_ecore_con_libecore_con_la_DEPENDENCIES = @ECORE_CON_INTERNAL_LIBS@
79lib_ecore_con_libecore_con_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ 81lib_ecore_con_libecore_con_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@
80 82
83lib_ecore_con_libecore_con_la_CPPFLAGS += -I$(top_srcdir)/src/static_libs/http-parser
84
81EXTRA_DIST += \ 85EXTRA_DIST += \
82tests/ecore_con/server.key \ 86tests/ecore_con/server.key \
83tests/ecore_con/server.pem 87tests/ecore_con/server.pem \
88static_libs/http-parser/AUTHORS \
89static_libs/http-parser/CONTRIBUTIONS \
90static_libs/http-parser/README.md \
91static_libs/http-parser/test.c \
92static_libs/http-parser/contrib/parsertrace.c \
93static_libs/http-parser/contrib/url_parser.c
84 94
85### Unit tests 95### Unit tests
86 96
diff --git a/src/static_libs/http-parser/AUTHORS b/src/static_libs/http-parser/AUTHORS
new file mode 100644
index 0000000000..92ee45cad6
--- /dev/null
+++ b/src/static_libs/http-parser/AUTHORS
@@ -0,0 +1,41 @@
1# Authors ordered by first contribution.
2Ryan Dahl <ry@tinyclouds.org>
3Jeremy Hinegardner <jeremy@hinegardner.org>
4Sergey Shepelev <temotor@gmail.com>
5Joe Damato <ice799@gmail.com>
6tomika <tomika_nospam@freemail.hu>
7Phoenix Sol <phoenix@burninglabs.com>
8Cliff Frey <cliff@meraki.com>
9Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
10Santiago Gala <sgala@apache.org>
11Tim Becker <tim.becker@syngenio.de>
12Jeff Terrace <jterrace@gmail.com>
13Ben Noordhuis <info@bnoordhuis.nl>
14Nathan Rajlich <nathan@tootallnate.net>
15Mark Nottingham <mnot@mnot.net>
16Aman Gupta <aman@tmm1.net>
17Tim Becker <tim.becker@kuriositaet.de>
18Sean Cunningham <sean.cunningham@mandiant.com>
19Peter Griess <pg@std.in>
20Salman Haq <salman.haq@asti-usa.com>
21Cliff Frey <clifffrey@gmail.com>
22Jon Kolb <jon@b0g.us>
23Fouad Mardini <f.mardini@gmail.com>
24Paul Querna <pquerna@apache.org>
25Felix Geisendörfer <felix@debuggable.com>
26koichik <koichik@improvement.jp>
27Andre Caron <andre.l.caron@gmail.com>
28Ivo Raisr <ivosh@ivosh.net>
29James McLaughlin <jamie@lacewing-project.org>
30David Gwynne <loki@animata.net>
31Thomas LE ROUX <thomas@november-eleven.fr>
32Randy Rizun <rrizun@ortivawireless.com>
33Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
34Simon Zimmermann <simonz05@gmail.com>
35Erik Dubbelboer <erik@dubbelboer.com>
36Martell Malone <martellmalone@gmail.com>
37Bertrand Paquet <bpaquet@octo.com>
38BogDan Vatra <bogdan@kde.org>
39Peter Faiman <peter@thepicard.org>
40Corey Richardson <corey@octayn.net>
41Tóth Tamás <tomika_nospam@freemail.hu>
diff --git a/src/static_libs/http-parser/CONTRIBUTIONS b/src/static_libs/http-parser/CONTRIBUTIONS
new file mode 100644
index 0000000000..11ba31e4b9
--- /dev/null
+++ b/src/static_libs/http-parser/CONTRIBUTIONS
@@ -0,0 +1,4 @@
1Contributors must agree to the Contributor License Agreement before patches
2can be accepted.
3
4http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
diff --git a/src/static_libs/http-parser/README.md b/src/static_libs/http-parser/README.md
new file mode 100644
index 0000000000..0bf5d359ac
--- /dev/null
+++ b/src/static_libs/http-parser/README.md
@@ -0,0 +1,183 @@
1HTTP Parser
2===========
3
4[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)
5
6This is a parser for HTTP messages written in C. It parses both requests and
7responses. The parser is designed to be used in performance HTTP
8applications. It does not make any syscalls nor allocations, it does not
9buffer data, it can be interrupted at anytime. Depending on your
10architecture, it only requires about 40 bytes of data per message
11stream (in a web server that is per connection).
12
13Features:
14
15 * No dependencies
16 * Handles persistent streams (keep-alive).
17 * Decodes chunked encoding.
18 * Upgrade support
19 * Defends against buffer overflow attacks.
20
21The parser extracts the following information from HTTP messages:
22
23 * Header fields and values
24 * Content-Length
25 * Request method
26 * Response status code
27 * Transfer-Encoding
28 * HTTP version
29 * Request URL
30 * Message body
31
32
33Usage
34-----
35
36One `http_parser` object is used per TCP connection. Initialize the struct
37using `http_parser_init()` and set the callbacks. That might look something
38like this for a request parser:
39```c
40http_parser_settings settings;
41settings.on_url = my_url_callback;
42settings.on_header_field = my_header_field_callback;
43/* ... */
44
45http_parser *parser = malloc(sizeof(http_parser));
46http_parser_init(parser, HTTP_REQUEST);
47parser->data = my_socket;
48```
49
50When data is received on the socket execute the parser and check for errors.
51
52```c
53size_t len = 80*1024, nparsed;
54char buf[len];
55ssize_t recved;
56
57recved = recv(fd, buf, len, 0);
58
59if (recved < 0) {
60 /* Handle error. */
61}
62
63/* Start up / continue the parser.
64 * Note we pass recved==0 to signal that EOF has been recieved.
65 */
66nparsed = http_parser_execute(parser, &settings, buf, recved);
67
68if (parser->upgrade) {
69 /* handle new protocol */
70} else if (nparsed != recved) {
71 /* Handle error. Usually just close the connection. */
72}
73```
74
75HTTP needs to know where the end of the stream is. For example, sometimes
76servers send responses without Content-Length and expect the client to
77consume input (for the body) until EOF. To tell http_parser about EOF, give
78`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors
79can still be encountered during an EOF, so one must still be prepared
80to receive them.
81
82Scalar valued message information such as `status_code`, `method`, and the
83HTTP version are stored in the parser structure. This data is only
84temporally stored in `http_parser` and gets reset on each new message. If
85this information is needed later, copy it out of the structure during the
86`headers_complete` callback.
87
88The parser decodes the transfer-encoding for both requests and responses
89transparently. That is, a chunked encoding is decoded before being sent to
90the on_body callback.
91
92
93The Special Problem of Upgrade
94------------------------------
95
96HTTP supports upgrading the connection to a different protocol. An
97increasingly common example of this is the Web Socket protocol which sends
98a request like
99
100 GET /demo HTTP/1.1
101 Upgrade: WebSocket
102 Connection: Upgrade
103 Host: example.com
104 Origin: http://example.com
105 WebSocket-Protocol: sample
106
107followed by non-HTTP data.
108
109(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
110information the Web Socket protocol.)
111
112To support this, the parser will treat this as a normal HTTP message without a
113body. Issuing both on_headers_complete and on_message_complete callbacks. However
114http_parser_execute() will stop parsing at the end of the headers and return.
115
116The user is expected to check if `parser->upgrade` has been set to 1 after
117`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
118offset by the return value of `http_parser_execute()`.
119
120
121Callbacks
122---------
123
124During the `http_parser_execute()` call, the callbacks set in
125`http_parser_settings` will be executed. The parser maintains state and
126never looks behind, so buffering the data is not necessary. If you need to
127save certain data for later usage, you can do that from the callbacks.
128
129There are two types of callbacks:
130
131* notification `typedef int (*http_cb) (http_parser*);`
132 Callbacks: on_message_begin, on_headers_complete, on_message_complete.
133* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
134 Callbacks: (requests only) on_uri,
135 (common) on_header_field, on_header_value, on_body;
136
137Callbacks must return 0 on success. Returning a non-zero value indicates
138error to the parser, making it exit immediately.
139
140In case you parse HTTP message in chunks (i.e. `read()` request line
141from socket, parse, read half headers, parse, etc) your data callbacks
142may be called more than once. Http-parser guarantees that data pointer is only
143valid for the lifetime of callback. You can also `read()` into a heap allocated
144buffer to avoid copying memory around if this fits your application.
145
146Reading headers may be a tricky task if you read/parse headers partially.
147Basically, you need to remember whether last header callback was field or value
148and apply following logic:
149
150 (on_header_field and on_header_value shortened to on_h_*)
151 ------------------------ ------------ --------------------------------------------
152 | State (prev. callback) | Callback | Description/action |
153 ------------------------ ------------ --------------------------------------------
154 | nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
155 | | | into it |
156 ------------------------ ------------ --------------------------------------------
157 | value | on_h_field | New header started. |
158 | | | Copy current name,value buffers to headers |
159 | | | list and allocate new buffer for new name |
160 ------------------------ ------------ --------------------------------------------
161 | field | on_h_field | Previous name continues. Reallocate name |
162 | | | buffer and append callback data to it |
163 ------------------------ ------------ --------------------------------------------
164 | field | on_h_value | Value for current header started. Allocate |
165 | | | new buffer and copy callback data to it |
166 ------------------------ ------------ --------------------------------------------
167 | value | on_h_value | Value continues. Reallocate value buffer |
168 | | | and append callback data to it |
169 ------------------------ ------------ --------------------------------------------
170
171
172Parsing URLs
173------------
174
175A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
176Users of this library may wish to use it to parse URLs constructed from
177consecutive `on_url` callbacks.
178
179See examples of reading in headers:
180
181* [partial example](http://gist.github.com/155877) in C
182* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
183* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript
diff --git a/src/static_libs/http-parser/contrib/parsertrace.c b/src/static_libs/http-parser/contrib/parsertrace.c
new file mode 100644
index 0000000000..c9bc71ec01
--- /dev/null
+++ b/src/static_libs/http-parser/contrib/parsertrace.c
@@ -0,0 +1,156 @@
1/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
2 *
3 * Additional changes are licensed under the same terms as NGINX and
4 * copyright Joyent, Inc. and other Node contributors. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25/* Dump what the parser finds to stdout as it happen */
26
27#include "http_parser.h"
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32int on_message_begin(http_parser* _) {
33 (void)_;
34 printf("\n***MESSAGE BEGIN***\n\n");
35 return 0;
36}
37
38int on_headers_complete(http_parser* _) {
39 (void)_;
40 printf("\n***HEADERS COMPLETE***\n\n");
41 return 0;
42}
43
44int on_message_complete(http_parser* _) {
45 (void)_;
46 printf("\n***MESSAGE COMPLETE***\n\n");
47 return 0;
48}
49
50int on_url(http_parser* _, const char* at, size_t length) {
51 (void)_;
52 printf("Url: %.*s\n", (int)length, at);
53 return 0;
54}
55
56int on_header_field(http_parser* _, const char* at, size_t length) {
57 (void)_;
58 printf("Header field: %.*s\n", (int)length, at);
59 return 0;
60}
61
62int on_header_value(http_parser* _, const char* at, size_t length) {
63 (void)_;
64 printf("Header value: %.*s\n", (int)length, at);
65 return 0;
66}
67
68int on_body(http_parser* _, const char* at, size_t length) {
69 (void)_;
70 printf("Body: %.*s\n", (int)length, at);
71 return 0;
72}
73
74void usage(const char* name) {
75 fprintf(stderr,
76 "Usage: %s $type $filename\n"
77 " type: -x, where x is one of {r,b,q}\n"
78 " parses file as a Response, reQuest, or Both\n",
79 name);
80 exit(EXIT_FAILURE);
81}
82
83int main(int argc, char* argv[]) {
84 enum http_parser_type file_type;
85
86 if (argc != 3) {
87 usage(argv[0]);
88 }
89
90 char* type = argv[1];
91 if (type[0] != '-') {
92 usage(argv[0]);
93 }
94
95 switch (type[1]) {
96 /* in the case of "-", type[1] will be NUL */
97 case 'r':
98 file_type = HTTP_RESPONSE;
99 break;
100 case 'q':
101 file_type = HTTP_REQUEST;
102 break;
103 case 'b':
104 file_type = HTTP_BOTH;
105 break;
106 default:
107 usage(argv[0]);
108 }
109
110 char* filename = argv[2];
111 FILE* file = fopen(filename, "r");
112 if (file == NULL) {
113 perror("fopen");
114 return EXIT_FAILURE;
115 }
116
117 fseek(file, 0, SEEK_END);
118 long file_length = ftell(file);
119 if (file_length == -1) {
120 perror("ftell");
121 return EXIT_FAILURE;
122 }
123 fseek(file, 0, SEEK_SET);
124
125 char* data = malloc(file_length);
126 if (fread(data, 1, file_length, file) != (size_t)file_length) {
127 fprintf(stderr, "couldn't read entire file\n");
128 free(data);
129 return EXIT_FAILURE;
130 }
131
132 http_parser_settings settings;
133 memset(&settings, 0, sizeof(settings));
134 settings.on_message_begin = on_message_begin;
135 settings.on_url = on_url;
136 settings.on_header_field = on_header_field;
137 settings.on_header_value = on_header_value;
138 settings.on_headers_complete = on_headers_complete;
139 settings.on_body = on_body;
140 settings.on_message_complete = on_message_complete;
141
142 http_parser parser;
143 http_parser_init(&parser, file_type);
144 size_t nparsed = http_parser_execute(&parser, &settings, data, file_length);
145 free(data);
146
147 if (nparsed != (size_t)file_length) {
148 fprintf(stderr,
149 "Error: %s (%s)\n",
150 http_errno_description(HTTP_PARSER_ERRNO(&parser)),
151 http_errno_name(HTTP_PARSER_ERRNO(&parser)));
152 return EXIT_FAILURE;
153 }
154
155 return EXIT_SUCCESS;
156}
diff --git a/src/static_libs/http-parser/contrib/url_parser.c b/src/static_libs/http-parser/contrib/url_parser.c
new file mode 100644
index 0000000000..b1f9c979f2
--- /dev/null
+++ b/src/static_libs/http-parser/contrib/url_parser.c
@@ -0,0 +1,44 @@
1#include "http_parser.h"
2#include <stdio.h>
3#include <string.h>
4
5void
6dump_url (const char *url, const struct http_parser_url *u)
7{
8 unsigned int i;
9
10 printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
11 for (i = 0; i < UF_MAX; i++) {
12 if ((u->field_set & (1 << i)) == 0) {
13 printf("\tfield_data[%u]: unset\n", i);
14 continue;
15 }
16
17 printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
18 i,
19 u->field_data[i].off,
20 u->field_data[i].len,
21 u->field_data[i].len,
22 url + u->field_data[i].off);
23 }
24}
25
26int main(int argc, char ** argv) {
27 if (argc != 3) {
28 printf("Syntax : %s connect|get url\n", argv[0]);
29 return 1;
30 }
31 struct http_parser_url u;
32 int len = strlen(argv[2]);
33 int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
34 printf("Parsing %s, connect %d\n", argv[2], connect);
35
36 int result = http_parser_parse_url(argv[2], len, connect, &u);
37 if (result != 0) {
38 printf("Parse error : %d\n", result);
39 return result;
40 }
41 printf("Parse ok, result : \n");
42 dump_url(argv[2], &u);
43 return 0;
44} \ No newline at end of file
diff --git a/src/static_libs/http-parser/http_parser.c b/src/static_libs/http-parser/http_parser.c
new file mode 100644
index 0000000000..9695525b2c
--- /dev/null
+++ b/src/static_libs/http-parser/http_parser.c
@@ -0,0 +1,2209 @@
1/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
2 *
3 * Additional changes are licensed under the same terms as NGINX and
4 * copyright Joyent, Inc. and other Node contributors. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24#include "http_parser.h"
25#include <assert.h>
26#include <stddef.h>
27#include <ctype.h>
28#include <stdlib.h>
29#include <string.h>
30#include <limits.h>
31
32#ifndef ULLONG_MAX
33# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
34#endif
35
36#ifndef MIN
37# define MIN(a,b) ((a) < (b) ? (a) : (b))
38#endif
39
40#ifndef ARRAY_SIZE
41# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
42#endif
43
44#ifndef BIT_AT
45# define BIT_AT(a, i) \
46 (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
47 (1 << ((unsigned int) (i) & 7))))
48#endif
49
50#ifndef ELEM_AT
51# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
52#endif
53
54#define SET_ERRNO(e) \
55do { \
56 parser->http_errno = (e); \
57} while(0)
58
59
60/* Run the notify callback FOR, returning ER if it fails */
61#define CALLBACK_NOTIFY_(FOR, ER) \
62do { \
63 assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
64 \
65 if (settings->on_##FOR) { \
66 if (0 != settings->on_##FOR(parser)) { \
67 SET_ERRNO(HPE_CB_##FOR); \
68 } \
69 \
70 /* We either errored above or got paused; get out */ \
71 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
72 return (ER); \
73 } \
74 } \
75} while (0)
76
77/* Run the notify callback FOR and consume the current byte */
78#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
79
80/* Run the notify callback FOR and don't consume the current byte */
81#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
82
83/* Run data callback FOR with LEN bytes, returning ER if it fails */
84#define CALLBACK_DATA_(FOR, LEN, ER) \
85do { \
86 assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
87 \
88 if (FOR##_mark) { \
89 if (settings->on_##FOR) { \
90 if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \
91 SET_ERRNO(HPE_CB_##FOR); \
92 } \
93 \
94 /* We either errored above or got paused; get out */ \
95 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \
96 return (ER); \
97 } \
98 } \
99 FOR##_mark = NULL; \
100 } \
101} while (0)
102
103/* Run the data callback FOR and consume the current byte */
104#define CALLBACK_DATA(FOR) \
105 CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
106
107/* Run the data callback FOR and don't consume the current byte */
108#define CALLBACK_DATA_NOADVANCE(FOR) \
109 CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
110
111/* Set the mark FOR; non-destructive if mark is already set */
112#define MARK(FOR) \
113do { \
114 if (!FOR##_mark) { \
115 FOR##_mark = p; \
116 } \
117} while (0)
118
119
120#define PROXY_CONNECTION "proxy-connection"
121#define CONNECTION "connection"
122#define CONTENT_LENGTH "content-length"
123#define TRANSFER_ENCODING "transfer-encoding"
124#define UPGRADE "upgrade"
125#define CHUNKED "chunked"
126#define KEEP_ALIVE "keep-alive"
127#define CLOSE "close"
128
129
130static const char *method_strings[] =
131 {
132#define XX(num, name, string) #string,
133 HTTP_METHOD_MAP(XX)
134#undef XX
135 };
136
137
138/* Tokens as defined by rfc 2616. Also lowercases them.
139 * token = 1*<any CHAR except CTLs or separators>
140 * separators = "(" | ")" | "<" | ">" | "@"
141 * | "," | ";" | ":" | "\" | <">
142 * | "/" | "[" | "]" | "?" | "="
143 * | "{" | "}" | SP | HT
144 */
145static const char tokens[256] = {
146/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
147 0, 0, 0, 0, 0, 0, 0, 0,
148/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
149 0, 0, 0, 0, 0, 0, 0, 0,
150/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
151 0, 0, 0, 0, 0, 0, 0, 0,
152/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
153 0, 0, 0, 0, 0, 0, 0, 0,
154/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
155 0, '!', 0, '#', '$', '%', '&', '\'',
156/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
157 0, 0, '*', '+', 0, '-', '.', 0,
158/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
159 '0', '1', '2', '3', '4', '5', '6', '7',
160/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
161 '8', '9', 0, 0, 0, 0, 0, 0,
162/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
163 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
164/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
165 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
166/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
167 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
168/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
169 'x', 'y', 'z', 0, 0, 0, '^', '_',
170/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
171 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
172/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
173 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
174/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
175 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
176/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
177 'x', 'y', 'z', 0, '|', 0, '~', 0 };
178
179
180static const int8_t unhex[256] =
181 {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
182 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
183 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
184 , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
185 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
186 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
187 ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
188 ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
189 };
190
191
192#if HTTP_PARSER_STRICT
193# define T(v) 0
194#else
195# define T(v) v
196#endif
197
198
199static const uint8_t normal_url_char[32] = {
200/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
201 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
202/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
203 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
204/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
205 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
206/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
207 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
208/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
209 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
210/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
211 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
212/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
213 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
214/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
215 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
216/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
217 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
218/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
219 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
220/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
221 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
222/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
223 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
224/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
225 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
226/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
227 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
228/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
229 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
230/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
231 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
232
233#undef T
234
235enum state
236 { s_dead = 1 /* important that this is > 0 */
237
238 , s_start_req_or_res
239 , s_res_or_resp_H
240 , s_start_res
241 , s_res_H
242 , s_res_HT
243 , s_res_HTT
244 , s_res_HTTP
245 , s_res_first_http_major
246 , s_res_http_major
247 , s_res_first_http_minor
248 , s_res_http_minor
249 , s_res_first_status_code
250 , s_res_status_code
251 , s_res_status
252 , s_res_line_almost_done
253
254 , s_start_req
255
256 , s_req_method
257 , s_req_spaces_before_url
258 , s_req_schema
259 , s_req_schema_slash
260 , s_req_schema_slash_slash
261 , s_req_server_start
262 , s_req_server
263 , s_req_server_with_at
264 , s_req_path
265 , s_req_query_string_start
266 , s_req_query_string
267 , s_req_fragment_start
268 , s_req_fragment
269 , s_req_http_start
270 , s_req_http_H
271 , s_req_http_HT
272 , s_req_http_HTT
273 , s_req_http_HTTP
274 , s_req_first_http_major
275 , s_req_http_major
276 , s_req_first_http_minor
277 , s_req_http_minor
278 , s_req_line_almost_done
279
280 , s_header_field_start
281 , s_header_field
282 , s_header_value_start
283 , s_header_value
284 , s_header_value_lws
285
286 , s_header_almost_done
287
288 , s_chunk_size_start
289 , s_chunk_size
290 , s_chunk_parameters
291 , s_chunk_size_almost_done
292
293 , s_headers_almost_done
294 , s_headers_done
295
296 /* Important: 's_headers_done' must be the last 'header' state. All
297 * states beyond this must be 'body' states. It is used for overflow
298 * checking. See the PARSING_HEADER() macro.
299 */
300
301 , s_chunk_data
302 , s_chunk_data_almost_done
303 , s_chunk_data_done
304
305 , s_body_identity
306 , s_body_identity_eof
307
308 , s_message_done
309 };
310
311
312#define PARSING_HEADER(state) (state <= s_headers_done)
313
314
315enum header_states
316 { h_general = 0
317 , h_C
318 , h_CO
319 , h_CON
320
321 , h_matching_connection
322 , h_matching_proxy_connection
323 , h_matching_content_length
324 , h_matching_transfer_encoding
325 , h_matching_upgrade
326
327 , h_connection
328 , h_content_length
329 , h_transfer_encoding
330 , h_upgrade
331
332 , h_matching_transfer_encoding_chunked
333 , h_matching_connection_keep_alive
334 , h_matching_connection_close
335
336 , h_transfer_encoding_chunked
337 , h_connection_keep_alive
338 , h_connection_close
339 };
340
341enum http_host_state
342 {
343 s_http_host_dead = 1
344 , s_http_userinfo_start
345 , s_http_userinfo
346 , s_http_host_start
347 , s_http_host_v6_start
348 , s_http_host
349 , s_http_host_v6
350 , s_http_host_v6_end
351 , s_http_host_port_start
352 , s_http_host_port
353};
354
355/* Macros for character classes; depends on strict-mode */
356#define CR '\r'
357#define LF '\n'
358#define LOWER(c) (unsigned char)(c | 0x20)
359#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
360#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
361#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
362#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
363#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
364 (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
365 (c) == ')')
366#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
367 (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
368 (c) == '$' || (c) == ',')
369
370#if HTTP_PARSER_STRICT
371#define TOKEN(c) (tokens[(unsigned char)c])
372#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
373#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
374#else
375#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
376#define IS_URL_CHAR(c) \
377 (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
378#define IS_HOST_CHAR(c) \
379 (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
380#endif
381
382
383#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
384
385
386#if HTTP_PARSER_STRICT
387# define STRICT_CHECK(cond) \
388do { \
389 if (cond) { \
390 SET_ERRNO(HPE_STRICT); \
391 goto error; \
392 } \
393} while (0)
394# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
395#else
396# define STRICT_CHECK(cond)
397# define NEW_MESSAGE() start_state
398#endif
399
400
401/* Map errno values to strings for human-readable output */
402#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
403static struct {
404 const char *name;
405 const char *description;
406} http_strerror_tab[] = {
407 HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
408};
409#undef HTTP_STRERROR_GEN
410
411int http_message_needs_eof(const http_parser *parser);
412
413/* Our URL parser.
414 *
415 * This is designed to be shared by http_parser_execute() for URL validation,
416 * hence it has a state transition + byte-for-byte interface. In addition, it
417 * is meant to be embedded in http_parser_parse_url(), which does the dirty
418 * work of turning state transitions URL components for its API.
419 *
420 * This function should only be invoked with non-space characters. It is
421 * assumed that the caller cares about (and can detect) the transition between
422 * URL and non-URL states by looking for these.
423 */
424static enum state
425parse_url_char(enum state s, const char ch)
426{
427 if (ch == ' ' || ch == '\r' || ch == '\n') {
428 return s_dead;
429 }
430
431#if HTTP_PARSER_STRICT
432 if (ch == '\t' || ch == '\f') {
433 return s_dead;
434 }
435#endif
436
437 switch (s) {
438 case s_req_spaces_before_url:
439 /* Proxied requests are followed by scheme of an absolute URI (alpha).
440 * All methods except CONNECT are followed by '/' or '*'.
441 */
442
443 if (ch == '/' || ch == '*') {
444 return s_req_path;
445 }
446
447 if (IS_ALPHA(ch)) {
448 return s_req_schema;
449 }
450
451 break;
452
453 case s_req_schema:
454 if (IS_ALPHA(ch)) {
455 return s;
456 }
457
458 if (ch == ':') {
459 return s_req_schema_slash;
460 }
461
462 break;
463
464 case s_req_schema_slash:
465 if (ch == '/') {
466 return s_req_schema_slash_slash;
467 }
468
469 break;
470
471 case s_req_schema_slash_slash:
472 if (ch == '/') {
473 return s_req_server_start;
474 }
475
476 break;
477
478 case s_req_server_with_at:
479 if (ch == '@') {
480 return s_dead;
481 }
482
483 /* FALLTHROUGH */
484 case s_req_server_start:
485 case s_req_server:
486 if (ch == '/') {
487 return s_req_path;
488 }
489
490 if (ch == '?') {
491 return s_req_query_string_start;
492 }
493
494 if (ch == '@') {
495 return s_req_server_with_at;
496 }
497
498 if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
499 return s_req_server;
500 }
501
502 break;
503
504 case s_req_path:
505 if (IS_URL_CHAR(ch)) {
506 return s;
507 }
508
509 switch (ch) {
510 case '?':
511 return s_req_query_string_start;
512
513 case '#':
514 return s_req_fragment_start;
515 }
516
517 break;
518
519 case s_req_query_string_start:
520 case s_req_query_string:
521 if (IS_URL_CHAR(ch)) {
522 return s_req_query_string;
523 }
524
525 switch (ch) {
526 case '?':
527 /* allow extra '?' in query string */
528 return s_req_query_string;
529
530 case '#':
531 return s_req_fragment_start;
532 }
533
534 break;
535
536 case s_req_fragment_start:
537 if (IS_URL_CHAR(ch)) {
538 return s_req_fragment;
539 }
540
541 switch (ch) {
542 case '?':
543 return s_req_fragment;
544
545 case '#':
546 return s;
547 }
548
549 break;
550
551 case s_req_fragment:
552 if (IS_URL_CHAR(ch)) {
553 return s;
554 }
555
556 switch (ch) {
557 case '?':
558 case '#':
559 return s;
560 }
561
562 break;
563
564 default:
565 break;
566 }
567
568 /* We should never fall out of the switch above unless there's an error */
569 return s_dead;
570}
571
572size_t http_parser_execute (http_parser *parser,
573 const http_parser_settings *settings,
574 const char *data,
575 size_t len)
576{
577 char c, ch;
578 int8_t unhex_val;
579 const char *p = data;
580 const char *header_field_mark = 0;
581 const char *header_value_mark = 0;
582 const char *url_mark = 0;
583 const char *body_mark = 0;
584
585 /* We're in an error state. Don't bother doing anything. */
586 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
587 return 0;
588 }
589
590 if (len == 0) {
591 switch (parser->state) {
592 case s_body_identity_eof:
593 /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
594 * we got paused.
595 */
596 CALLBACK_NOTIFY_NOADVANCE(message_complete);
597 return 0;
598
599 case s_dead:
600 case s_start_req_or_res:
601 case s_start_res:
602 case s_start_req:
603 return 0;
604
605 default:
606 SET_ERRNO(HPE_INVALID_EOF_STATE);
607 return 1;
608 }
609 }
610
611
612 if (parser->state == s_header_field)
613 header_field_mark = data;
614 if (parser->state == s_header_value)
615 header_value_mark = data;
616 switch (parser->state) {
617 case s_req_path:
618 case s_req_schema:
619 case s_req_schema_slash:
620 case s_req_schema_slash_slash:
621 case s_req_server_start:
622 case s_req_server:
623 case s_req_server_with_at:
624 case s_req_query_string_start:
625 case s_req_query_string:
626 case s_req_fragment_start:
627 case s_req_fragment:
628 url_mark = data;
629 break;
630 }
631
632 for (p=data; p != data + len; p++) {
633 ch = *p;
634
635 if (PARSING_HEADER(parser->state)) {
636 ++parser->nread;
637 /* Don't allow the total size of the HTTP headers (including the status
638 * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
639 * embedders against denial-of-service attacks where the attacker feeds
640 * us a never-ending header that the embedder keeps buffering.
641 *
642 * This check is arguably the responsibility of embedders but we're doing
643 * it on the embedder's behalf because most won't bother and this way we
644 * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
645 * than any reasonable request or response so this should never affect
646 * day-to-day operation.
647 */
648 if (parser->nread > HTTP_MAX_HEADER_SIZE) {
649 SET_ERRNO(HPE_HEADER_OVERFLOW);
650 goto error;
651 }
652 }
653
654 reexecute_byte:
655 switch (parser->state) {
656
657 case s_dead:
658 /* this state is used after a 'Connection: close' message
659 * the parser will error out if it reads another message
660 */
661 if (ch == CR || ch == LF)
662 break;
663
664 SET_ERRNO(HPE_CLOSED_CONNECTION);
665 goto error;
666
667 case s_start_req_or_res:
668 {
669 if (ch == CR || ch == LF)
670 break;
671 parser->flags = 0;
672 parser->content_length = ULLONG_MAX;
673
674 if (ch == 'H') {
675 parser->state = s_res_or_resp_H;
676
677 CALLBACK_NOTIFY(message_begin);
678 } else {
679 parser->type = HTTP_REQUEST;
680 parser->state = s_start_req;
681 goto reexecute_byte;
682 }
683
684 break;
685 }
686
687 case s_res_or_resp_H:
688 if (ch == 'T') {
689 parser->type = HTTP_RESPONSE;
690 parser->state = s_res_HT;
691 } else {
692 if (ch != 'E') {
693 SET_ERRNO(HPE_INVALID_CONSTANT);
694 goto error;
695 }
696
697 parser->type = HTTP_REQUEST;
698 parser->method = HTTP_HEAD;
699 parser->index = 2;
700 parser->state = s_req_method;
701 }
702 break;
703
704 case s_start_res:
705 {
706 parser->flags = 0;
707 parser->content_length = ULLONG_MAX;
708
709 switch (ch) {
710 case 'H':
711 parser->state = s_res_H;
712 break;
713
714 case CR:
715 case LF:
716 break;
717
718 default:
719 SET_ERRNO(HPE_INVALID_CONSTANT);
720 goto error;
721 }
722
723 CALLBACK_NOTIFY(message_begin);
724 break;
725 }
726
727 case s_res_H:
728 STRICT_CHECK(ch != 'T');
729 parser->state = s_res_HT;
730 break;
731
732 case s_res_HT:
733 STRICT_CHECK(ch != 'T');
734 parser->state = s_res_HTT;
735 break;
736
737 case s_res_HTT:
738 STRICT_CHECK(ch != 'P');
739 parser->state = s_res_HTTP;
740 break;
741
742 case s_res_HTTP:
743 STRICT_CHECK(ch != '/');
744 parser->state = s_res_first_http_major;
745 break;
746
747 case s_res_first_http_major:
748 if (ch < '0' || ch > '9') {
749 SET_ERRNO(HPE_INVALID_VERSION);
750 goto error;
751 }
752
753 parser->http_major = ch - '0';
754 parser->state = s_res_http_major;
755 break;
756
757 /* major HTTP version or dot */
758 case s_res_http_major:
759 {
760 if (ch == '.') {
761 parser->state = s_res_first_http_minor;
762 break;
763 }
764
765 if (!IS_NUM(ch)) {
766 SET_ERRNO(HPE_INVALID_VERSION);
767 goto error;
768 }
769
770 parser->http_major *= 10;
771 parser->http_major += ch - '0';
772
773 if (parser->http_major > 999) {
774 SET_ERRNO(HPE_INVALID_VERSION);
775 goto error;
776 }
777
778 break;
779 }
780
781 /* first digit of minor HTTP version */
782 case s_res_first_http_minor:
783 if (!IS_NUM(ch)) {
784 SET_ERRNO(HPE_INVALID_VERSION);
785 goto error;
786 }
787
788 parser->http_minor = ch - '0';
789 parser->state = s_res_http_minor;
790 break;
791
792 /* minor HTTP version or end of request line */
793 case s_res_http_minor:
794 {
795 if (ch == ' ') {
796 parser->state = s_res_first_status_code;
797 break;
798 }
799
800 if (!IS_NUM(ch)) {
801 SET_ERRNO(HPE_INVALID_VERSION);
802 goto error;
803 }
804
805 parser->http_minor *= 10;
806 parser->http_minor += ch - '0';
807
808 if (parser->http_minor > 999) {
809 SET_ERRNO(HPE_INVALID_VERSION);
810 goto error;
811 }
812
813 break;
814 }
815
816 case s_res_first_status_code:
817 {
818 if (!IS_NUM(ch)) {
819 if (ch == ' ') {
820 break;
821 }
822
823 SET_ERRNO(HPE_INVALID_STATUS);
824 goto error;
825 }
826 parser->status_code = ch - '0';
827 parser->state = s_res_status_code;
828 break;
829 }
830
831 case s_res_status_code:
832 {
833 if (!IS_NUM(ch)) {
834 switch (ch) {
835 case ' ':
836 parser->state = s_res_status;
837 break;
838 case CR:
839 parser->state = s_res_line_almost_done;
840 break;
841 case LF:
842 parser->state = s_header_field_start;
843 break;
844 default:
845 SET_ERRNO(HPE_INVALID_STATUS);
846 goto error;
847 }
848 break;
849 }
850
851 parser->status_code *= 10;
852 parser->status_code += ch - '0';
853
854 if (parser->status_code > 999) {
855 SET_ERRNO(HPE_INVALID_STATUS);
856 goto error;
857 }
858
859 break;
860 }
861
862 case s_res_status:
863 /* the human readable status. e.g. "NOT FOUND"
864 * we are not humans so just ignore this */
865 if (ch == CR) {
866 parser->state = s_res_line_almost_done;
867 break;
868 }
869
870 if (ch == LF) {
871 parser->state = s_header_field_start;
872 break;
873 }
874 break;
875
876 case s_res_line_almost_done:
877 STRICT_CHECK(ch != LF);
878 parser->state = s_header_field_start;
879 CALLBACK_NOTIFY(status_complete);
880 break;
881
882 case s_start_req:
883 {
884 if (ch == CR || ch == LF)
885 break;
886 parser->flags = 0;
887 parser->content_length = ULLONG_MAX;
888
889 if (!IS_ALPHA(ch)) {
890 SET_ERRNO(HPE_INVALID_METHOD);
891 goto error;
892 }
893
894 parser->method = (enum http_method) 0;
895 parser->index = 1;
896 switch (ch) {
897 case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
898 case 'D': parser->method = HTTP_DELETE; break;
899 case 'G': parser->method = HTTP_GET; break;
900 case 'H': parser->method = HTTP_HEAD; break;
901 case 'L': parser->method = HTTP_LOCK; break;
902 case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
903 case 'N': parser->method = HTTP_NOTIFY; break;
904 case 'O': parser->method = HTTP_OPTIONS; break;
905 case 'P': parser->method = HTTP_POST;
906 /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
907 break;
908 case 'R': parser->method = HTTP_REPORT; break;
909 case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
910 case 'T': parser->method = HTTP_TRACE; break;
911 case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
912 default:
913 SET_ERRNO(HPE_INVALID_METHOD);
914 goto error;
915 }
916 parser->state = s_req_method;
917
918 CALLBACK_NOTIFY(message_begin);
919
920 break;
921 }
922
923 case s_req_method:
924 {
925 const char *matcher;
926 if (ch == '\0') {
927 SET_ERRNO(HPE_INVALID_METHOD);
928 goto error;
929 }
930
931 matcher = method_strings[parser->method];
932 if (ch == ' ' && matcher[parser->index] == '\0') {
933 parser->state = s_req_spaces_before_url;
934 } else if (ch == matcher[parser->index]) {
935 ; /* nada */
936 } else if (parser->method == HTTP_CONNECT) {
937 if (parser->index == 1 && ch == 'H') {
938 parser->method = HTTP_CHECKOUT;
939 } else if (parser->index == 2 && ch == 'P') {
940 parser->method = HTTP_COPY;
941 } else {
942 SET_ERRNO(HPE_INVALID_METHOD);
943 goto error;
944 }
945 } else if (parser->method == HTTP_MKCOL) {
946 if (parser->index == 1 && ch == 'O') {
947 parser->method = HTTP_MOVE;
948 } else if (parser->index == 1 && ch == 'E') {
949 parser->method = HTTP_MERGE;
950 } else if (parser->index == 1 && ch == '-') {
951 parser->method = HTTP_MSEARCH;
952 } else if (parser->index == 2 && ch == 'A') {
953 parser->method = HTTP_MKACTIVITY;
954 } else {
955 SET_ERRNO(HPE_INVALID_METHOD);
956 goto error;
957 }
958 } else if (parser->method == HTTP_SUBSCRIBE) {
959 if (parser->index == 1 && ch == 'E') {
960 parser->method = HTTP_SEARCH;
961 } else {
962 SET_ERRNO(HPE_INVALID_METHOD);
963 goto error;
964 }
965 } else if (parser->index == 1 && parser->method == HTTP_POST) {
966 if (ch == 'R') {
967 parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
968 } else if (ch == 'U') {
969 parser->method = HTTP_PUT; /* or HTTP_PURGE */
970 } else if (ch == 'A') {
971 parser->method = HTTP_PATCH;
972 } else {
973 SET_ERRNO(HPE_INVALID_METHOD);
974 goto error;
975 }
976 } else if (parser->index == 2) {
977 if (parser->method == HTTP_PUT) {
978 if (ch == 'R') {
979 parser->method = HTTP_PURGE;
980 } else {
981 SET_ERRNO(HPE_INVALID_METHOD);
982 goto error;
983 }
984 } else if (parser->method == HTTP_UNLOCK) {
985 if (ch == 'S') {
986 parser->method = HTTP_UNSUBSCRIBE;
987 } else {
988 SET_ERRNO(HPE_INVALID_METHOD);
989 goto error;
990 }
991 } else {
992 SET_ERRNO(HPE_INVALID_METHOD);
993 goto error;
994 }
995 } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
996 parser->method = HTTP_PROPPATCH;
997 } else {
998 SET_ERRNO(HPE_INVALID_METHOD);
999 goto error;
1000 }
1001
1002 ++parser->index;
1003 break;
1004 }
1005
1006 case s_req_spaces_before_url:
1007 {
1008 if (ch == ' ') break;
1009
1010 MARK(url);
1011 if (parser->method == HTTP_CONNECT) {
1012 parser->state = s_req_server_start;
1013 }
1014
1015 parser->state = parse_url_char((enum state)parser->state, ch);
1016 if (parser->state == s_dead) {
1017 SET_ERRNO(HPE_INVALID_URL);
1018 goto error;
1019 }
1020
1021 break;
1022 }
1023
1024 case s_req_schema:
1025 case s_req_schema_slash:
1026 case s_req_schema_slash_slash:
1027 case s_req_server_start:
1028 {
1029 switch (ch) {
1030 /* No whitespace allowed here */
1031 case ' ':
1032 case CR:
1033 case LF:
1034 SET_ERRNO(HPE_INVALID_URL);
1035 goto error;
1036 default:
1037 parser->state = parse_url_char((enum state)parser->state, ch);
1038 if (parser->state == s_dead) {
1039 SET_ERRNO(HPE_INVALID_URL);
1040 goto error;
1041 }
1042 }
1043
1044 break;
1045 }
1046
1047 case s_req_server:
1048 case s_req_server_with_at:
1049 case s_req_path:
1050 case s_req_query_string_start:
1051 case s_req_query_string:
1052 case s_req_fragment_start:
1053 case s_req_fragment:
1054 {
1055 switch (ch) {
1056 case ' ':
1057 parser->state = s_req_http_start;
1058 CALLBACK_DATA(url);
1059 break;
1060 case CR:
1061 case LF:
1062 parser->http_major = 0;
1063 parser->http_minor = 9;
1064 parser->state = (ch == CR) ?
1065 s_req_line_almost_done :
1066 s_header_field_start;
1067 CALLBACK_DATA(url);
1068 break;
1069 default:
1070 parser->state = parse_url_char((enum state)parser->state, ch);
1071 if (parser->state == s_dead) {
1072 SET_ERRNO(HPE_INVALID_URL);
1073 goto error;
1074 }
1075 }
1076 break;
1077 }
1078
1079 case s_req_http_start:
1080 switch (ch) {
1081 case 'H':
1082 parser->state = s_req_http_H;
1083 break;
1084 case ' ':
1085 break;
1086 default:
1087 SET_ERRNO(HPE_INVALID_CONSTANT);
1088 goto error;
1089 }
1090 break;
1091
1092 case s_req_http_H:
1093 STRICT_CHECK(ch != 'T');
1094 parser->state = s_req_http_HT;
1095 break;
1096
1097 case s_req_http_HT:
1098 STRICT_CHECK(ch != 'T');
1099 parser->state = s_req_http_HTT;
1100 break;
1101
1102 case s_req_http_HTT:
1103 STRICT_CHECK(ch != 'P');
1104 parser->state = s_req_http_HTTP;
1105 break;
1106
1107 case s_req_http_HTTP:
1108 STRICT_CHECK(ch != '/');
1109 parser->state = s_req_first_http_major;
1110 break;
1111
1112 /* first digit of major HTTP version */
1113 case s_req_first_http_major:
1114 if (ch < '1' || ch > '9') {
1115 SET_ERRNO(HPE_INVALID_VERSION);
1116 goto error;
1117 }
1118
1119 parser->http_major = ch - '0';
1120 parser->state = s_req_http_major;
1121 break;
1122
1123 /* major HTTP version or dot */
1124 case s_req_http_major:
1125 {
1126 if (ch == '.') {
1127 parser->state = s_req_first_http_minor;
1128 break;
1129 }
1130
1131 if (!IS_NUM(ch)) {
1132 SET_ERRNO(HPE_INVALID_VERSION);
1133 goto error;
1134 }
1135
1136 parser->http_major *= 10;
1137 parser->http_major += ch - '0';
1138
1139 if (parser->http_major > 999) {
1140 SET_ERRNO(HPE_INVALID_VERSION);
1141 goto error;
1142 }
1143
1144 break;
1145 }
1146
1147 /* first digit of minor HTTP version */
1148 case s_req_first_http_minor:
1149 if (!IS_NUM(ch)) {
1150 SET_ERRNO(HPE_INVALID_VERSION);
1151 goto error;
1152 }
1153
1154 parser->http_minor = ch - '0';
1155 parser->state = s_req_http_minor;
1156 break;
1157
1158 /* minor HTTP version or end of request line */
1159 case s_req_http_minor:
1160 {
1161 if (ch == CR) {
1162 parser->state = s_req_line_almost_done;
1163 break;
1164 }
1165
1166 if (ch == LF) {
1167 parser->state = s_header_field_start;
1168 break;
1169 }
1170
1171 /* XXX allow spaces after digit? */
1172
1173 if (!IS_NUM(ch)) {
1174 SET_ERRNO(HPE_INVALID_VERSION);
1175 goto error;
1176 }
1177
1178 parser->http_minor *= 10;
1179 parser->http_minor += ch - '0';
1180
1181 if (parser->http_minor > 999) {
1182 SET_ERRNO(HPE_INVALID_VERSION);
1183 goto error;
1184 }
1185
1186 break;
1187 }
1188
1189 /* end of request line */
1190 case s_req_line_almost_done:
1191 {
1192 if (ch != LF) {
1193 SET_ERRNO(HPE_LF_EXPECTED);
1194 goto error;
1195 }
1196
1197 parser->state = s_header_field_start;
1198 break;
1199 }
1200
1201 case s_header_field_start:
1202 {
1203 if (ch == CR) {
1204 parser->state = s_headers_almost_done;
1205 break;
1206 }
1207
1208 if (ch == LF) {
1209 /* they might be just sending \n instead of \r\n so this would be
1210 * the second \n to denote the end of headers*/
1211 parser->state = s_headers_almost_done;
1212 goto reexecute_byte;
1213 }
1214
1215 c = TOKEN(ch);
1216
1217 if (!c) {
1218 SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
1219 goto error;
1220 }
1221
1222 MARK(header_field);
1223
1224 parser->index = 0;
1225 parser->state = s_header_field;
1226
1227 switch (c) {
1228 case 'c':
1229 parser->header_state = h_C;
1230 break;
1231
1232 case 'p':
1233 parser->header_state = h_matching_proxy_connection;
1234 break;
1235
1236 case 't':
1237 parser->header_state = h_matching_transfer_encoding;
1238 break;
1239
1240 case 'u':
1241 parser->header_state = h_matching_upgrade;
1242 break;
1243
1244 default:
1245 parser->header_state = h_general;
1246 break;
1247 }
1248 break;
1249 }
1250
1251 case s_header_field:
1252 {
1253 c = TOKEN(ch);
1254
1255 if (c) {
1256 switch (parser->header_state) {
1257 case h_general:
1258 break;
1259
1260 case h_C:
1261 parser->index++;
1262 parser->header_state = (c == 'o' ? h_CO : h_general);
1263 break;
1264
1265 case h_CO:
1266 parser->index++;
1267 parser->header_state = (c == 'n' ? h_CON : h_general);
1268 break;
1269
1270 case h_CON:
1271 parser->index++;
1272 switch (c) {
1273 case 'n':
1274 parser->header_state = h_matching_connection;
1275 break;
1276 case 't':
1277 parser->header_state = h_matching_content_length;
1278 break;
1279 default:
1280 parser->header_state = h_general;
1281 break;
1282 }
1283 break;
1284
1285 /* connection */
1286
1287 case h_matching_connection:
1288 parser->index++;
1289 if (parser->index > sizeof(CONNECTION)-1
1290 || c != CONNECTION[parser->index]) {
1291 parser->header_state = h_general;
1292 } else if (parser->index == sizeof(CONNECTION)-2) {
1293 parser->header_state = h_connection;
1294 }
1295 break;
1296
1297 /* proxy-connection */
1298
1299 case h_matching_proxy_connection:
1300 parser->index++;
1301 if (parser->index > sizeof(PROXY_CONNECTION)-1
1302 || c != PROXY_CONNECTION[parser->index]) {
1303 parser->header_state = h_general;
1304 } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
1305 parser->header_state = h_connection;
1306 }
1307 break;
1308
1309 /* content-length */
1310
1311 case h_matching_content_length:
1312 parser->index++;
1313 if (parser->index > sizeof(CONTENT_LENGTH)-1
1314 || c != CONTENT_LENGTH[parser->index]) {
1315 parser->header_state = h_general;
1316 } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
1317 parser->header_state = h_content_length;
1318 }
1319 break;
1320
1321 /* transfer-encoding */
1322
1323 case h_matching_transfer_encoding:
1324 parser->index++;
1325 if (parser->index > sizeof(TRANSFER_ENCODING)-1
1326 || c != TRANSFER_ENCODING[parser->index]) {
1327 parser->header_state = h_general;
1328 } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
1329 parser->header_state = h_transfer_encoding;
1330 }
1331 break;
1332
1333 /* upgrade */
1334
1335 case h_matching_upgrade:
1336 parser->index++;
1337 if (parser->index > sizeof(UPGRADE)-1
1338 || c != UPGRADE[parser->index]) {
1339 parser->header_state = h_general;
1340 } else if (parser->index == sizeof(UPGRADE)-2) {
1341 parser->header_state = h_upgrade;
1342 }
1343 break;
1344
1345 case h_connection:
1346 case h_content_length:
1347 case h_transfer_encoding:
1348 case h_upgrade:
1349 if (ch != ' ') parser->header_state = h_general;
1350 break;
1351
1352 default:
1353 assert(0 && "Unknown header_state");
1354 break;
1355 }
1356 break;
1357 }
1358
1359 if (ch == ':') {
1360 parser->state = s_header_value_start;
1361 CALLBACK_DATA(header_field);
1362 break;
1363 }
1364
1365 if (ch == CR) {
1366 parser->state = s_header_almost_done;
1367 CALLBACK_DATA(header_field);
1368 break;
1369 }
1370
1371 if (ch == LF) {
1372 parser->state = s_header_field_start;
1373 CALLBACK_DATA(header_field);
1374 break;
1375 }
1376
1377 SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
1378 goto error;
1379 }
1380
1381 case s_header_value_start:
1382 {
1383 if (ch == ' ' || ch == '\t') break;
1384
1385 MARK(header_value);
1386
1387 parser->state = s_header_value;
1388 parser->index = 0;
1389
1390 if (ch == CR) {
1391 parser->header_state = h_general;
1392 parser->state = s_header_almost_done;
1393 CALLBACK_DATA(header_value);
1394 break;
1395 }
1396
1397 if (ch == LF) {
1398 parser->state = s_header_field_start;
1399 CALLBACK_DATA(header_value);
1400 break;
1401 }
1402
1403 c = LOWER(ch);
1404
1405 switch (parser->header_state) {
1406 case h_upgrade:
1407 parser->flags |= F_UPGRADE;
1408 parser->header_state = h_general;
1409 break;
1410
1411 case h_transfer_encoding:
1412 /* looking for 'Transfer-Encoding: chunked' */
1413 if ('c' == c) {
1414 parser->header_state = h_matching_transfer_encoding_chunked;
1415 } else {
1416 parser->header_state = h_general;
1417 }
1418 break;
1419
1420 case h_content_length:
1421 if (!IS_NUM(ch)) {
1422 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1423 goto error;
1424 }
1425
1426 parser->content_length = ch - '0';
1427 break;
1428
1429 case h_connection:
1430 /* looking for 'Connection: keep-alive' */
1431 if (c == 'k') {
1432 parser->header_state = h_matching_connection_keep_alive;
1433 /* looking for 'Connection: close' */
1434 } else if (c == 'c') {
1435 parser->header_state = h_matching_connection_close;
1436 } else {
1437 parser->header_state = h_general;
1438 }
1439 break;
1440
1441 default:
1442 parser->header_state = h_general;
1443 break;
1444 }
1445 break;
1446 }
1447
1448 case s_header_value:
1449 {
1450
1451 if (ch == CR) {
1452 parser->state = s_header_almost_done;
1453 CALLBACK_DATA(header_value);
1454 break;
1455 }
1456
1457 if (ch == LF) {
1458 parser->state = s_header_almost_done;
1459 CALLBACK_DATA_NOADVANCE(header_value);
1460 goto reexecute_byte;
1461 }
1462
1463 c = LOWER(ch);
1464
1465 switch (parser->header_state) {
1466 case h_general:
1467 break;
1468
1469 case h_connection:
1470 case h_transfer_encoding:
1471 assert(0 && "Shouldn't get here.");
1472 break;
1473
1474 case h_content_length:
1475 {
1476 uint64_t t;
1477
1478 if (ch == ' ') break;
1479
1480 if (!IS_NUM(ch)) {
1481 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1482 goto error;
1483 }
1484
1485 t = parser->content_length;
1486 t *= 10;
1487 t += ch - '0';
1488
1489 /* Overflow? */
1490 if (t < parser->content_length || t == ULLONG_MAX) {
1491 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1492 goto error;
1493 }
1494
1495 parser->content_length = t;
1496 break;
1497 }
1498
1499 /* Transfer-Encoding: chunked */
1500 case h_matching_transfer_encoding_chunked:
1501 parser->index++;
1502 if (parser->index > sizeof(CHUNKED)-1
1503 || c != CHUNKED[parser->index]) {
1504 parser->header_state = h_general;
1505 } else if (parser->index == sizeof(CHUNKED)-2) {
1506 parser->header_state = h_transfer_encoding_chunked;
1507 }
1508 break;
1509
1510 /* looking for 'Connection: keep-alive' */
1511 case h_matching_connection_keep_alive:
1512 parser->index++;
1513 if (parser->index > sizeof(KEEP_ALIVE)-1
1514 || c != KEEP_ALIVE[parser->index]) {
1515 parser->header_state = h_general;
1516 } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
1517 parser->header_state = h_connection_keep_alive;
1518 }
1519 break;
1520
1521 /* looking for 'Connection: close' */
1522 case h_matching_connection_close:
1523 parser->index++;
1524 if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
1525 parser->header_state = h_general;
1526 } else if (parser->index == sizeof(CLOSE)-2) {
1527 parser->header_state = h_connection_close;
1528 }
1529 break;
1530
1531 case h_transfer_encoding_chunked:
1532 case h_connection_keep_alive:
1533 case h_connection_close:
1534 if (ch != ' ') parser->header_state = h_general;
1535 break;
1536
1537 default:
1538 parser->state = s_header_value;
1539 parser->header_state = h_general;
1540 break;
1541 }
1542 break;
1543 }
1544
1545 case s_header_almost_done:
1546 {
1547 STRICT_CHECK(ch != LF);
1548
1549 parser->state = s_header_value_lws;
1550
1551 switch (parser->header_state) {
1552 case h_connection_keep_alive:
1553 parser->flags |= F_CONNECTION_KEEP_ALIVE;
1554 break;
1555 case h_connection_close:
1556 parser->flags |= F_CONNECTION_CLOSE;
1557 break;
1558 case h_transfer_encoding_chunked:
1559 parser->flags |= F_CHUNKED;
1560 break;
1561 default:
1562 break;
1563 }
1564
1565 break;
1566 }
1567
1568 case s_header_value_lws:
1569 {
1570 if (ch == ' ' || ch == '\t')
1571 parser->state = s_header_value_start;
1572 else
1573 {
1574 parser->state = s_header_field_start;
1575 goto reexecute_byte;
1576 }
1577 break;
1578 }
1579
1580 case s_headers_almost_done:
1581 {
1582 STRICT_CHECK(ch != LF);
1583
1584 if (parser->flags & F_TRAILING) {
1585 /* End of a chunked request */
1586 parser->state = NEW_MESSAGE();
1587 CALLBACK_NOTIFY(message_complete);
1588 break;
1589 }
1590
1591 parser->state = s_headers_done;
1592
1593 /* Set this here so that on_headers_complete() callbacks can see it */
1594 parser->upgrade =
1595 (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
1596
1597 /* Here we call the headers_complete callback. This is somewhat
1598 * different than other callbacks because if the user returns 1, we
1599 * will interpret that as saying that this message has no body. This
1600 * is needed for the annoying case of recieving a response to a HEAD
1601 * request.
1602 *
1603 * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
1604 * we have to simulate it by handling a change in errno below.
1605 */
1606 if (settings->on_headers_complete) {
1607 switch (settings->on_headers_complete(parser)) {
1608 case 0:
1609 break;
1610
1611 case 1:
1612 parser->flags |= F_SKIPBODY;
1613 break;
1614
1615 default:
1616 SET_ERRNO(HPE_CB_headers_complete);
1617 return p - data; /* Error */
1618 }
1619 }
1620
1621 if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
1622 return p - data;
1623 }
1624
1625 goto reexecute_byte;
1626 }
1627
1628 case s_headers_done:
1629 {
1630 STRICT_CHECK(ch != LF);
1631
1632 parser->nread = 0;
1633
1634 /* Exit, the rest of the connect is in a different protocol. */
1635 if (parser->upgrade) {
1636 parser->state = NEW_MESSAGE();
1637 CALLBACK_NOTIFY(message_complete);
1638 return (p - data) + 1;
1639 }
1640
1641 if (parser->flags & F_SKIPBODY) {
1642 parser->state = NEW_MESSAGE();
1643 CALLBACK_NOTIFY(message_complete);
1644 } else if (parser->flags & F_CHUNKED) {
1645 /* chunked encoding - ignore Content-Length header */
1646 parser->state = s_chunk_size_start;
1647 } else {
1648 if (parser->content_length == 0) {
1649 /* Content-Length header given but zero: Content-Length: 0\r\n */
1650 parser->state = NEW_MESSAGE();
1651 CALLBACK_NOTIFY(message_complete);
1652 } else if (parser->content_length != ULLONG_MAX) {
1653 /* Content-Length header given and non-zero */
1654 parser->state = s_body_identity;
1655 } else {
1656 if (parser->type == HTTP_REQUEST ||
1657 !http_message_needs_eof(parser)) {
1658 /* Assume content-length 0 - read the next */
1659 parser->state = NEW_MESSAGE();
1660 CALLBACK_NOTIFY(message_complete);
1661 } else {
1662 /* Read body until EOF */
1663 parser->state = s_body_identity_eof;
1664 }
1665 }
1666 }
1667
1668 break;
1669 }
1670
1671 case s_body_identity:
1672 {
1673 uint64_t to_read = MIN(parser->content_length,
1674 (uint64_t) ((data + len) - p));
1675
1676 assert(parser->content_length != 0
1677 && parser->content_length != ULLONG_MAX);
1678
1679 /* The difference between advancing content_length and p is because
1680 * the latter will automaticaly advance on the next loop iteration.
1681 * Further, if content_length ends up at 0, we want to see the last
1682 * byte again for our message complete callback.
1683 */
1684 MARK(body);
1685 parser->content_length -= to_read;
1686 p += to_read - 1;
1687
1688 if (parser->content_length == 0) {
1689 parser->state = s_message_done;
1690
1691 /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
1692 *
1693 * The alternative to doing this is to wait for the next byte to
1694 * trigger the data callback, just as in every other case. The
1695 * problem with this is that this makes it difficult for the test
1696 * harness to distinguish between complete-on-EOF and
1697 * complete-on-length. It's not clear that this distinction is
1698 * important for applications, but let's keep it for now.
1699 */
1700 CALLBACK_DATA_(body, p - body_mark + 1, p - data);
1701 goto reexecute_byte;
1702 }
1703
1704 break;
1705 }
1706
1707 /* read until EOF */
1708 case s_body_identity_eof:
1709 MARK(body);
1710 p = data + len - 1;
1711
1712 break;
1713
1714 case s_message_done:
1715 parser->state = NEW_MESSAGE();
1716 CALLBACK_NOTIFY(message_complete);
1717 break;
1718
1719 case s_chunk_size_start:
1720 {
1721 assert(parser->nread == 1);
1722 assert(parser->flags & F_CHUNKED);
1723
1724 unhex_val = unhex[(unsigned char)ch];
1725 if (unhex_val == -1) {
1726 SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
1727 goto error;
1728 }
1729
1730 parser->content_length = unhex_val;
1731 parser->state = s_chunk_size;
1732 break;
1733 }
1734
1735 case s_chunk_size:
1736 {
1737 uint64_t t;
1738
1739 assert(parser->flags & F_CHUNKED);
1740
1741 if (ch == CR) {
1742 parser->state = s_chunk_size_almost_done;
1743 break;
1744 }
1745
1746 unhex_val = unhex[(unsigned char)ch];
1747
1748 if (unhex_val == -1) {
1749 if (ch == ';' || ch == ' ') {
1750 parser->state = s_chunk_parameters;
1751 break;
1752 }
1753
1754 SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
1755 goto error;
1756 }
1757
1758 t = parser->content_length;
1759 t *= 16;
1760 t += unhex_val;
1761
1762 /* Overflow? */
1763 if (t < parser->content_length || t == ULLONG_MAX) {
1764 SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
1765 goto error;
1766 }
1767
1768 parser->content_length = t;
1769 break;
1770 }
1771
1772 case s_chunk_parameters:
1773 {
1774 assert(parser->flags & F_CHUNKED);
1775 /* just ignore this shit. TODO check for overflow */
1776 if (ch == CR) {
1777 parser->state = s_chunk_size_almost_done;
1778 break;
1779 }
1780 break;
1781 }
1782
1783 case s_chunk_size_almost_done:
1784 {
1785 assert(parser->flags & F_CHUNKED);
1786 STRICT_CHECK(ch != LF);
1787
1788 parser->nread = 0;
1789
1790 if (parser->content_length == 0) {
1791 parser->flags |= F_TRAILING;
1792 parser->state = s_header_field_start;
1793 } else {
1794 parser->state = s_chunk_data;
1795 }
1796 break;
1797 }
1798
1799 case s_chunk_data:
1800 {
1801 uint64_t to_read = MIN(parser->content_length,
1802 (uint64_t) ((data + len) - p));
1803
1804 assert(parser->flags & F_CHUNKED);
1805 assert(parser->content_length != 0
1806 && parser->content_length != ULLONG_MAX);
1807
1808 /* See the explanation in s_body_identity for why the content
1809 * length and data pointers are managed this way.
1810 */
1811 MARK(body);
1812 parser->content_length -= to_read;
1813 p += to_read - 1;
1814
1815 if (parser->content_length == 0) {
1816 parser->state = s_chunk_data_almost_done;
1817 }
1818
1819 break;
1820 }
1821
1822 case s_chunk_data_almost_done:
1823 assert(parser->flags & F_CHUNKED);
1824 assert(parser->content_length == 0);
1825 STRICT_CHECK(ch != CR);
1826 parser->state = s_chunk_data_done;
1827 CALLBACK_DATA(body);
1828 break;
1829
1830 case s_chunk_data_done:
1831 assert(parser->flags & F_CHUNKED);
1832 STRICT_CHECK(ch != LF);
1833 parser->nread = 0;
1834 parser->state = s_chunk_size_start;
1835 break;
1836
1837 default:
1838 assert(0 && "unhandled state");
1839 SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
1840 goto error;
1841 }
1842 }
1843
1844 /* Run callbacks for any marks that we have leftover after we ran our of
1845 * bytes. There should be at most one of these set, so it's OK to invoke
1846 * them in series (unset marks will not result in callbacks).
1847 *
1848 * We use the NOADVANCE() variety of callbacks here because 'p' has already
1849 * overflowed 'data' and this allows us to correct for the off-by-one that
1850 * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
1851 * value that's in-bounds).
1852 */
1853
1854 assert(((header_field_mark ? 1 : 0) +
1855 (header_value_mark ? 1 : 0) +
1856 (url_mark ? 1 : 0) +
1857 (body_mark ? 1 : 0)) <= 1);
1858
1859 CALLBACK_DATA_NOADVANCE(header_field);
1860 CALLBACK_DATA_NOADVANCE(header_value);
1861 CALLBACK_DATA_NOADVANCE(url);
1862 CALLBACK_DATA_NOADVANCE(body);
1863
1864 return len;
1865
1866error:
1867 if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
1868 SET_ERRNO(HPE_UNKNOWN);
1869 }
1870
1871 return (p - data);
1872}
1873
1874
1875/* Does the parser need to see an EOF to find the end of the message? */
1876int
1877http_message_needs_eof (const http_parser *parser)
1878{
1879 if (parser->type == HTTP_REQUEST) {
1880 return 0;
1881 }
1882
1883 /* See RFC 2616 section 4.4 */
1884 if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
1885 parser->status_code == 204 || /* No Content */
1886 parser->status_code == 304 || /* Not Modified */
1887 parser->flags & F_SKIPBODY) { /* response to a HEAD request */
1888 return 0;
1889 }
1890
1891 if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
1892 return 0;
1893 }
1894
1895 return 1;
1896}
1897
1898
1899int
1900http_should_keep_alive (const http_parser *parser)
1901{
1902 if (parser->http_major > 0 && parser->http_minor > 0) {
1903 /* HTTP/1.1 */
1904 if (parser->flags & F_CONNECTION_CLOSE) {
1905 return 0;
1906 }
1907 } else {
1908 /* HTTP/1.0 or earlier */
1909 if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
1910 return 0;
1911 }
1912 }
1913
1914 return !http_message_needs_eof(parser);
1915}
1916
1917
1918const char *
1919http_method_str (enum http_method m)
1920{
1921 return ELEM_AT(method_strings, m, "<unknown>");
1922}
1923
1924
1925void
1926http_parser_init (http_parser *parser, enum http_parser_type t)
1927{
1928 void *data = parser->data; /* preserve application data */
1929 memset(parser, 0, sizeof(*parser));
1930 parser->data = data;
1931 parser->type = t;
1932 parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
1933 parser->http_errno = HPE_OK;
1934}
1935
1936const char *
1937http_errno_name(enum http_errno err) {
1938 assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
1939 return http_strerror_tab[err].name;
1940}
1941
1942const char *
1943http_errno_description(enum http_errno err) {
1944 assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
1945 return http_strerror_tab[err].description;
1946}
1947
1948static enum http_host_state
1949http_parse_host_char(enum http_host_state s, const char ch) {
1950 switch(s) {
1951 case s_http_userinfo:
1952 case s_http_userinfo_start:
1953 if (ch == '@') {
1954 return s_http_host_start;
1955 }
1956
1957 if (IS_USERINFO_CHAR(ch)) {
1958 return s_http_userinfo;
1959 }
1960 break;
1961
1962 case s_http_host_start:
1963 if (ch == '[') {
1964 return s_http_host_v6_start;
1965 }
1966
1967 if (IS_HOST_CHAR(ch)) {
1968 return s_http_host;
1969 }
1970
1971 break;
1972
1973 case s_http_host:
1974 if (IS_HOST_CHAR(ch)) {
1975 return s_http_host;
1976 }
1977
1978 /* FALLTHROUGH */
1979 case s_http_host_v6_end:
1980 if (ch == ':') {
1981 return s_http_host_port_start;
1982 }
1983
1984 break;
1985
1986 case s_http_host_v6:
1987 if (ch == ']') {
1988 return s_http_host_v6_end;
1989 }
1990
1991 /* FALLTHROUGH */
1992 case s_http_host_v6_start:
1993 if (IS_HEX(ch) || ch == ':' || ch == '.') {
1994 return s_http_host_v6;
1995 }
1996
1997 break;
1998
1999 case s_http_host_port:
2000 case s_http_host_port_start:
2001 if (IS_NUM(ch)) {
2002 return s_http_host_port;
2003 }
2004
2005 break;
2006
2007 default:
2008 break;
2009 }
2010 return s_http_host_dead;
2011}
2012
2013static int
2014http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
2015 enum http_host_state s;
2016
2017 const char *p;
2018 size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
2019
2020 u->field_data[UF_HOST].len = 0;
2021
2022 s = found_at ? s_http_userinfo_start : s_http_host_start;
2023
2024 for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
2025 enum http_host_state new_s = http_parse_host_char(s, *p);
2026
2027 if (new_s == s_http_host_dead) {
2028 return 1;
2029 }
2030
2031 switch(new_s) {
2032 case s_http_host:
2033 if (s != s_http_host) {
2034 u->field_data[UF_HOST].off = p - buf;
2035 }
2036 u->field_data[UF_HOST].len++;
2037 break;
2038
2039 case s_http_host_v6:
2040 if (s != s_http_host_v6) {
2041 u->field_data[UF_HOST].off = p - buf;
2042 }
2043 u->field_data[UF_HOST].len++;
2044 break;
2045
2046 case s_http_host_port:
2047 if (s != s_http_host_port) {
2048 u->field_data[UF_PORT].off = p - buf;
2049 u->field_data[UF_PORT].len = 0;
2050 u->field_set |= (1 << UF_PORT);
2051 }
2052 u->field_data[UF_PORT].len++;
2053 break;
2054
2055 case s_http_userinfo:
2056 if (s != s_http_userinfo) {
2057 u->field_data[UF_USERINFO].off = p - buf ;
2058 u->field_data[UF_USERINFO].len = 0;
2059 u->field_set |= (1 << UF_USERINFO);
2060 }
2061 u->field_data[UF_USERINFO].len++;
2062 break;
2063
2064 default:
2065 break;
2066 }
2067 s = new_s;
2068 }
2069
2070 /* Make sure we don't end somewhere unexpected */
2071 switch (s) {
2072 case s_http_host_start:
2073 case s_http_host_v6_start:
2074 case s_http_host_v6:
2075 case s_http_host_port_start:
2076 case s_http_userinfo:
2077 case s_http_userinfo_start:
2078 return 1;
2079 default:
2080 break;
2081 }
2082
2083 return 0;
2084}
2085
2086int
2087http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
2088 struct http_parser_url *u)
2089{
2090 enum state s;
2091 const char *p;
2092 enum http_parser_url_fields uf, old_uf;
2093 int found_at = 0;
2094
2095 u->port = u->field_set = 0;
2096 s = is_connect ? s_req_server_start : s_req_spaces_before_url;
2097 uf = old_uf = UF_MAX;
2098
2099 for (p = buf; p < buf + buflen; p++) {
2100 s = parse_url_char(s, *p);
2101
2102 /* Figure out the next field that we're operating on */
2103 switch (s) {
2104 case s_dead:
2105 return 1;
2106
2107 /* Skip delimeters */
2108 case s_req_schema_slash:
2109 case s_req_schema_slash_slash:
2110 case s_req_server_start:
2111 case s_req_query_string_start:
2112 case s_req_fragment_start:
2113 continue;
2114
2115 case s_req_schema:
2116 uf = UF_SCHEMA;
2117 break;
2118
2119 case s_req_server_with_at:
2120 found_at = 1;
2121
2122 /* FALLTROUGH */
2123 case s_req_server:
2124 uf = UF_HOST;
2125 break;
2126
2127 case s_req_path:
2128 uf = UF_PATH;
2129 break;
2130
2131 case s_req_query_string:
2132 uf = UF_QUERY;
2133 break;
2134
2135 case s_req_fragment:
2136 uf = UF_FRAGMENT;
2137 break;
2138
2139 default:
2140 assert(!"Unexpected state");
2141 return 1;
2142 }
2143
2144 /* Nothing's changed; soldier on */
2145 if (uf == old_uf) {
2146 u->field_data[uf].len++;
2147 continue;
2148 }
2149
2150 u->field_data[uf].off = p - buf;
2151 u->field_data[uf].len = 1;
2152
2153 u->field_set |= (1 << uf);
2154 old_uf = uf;
2155 }
2156
2157 /* host must be present if there is a schema */
2158 /* parsing http:///toto will fail */
2159 if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
2160 if (http_parse_host(buf, u, found_at) != 0) {
2161 return 1;
2162 }
2163 }
2164
2165 /* CONNECT requests can only contain "hostname:port" */
2166 if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
2167 return 1;
2168 }
2169
2170 if (u->field_set & (1 << UF_PORT)) {
2171 /* Don't bother with endp; we've already validated the string */
2172 unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
2173
2174 /* Ports have a max value of 2^16 */
2175 if (v > 0xffff) {
2176 return 1;
2177 }
2178
2179 u->port = (uint16_t) v;
2180 }
2181
2182 return 0;
2183}
2184
2185void
2186http_parser_pause(http_parser *parser, int paused) {
2187 /* Users should only be pausing/unpausing a parser that is not in an error
2188 * state. In non-debug builds, there's not much that we can do about this
2189 * other than ignore it.
2190 */
2191 if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
2192 HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
2193 SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
2194 } else {
2195 assert(0 && "Attempting to pause parser in error state");
2196 }
2197}
2198
2199int
2200http_body_is_final(const struct http_parser *parser) {
2201 return parser->state == s_message_done;
2202}
2203
2204unsigned long
2205http_parser_version(void) {
2206 return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
2207 HTTP_PARSER_VERSION_MINOR * 0x00100 |
2208 HTTP_PARSER_VERSION_PATCH * 0x00001;
2209}
diff --git a/src/static_libs/http-parser/http_parser.h b/src/static_libs/http-parser/http_parser.h
new file mode 100644
index 0000000000..4810cdcd24
--- /dev/null
+++ b/src/static_libs/http-parser/http_parser.h
@@ -0,0 +1,318 @@
1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21#ifndef http_parser_h
22#define http_parser_h
23#ifdef __cplusplus
24extern "C" {
25#endif
26
27/* Also update SONAME in the Makefile whenever you change these. */
28#define HTTP_PARSER_VERSION_MAJOR 2
29#define HTTP_PARSER_VERSION_MINOR 1
30#define HTTP_PARSER_VERSION_PATCH 0
31
32#include <sys/types.h>
33#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
34#include <BaseTsd.h>
35#include <stddef.h>
36typedef __int8 int8_t;
37typedef unsigned __int8 uint8_t;
38typedef __int16 int16_t;
39typedef unsigned __int16 uint16_t;
40typedef __int32 int32_t;
41typedef unsigned __int32 uint32_t;
42typedef __int64 int64_t;
43typedef unsigned __int64 uint64_t;
44#else
45#include <stdint.h>
46#endif
47
48/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
49 * faster
50 */
51#ifndef HTTP_PARSER_STRICT
52# define HTTP_PARSER_STRICT 1
53#endif
54
55/* Maximium header size allowed */
56#define HTTP_MAX_HEADER_SIZE (80*1024)
57
58
59typedef struct http_parser http_parser;
60typedef struct http_parser_settings http_parser_settings;
61
62
63/* Callbacks should return non-zero to indicate an error. The parser will
64 * then halt execution.
65 *
66 * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
67 * returning '1' from on_headers_complete will tell the parser that it
68 * should not expect a body. This is used when receiving a response to a
69 * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
70 * chunked' headers that indicate the presence of a body.
71 *
72 * http_data_cb does not return data chunks. It will be call arbitrarally
73 * many times for each string. E.G. you might get 10 callbacks for "on_url"
74 * each providing just a few characters more data.
75 */
76typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
77typedef int (*http_cb) (http_parser*);
78
79
80/* Request Methods */
81#define HTTP_METHOD_MAP(XX) \
82 XX(0, DELETE, DELETE) \
83 XX(1, GET, GET) \
84 XX(2, HEAD, HEAD) \
85 XX(3, POST, POST) \
86 XX(4, PUT, PUT) \
87 /* pathological */ \
88 XX(5, CONNECT, CONNECT) \
89 XX(6, OPTIONS, OPTIONS) \
90 XX(7, TRACE, TRACE) \
91 /* webdav */ \
92 XX(8, COPY, COPY) \
93 XX(9, LOCK, LOCK) \
94 XX(10, MKCOL, MKCOL) \
95 XX(11, MOVE, MOVE) \
96 XX(12, PROPFIND, PROPFIND) \
97 XX(13, PROPPATCH, PROPPATCH) \
98 XX(14, SEARCH, SEARCH) \
99 XX(15, UNLOCK, UNLOCK) \
100 /* subversion */ \
101 XX(16, REPORT, REPORT) \
102 XX(17, MKACTIVITY, MKACTIVITY) \
103 XX(18, CHECKOUT, CHECKOUT) \
104 XX(19, MERGE, MERGE) \
105 /* upnp */ \
106 XX(20, MSEARCH, M-SEARCH) \
107 XX(21, NOTIFY, NOTIFY) \
108 XX(22, SUBSCRIBE, SUBSCRIBE) \
109 XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
110 /* RFC-5789 */ \
111 XX(24, PATCH, PATCH) \
112 XX(25, PURGE, PURGE) \
113
114enum http_method
115 {
116#define XX(num, name, string) HTTP_##name = num,
117 HTTP_METHOD_MAP(XX)
118#undef XX
119 };
120
121
122enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
123
124
125/* Flag values for http_parser.flags field */
126enum flags
127 { F_CHUNKED = 1 << 0
128 , F_CONNECTION_KEEP_ALIVE = 1 << 1
129 , F_CONNECTION_CLOSE = 1 << 2
130 , F_TRAILING = 1 << 3
131 , F_UPGRADE = 1 << 4
132 , F_SKIPBODY = 1 << 5
133 };
134
135
136/* Map for errno-related constants
137 *
138 * The provided argument should be a macro that takes 2 arguments.
139 */
140#define HTTP_ERRNO_MAP(XX) \
141 /* No error */ \
142 XX(OK, "success") \
143 \
144 /* Callback-related errors */ \
145 XX(CB_message_begin, "the on_message_begin callback failed") \
146 XX(CB_status_complete, "the on_status_complete callback failed") \
147 XX(CB_url, "the on_url callback failed") \
148 XX(CB_header_field, "the on_header_field callback failed") \
149 XX(CB_header_value, "the on_header_value callback failed") \
150 XX(CB_headers_complete, "the on_headers_complete callback failed") \
151 XX(CB_body, "the on_body callback failed") \
152 XX(CB_message_complete, "the on_message_complete callback failed") \
153 \
154 /* Parsing-related errors */ \
155 XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
156 XX(HEADER_OVERFLOW, \
157 "too many header bytes seen; overflow detected") \
158 XX(CLOSED_CONNECTION, \
159 "data received after completed connection: close message") \
160 XX(INVALID_VERSION, "invalid HTTP version") \
161 XX(INVALID_STATUS, "invalid HTTP status code") \
162 XX(INVALID_METHOD, "invalid HTTP method") \
163 XX(INVALID_URL, "invalid URL") \
164 XX(INVALID_HOST, "invalid host") \
165 XX(INVALID_PORT, "invalid port") \
166 XX(INVALID_PATH, "invalid path") \
167 XX(INVALID_QUERY_STRING, "invalid query string") \
168 XX(INVALID_FRAGMENT, "invalid fragment") \
169 XX(LF_EXPECTED, "LF character expected") \
170 XX(INVALID_HEADER_TOKEN, "invalid character in header") \
171 XX(INVALID_CONTENT_LENGTH, \
172 "invalid character in content-length header") \
173 XX(INVALID_CHUNK_SIZE, \
174 "invalid character in chunk size header") \
175 XX(INVALID_CONSTANT, "invalid constant string") \
176 XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
177 XX(STRICT, "strict mode assertion failed") \
178 XX(PAUSED, "parser is paused") \
179 XX(UNKNOWN, "an unknown error occurred")
180
181
182/* Define HPE_* values for each errno value above */
183#define HTTP_ERRNO_GEN(n, s) HPE_##n,
184enum http_errno {
185 HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
186};
187#undef HTTP_ERRNO_GEN
188
189
190/* Get an http_errno value from an http_parser */
191#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
192
193
194struct http_parser {
195 /** PRIVATE **/
196 unsigned int type : 2; /* enum http_parser_type */
197 unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */
198 unsigned int state : 8; /* enum state from http_parser.c */
199 unsigned int header_state : 8; /* enum header_state from http_parser.c */
200 unsigned int index : 8; /* index into current matcher */
201
202 uint32_t nread; /* # bytes read in various scenarios */
203 uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
204
205 /** READ-ONLY **/
206 unsigned short http_major;
207 unsigned short http_minor;
208 unsigned int status_code : 16; /* responses only */
209 unsigned int method : 8; /* requests only */
210 unsigned int http_errno : 7;
211
212 /* 1 = Upgrade header was present and the parser has exited because of that.
213 * 0 = No upgrade header present.
214 * Should be checked when http_parser_execute() returns in addition to
215 * error checking.
216 */
217 unsigned int upgrade : 1;
218
219 /** PUBLIC **/
220 void *data; /* A pointer to get hook to the "connection" or "socket" object */
221};
222
223
224struct http_parser_settings {
225 http_cb on_message_begin;
226 http_data_cb on_url;
227 http_cb on_status_complete;
228 http_data_cb on_header_field;
229 http_data_cb on_header_value;
230 http_cb on_headers_complete;
231 http_data_cb on_body;
232 http_cb on_message_complete;
233};
234
235
236enum http_parser_url_fields
237 { UF_SCHEMA = 0
238 , UF_HOST = 1
239 , UF_PORT = 2
240 , UF_PATH = 3
241 , UF_QUERY = 4
242 , UF_FRAGMENT = 5
243 , UF_USERINFO = 6
244 , UF_MAX = 7
245 };
246
247
248/* Result structure for http_parser_parse_url().
249 *
250 * Callers should index into field_data[] with UF_* values iff field_set
251 * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
252 * because we probably have padding left over), we convert any port to
253 * a uint16_t.
254 */
255struct http_parser_url {
256 uint16_t field_set; /* Bitmask of (1 << UF_*) values */
257 uint16_t port; /* Converted UF_PORT string */
258
259 struct {
260 uint16_t off; /* Offset into buffer in which field starts */
261 uint16_t len; /* Length of run in buffer */
262 } field_data[UF_MAX];
263};
264
265
266/* Returns the library version. Bits 16-23 contain the major version number,
267 * bits 8-15 the minor version number and bits 0-7 the patch level.
268 * Usage example:
269 *
270 * unsigned long version = http_parser_version();
271 * unsigned major = (version >> 16) & 255;
272 * unsigned minor = (version >> 8) & 255;
273 * unsigned patch = version & 255;
274 * printf("http_parser v%u.%u.%u\n", major, minor, version);
275 */
276unsigned long http_parser_version(void);
277
278void http_parser_init(http_parser *parser, enum http_parser_type type);
279
280
281size_t http_parser_execute(http_parser *parser,
282 const http_parser_settings *settings,
283 const char *data,
284 size_t len);
285
286
287/* If http_should_keep_alive() in the on_headers_complete or
288 * on_message_complete callback returns 0, then this should be
289 * the last message on the connection.
290 * If you are the server, respond with the "Connection: close" header.
291 * If you are the client, close the connection.
292 */
293int http_should_keep_alive(const http_parser *parser);
294
295/* Returns a string version of the HTTP method. */
296const char *http_method_str(enum http_method m);
297
298/* Return a string name of the given error */
299const char *http_errno_name(enum http_errno err);
300
301/* Return a string description of the given error */
302const char *http_errno_description(enum http_errno err);
303
304/* Parse a URL; return nonzero on failure */
305int http_parser_parse_url(const char *buf, size_t buflen,
306 int is_connect,
307 struct http_parser_url *u);
308
309/* Pause or un-pause the parser; a nonzero value pauses */
310void http_parser_pause(http_parser *parser, int paused);
311
312/* Checks if this is the final chunk of the body. */
313int http_body_is_final(const http_parser *parser);
314
315#ifdef __cplusplus
316}
317#endif
318#endif
diff --git a/src/static_libs/http-parser/test.c b/src/static_libs/http-parser/test.c
new file mode 100644
index 0000000000..656bc9f86f
--- /dev/null
+++ b/src/static_libs/http-parser/test.c
@@ -0,0 +1,3476 @@
1/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21#include "http_parser.h"
22#include <stdlib.h>
23#include <assert.h>
24#include <stdio.h>
25#include <stdlib.h> /* rand */
26#include <string.h>
27#include <stdarg.h>
28
29#undef TRUE
30#define TRUE 1
31#undef FALSE
32#define FALSE 0
33
34#define MAX_HEADERS 13
35#define MAX_ELEMENT_SIZE 2048
36
37#define MIN(a,b) ((a) < (b) ? (a) : (b))
38
39static http_parser *parser;
40
41struct message {
42 const char *name; // for debugging purposes
43 const char *raw;
44 enum http_parser_type type;
45 enum http_method method;
46 int status_code;
47 char request_path[MAX_ELEMENT_SIZE];
48 char request_url[MAX_ELEMENT_SIZE];
49 char fragment[MAX_ELEMENT_SIZE];
50 char query_string[MAX_ELEMENT_SIZE];
51 char body[MAX_ELEMENT_SIZE];
52 size_t body_size;
53 const char *host;
54 const char *userinfo;
55 uint16_t port;
56 int num_headers;
57 enum { NONE=0, FIELD, VALUE } last_header_element;
58 char headers [MAX_HEADERS][2][MAX_ELEMENT_SIZE];
59 int should_keep_alive;
60
61 const char *upgrade; // upgraded body
62
63 unsigned short http_major;
64 unsigned short http_minor;
65
66 int message_begin_cb_called;
67 int headers_complete_cb_called;
68 int message_complete_cb_called;
69 int message_complete_on_eof;
70 int body_is_final;
71};
72
73static int currently_parsing_eof;
74
75static struct message messages[5];
76static int num_messages;
77static http_parser_settings *current_pause_parser;
78
79/* * R E Q U E S T S * */
80const struct message requests[] =
81#define CURL_GET 0
82{ {.name= "curl get"
83 ,.type= HTTP_REQUEST
84 ,.raw= "GET /test HTTP/1.1\r\n"
85 "User-Agent: curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1\r\n"
86 "Host: 0.0.0.0=5000\r\n"
87 "Accept: */*\r\n"
88 "\r\n"
89 ,.should_keep_alive= TRUE
90 ,.message_complete_on_eof= FALSE
91 ,.http_major= 1
92 ,.http_minor= 1
93 ,.method= HTTP_GET
94 ,.query_string= ""
95 ,.fragment= ""
96 ,.request_path= "/test"
97 ,.request_url= "/test"
98 ,.num_headers= 3
99 ,.headers=
100 { { "User-Agent", "curl/7.18.0 (i486-pc-linux-gnu) libcurl/7.18.0 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.1" }
101 , { "Host", "0.0.0.0=5000" }
102 , { "Accept", "*/*" }
103 }
104 ,.body= ""
105 }
106
107#define FIREFOX_GET 1
108, {.name= "firefox get"
109 ,.type= HTTP_REQUEST
110 ,.raw= "GET /favicon.ico HTTP/1.1\r\n"
111 "Host: 0.0.0.0=5000\r\n"
112 "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0\r\n"
113 "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
114 "Accept-Language: en-us,en;q=0.5\r\n"
115 "Accept-Encoding: gzip,deflate\r\n"
116 "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"
117 "Keep-Alive: 300\r\n"
118 "Connection: keep-alive\r\n"
119 "\r\n"
120 ,.should_keep_alive= TRUE
121 ,.message_complete_on_eof= FALSE
122 ,.http_major= 1
123 ,.http_minor= 1
124 ,.method= HTTP_GET
125 ,.query_string= ""
126 ,.fragment= ""
127 ,.request_path= "/favicon.ico"
128 ,.request_url= "/favicon.ico"
129 ,.num_headers= 8
130 ,.headers=
131 { { "Host", "0.0.0.0=5000" }
132 , { "User-Agent", "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9) Gecko/2008061015 Firefox/3.0" }
133 , { "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" }
134 , { "Accept-Language", "en-us,en;q=0.5" }
135 , { "Accept-Encoding", "gzip,deflate" }
136 , { "Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7" }
137 , { "Keep-Alive", "300" }
138 , { "Connection", "keep-alive" }
139 }
140 ,.body= ""
141 }
142
143#define DUMBFUCK 2
144, {.name= "dumbfuck"
145 ,.type= HTTP_REQUEST
146 ,.raw= "GET /dumbfuck HTTP/1.1\r\n"
147 "aaaaaaaaaaaaa:++++++++++\r\n"
148 "\r\n"
149 ,.should_keep_alive= TRUE
150 ,.message_complete_on_eof= FALSE
151 ,.http_major= 1
152 ,.http_minor= 1
153 ,.method= HTTP_GET
154 ,.query_string= ""
155 ,.fragment= ""
156 ,.request_path= "/dumbfuck"
157 ,.request_url= "/dumbfuck"
158 ,.num_headers= 1
159 ,.headers=
160 { { "aaaaaaaaaaaaa", "++++++++++" }
161 }
162 ,.body= ""
163 }
164
165#define FRAGMENT_IN_URI 3
166, {.name= "fragment in url"
167 ,.type= HTTP_REQUEST
168 ,.raw= "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n"
169 "\r\n"
170 ,.should_keep_alive= TRUE
171 ,.message_complete_on_eof= FALSE
172 ,.http_major= 1
173 ,.http_minor= 1
174 ,.method= HTTP_GET
175 ,.query_string= "page=1"
176 ,.fragment= "posts-17408"
177 ,.request_path= "/forums/1/topics/2375"
178 /* XXX request url does include fragment? */
179 ,.request_url= "/forums/1/topics/2375?page=1#posts-17408"
180 ,.num_headers= 0
181 ,.body= ""
182 }
183
184#define GET_NO_HEADERS_NO_BODY 4
185, {.name= "get no headers no body"
186 ,.type= HTTP_REQUEST
187 ,.raw= "GET /get_no_headers_no_body/world HTTP/1.1\r\n"
188 "\r\n"
189 ,.should_keep_alive= TRUE
190 ,.message_complete_on_eof= FALSE /* would need Connection: close */
191 ,.http_major= 1
192 ,.http_minor= 1
193 ,.method= HTTP_GET
194 ,.query_string= ""
195 ,.fragment= ""
196 ,.request_path= "/get_no_headers_no_body/world"
197 ,.request_url= "/get_no_headers_no_body/world"
198 ,.num_headers= 0
199 ,.body= ""
200 }
201
202#define GET_ONE_HEADER_NO_BODY 5
203, {.name= "get one header no body"
204 ,.type= HTTP_REQUEST
205 ,.raw= "GET /get_one_header_no_body HTTP/1.1\r\n"
206 "Accept: */*\r\n"
207 "\r\n"
208 ,.should_keep_alive= TRUE
209 ,.message_complete_on_eof= FALSE /* would need Connection: close */
210 ,.http_major= 1
211 ,.http_minor= 1
212 ,.method= HTTP_GET
213 ,.query_string= ""
214 ,.fragment= ""
215 ,.request_path= "/get_one_header_no_body"
216 ,.request_url= "/get_one_header_no_body"
217 ,.num_headers= 1
218 ,.headers=
219 { { "Accept" , "*/*" }
220 }
221 ,.body= ""
222 }
223
224#define GET_FUNKY_CONTENT_LENGTH 6
225, {.name= "get funky content length body hello"
226 ,.type= HTTP_REQUEST
227 ,.raw= "GET /get_funky_content_length_body_hello HTTP/1.0\r\n"
228 "conTENT-Length: 5\r\n"
229 "\r\n"
230 "HELLO"
231 ,.should_keep_alive= FALSE
232 ,.message_complete_on_eof= FALSE
233 ,.http_major= 1
234 ,.http_minor= 0
235 ,.method= HTTP_GET
236 ,.query_string= ""
237 ,.fragment= ""
238 ,.request_path= "/get_funky_content_length_body_hello"
239 ,.request_url= "/get_funky_content_length_body_hello"
240 ,.num_headers= 1
241 ,.headers=
242 { { "conTENT-Length" , "5" }
243 }
244 ,.body= "HELLO"
245 }
246
247#define POST_IDENTITY_BODY_WORLD 7
248, {.name= "post identity body world"
249 ,.type= HTTP_REQUEST
250 ,.raw= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
251 "Accept: */*\r\n"
252 "Transfer-Encoding: identity\r\n"
253 "Content-Length: 5\r\n"
254 "\r\n"
255 "World"
256 ,.should_keep_alive= TRUE
257 ,.message_complete_on_eof= FALSE
258 ,.http_major= 1
259 ,.http_minor= 1
260 ,.method= HTTP_POST
261 ,.query_string= "q=search"
262 ,.fragment= "hey"
263 ,.request_path= "/post_identity_body_world"
264 ,.request_url= "/post_identity_body_world?q=search#hey"
265 ,.num_headers= 3
266 ,.headers=
267 { { "Accept", "*/*" }
268 , { "Transfer-Encoding", "identity" }
269 , { "Content-Length", "5" }
270 }
271 ,.body= "World"
272 }
273
274#define POST_CHUNKED_ALL_YOUR_BASE 8
275, {.name= "post - chunked body: all your base are belong to us"
276 ,.type= HTTP_REQUEST
277 ,.raw= "POST /post_chunked_all_your_base HTTP/1.1\r\n"
278 "Transfer-Encoding: chunked\r\n"
279 "\r\n"
280 "1e\r\nall your base are belong to us\r\n"
281 "0\r\n"
282 "\r\n"
283 ,.should_keep_alive= TRUE
284 ,.message_complete_on_eof= FALSE
285 ,.http_major= 1
286 ,.http_minor= 1
287 ,.method= HTTP_POST
288 ,.query_string= ""
289 ,.fragment= ""
290 ,.request_path= "/post_chunked_all_your_base"
291 ,.request_url= "/post_chunked_all_your_base"
292 ,.num_headers= 1
293 ,.headers=
294 { { "Transfer-Encoding" , "chunked" }
295 }
296 ,.body= "all your base are belong to us"
297 }
298
299#define TWO_CHUNKS_MULT_ZERO_END 9
300, {.name= "two chunks ; triple zero ending"
301 ,.type= HTTP_REQUEST
302 ,.raw= "POST /two_chunks_mult_zero_end HTTP/1.1\r\n"
303 "Transfer-Encoding: chunked\r\n"
304 "\r\n"
305 "5\r\nhello\r\n"
306 "6\r\n world\r\n"
307 "000\r\n"
308 "\r\n"
309 ,.should_keep_alive= TRUE
310 ,.message_complete_on_eof= FALSE
311 ,.http_major= 1
312 ,.http_minor= 1
313 ,.method= HTTP_POST
314 ,.query_string= ""
315 ,.fragment= ""
316 ,.request_path= "/two_chunks_mult_zero_end"
317 ,.request_url= "/two_chunks_mult_zero_end"
318 ,.num_headers= 1
319 ,.headers=
320 { { "Transfer-Encoding", "chunked" }
321 }
322 ,.body= "hello world"
323 }
324
325#define CHUNKED_W_TRAILING_HEADERS 10
326, {.name= "chunked with trailing headers. blech."
327 ,.type= HTTP_REQUEST
328 ,.raw= "POST /chunked_w_trailing_headers HTTP/1.1\r\n"
329 "Transfer-Encoding: chunked\r\n"
330 "\r\n"
331 "5\r\nhello\r\n"
332 "6\r\n world\r\n"
333 "0\r\n"
334 "Vary: *\r\n"
335 "Content-Type: text/plain\r\n"
336 "\r\n"
337 ,.should_keep_alive= TRUE
338 ,.message_complete_on_eof= FALSE
339 ,.http_major= 1
340 ,.http_minor= 1
341 ,.method= HTTP_POST
342 ,.query_string= ""
343 ,.fragment= ""
344 ,.request_path= "/chunked_w_trailing_headers"
345 ,.request_url= "/chunked_w_trailing_headers"
346 ,.num_headers= 3
347 ,.headers=
348 { { "Transfer-Encoding", "chunked" }
349 , { "Vary", "*" }
350 , { "Content-Type", "text/plain" }
351 }
352 ,.body= "hello world"
353 }
354
355#define CHUNKED_W_BULLSHIT_AFTER_LENGTH 11
356, {.name= "with bullshit after the length"
357 ,.type= HTTP_REQUEST
358 ,.raw= "POST /chunked_w_bullshit_after_length HTTP/1.1\r\n"
359 "Transfer-Encoding: chunked\r\n"
360 "\r\n"
361 "5; ihatew3;whatthefuck=aretheseparametersfor\r\nhello\r\n"
362 "6; blahblah; blah\r\n world\r\n"
363 "0\r\n"
364 "\r\n"
365 ,.should_keep_alive= TRUE
366 ,.message_complete_on_eof= FALSE
367 ,.http_major= 1
368 ,.http_minor= 1
369 ,.method= HTTP_POST
370 ,.query_string= ""
371 ,.fragment= ""
372 ,.request_path= "/chunked_w_bullshit_after_length"
373 ,.request_url= "/chunked_w_bullshit_after_length"
374 ,.num_headers= 1
375 ,.headers=
376 { { "Transfer-Encoding", "chunked" }
377 }
378 ,.body= "hello world"
379 }
380
381#define WITH_QUOTES 12
382, {.name= "with quotes"
383 ,.type= HTTP_REQUEST
384 ,.raw= "GET /with_\"stupid\"_quotes?foo=\"bar\" HTTP/1.1\r\n\r\n"
385 ,.should_keep_alive= TRUE
386 ,.message_complete_on_eof= FALSE
387 ,.http_major= 1
388 ,.http_minor= 1
389 ,.method= HTTP_GET
390 ,.query_string= "foo=\"bar\""
391 ,.fragment= ""
392 ,.request_path= "/with_\"stupid\"_quotes"
393 ,.request_url= "/with_\"stupid\"_quotes?foo=\"bar\""
394 ,.num_headers= 0
395 ,.headers= { }
396 ,.body= ""
397 }
398
399#define APACHEBENCH_GET 13
400/* The server receiving this request SHOULD NOT wait for EOF
401 * to know that content-length == 0.
402 * How to represent this in a unit test? message_complete_on_eof
403 * Compare with NO_CONTENT_LENGTH_RESPONSE.
404 */
405, {.name = "apachebench get"
406 ,.type= HTTP_REQUEST
407 ,.raw= "GET /test HTTP/1.0\r\n"
408 "Host: 0.0.0.0:5000\r\n"
409 "User-Agent: ApacheBench/2.3\r\n"
410 "Accept: */*\r\n\r\n"
411 ,.should_keep_alive= FALSE
412 ,.message_complete_on_eof= FALSE
413 ,.http_major= 1
414 ,.http_minor= 0
415 ,.method= HTTP_GET
416 ,.query_string= ""
417 ,.fragment= ""
418 ,.request_path= "/test"
419 ,.request_url= "/test"
420 ,.num_headers= 3
421 ,.headers= { { "Host", "0.0.0.0:5000" }
422 , { "User-Agent", "ApacheBench/2.3" }
423 , { "Accept", "*/*" }
424 }
425 ,.body= ""
426 }
427
428#define QUERY_URL_WITH_QUESTION_MARK_GET 14
429/* Some clients include '?' characters in query strings.
430 */
431, {.name = "query url with question mark"
432 ,.type= HTTP_REQUEST
433 ,.raw= "GET /test.cgi?foo=bar?baz HTTP/1.1\r\n\r\n"
434 ,.should_keep_alive= TRUE
435 ,.message_complete_on_eof= FALSE
436 ,.http_major= 1
437 ,.http_minor= 1
438 ,.method= HTTP_GET
439 ,.query_string= "foo=bar?baz"
440 ,.fragment= ""
441 ,.request_path= "/test.cgi"
442 ,.request_url= "/test.cgi?foo=bar?baz"
443 ,.num_headers= 0
444 ,.headers= {}
445 ,.body= ""
446 }
447
448#define PREFIX_NEWLINE_GET 15
449/* Some clients, especially after a POST in a keep-alive connection,
450 * will send an extra CRLF before the next request
451 */
452, {.name = "newline prefix get"
453 ,.type= HTTP_REQUEST
454 ,.raw= "\r\nGET /test HTTP/1.1\r\n\r\n"
455 ,.should_keep_alive= TRUE
456 ,.message_complete_on_eof= FALSE
457 ,.http_major= 1
458 ,.http_minor= 1
459 ,.method= HTTP_GET
460 ,.query_string= ""
461 ,.fragment= ""
462 ,.request_path= "/test"
463 ,.request_url= "/test"
464 ,.num_headers= 0
465 ,.headers= { }
466 ,.body= ""
467 }
468
469#define UPGRADE_REQUEST 16
470, {.name = "upgrade request"
471 ,.type= HTTP_REQUEST
472 ,.raw= "GET /demo HTTP/1.1\r\n"
473 "Host: example.com\r\n"
474 "Connection: Upgrade\r\n"
475 "Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
476 "Sec-WebSocket-Protocol: sample\r\n"
477 "Upgrade: WebSocket\r\n"
478 "Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
479 "Origin: http://example.com\r\n"
480 "\r\n"
481 "Hot diggity dogg"
482 ,.should_keep_alive= TRUE
483 ,.message_complete_on_eof= FALSE
484 ,.http_major= 1
485 ,.http_minor= 1
486 ,.method= HTTP_GET
487 ,.query_string= ""
488 ,.fragment= ""
489 ,.request_path= "/demo"
490 ,.request_url= "/demo"
491 ,.num_headers= 7
492 ,.upgrade="Hot diggity dogg"
493 ,.headers= { { "Host", "example.com" }
494 , { "Connection", "Upgrade" }
495 , { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
496 , { "Sec-WebSocket-Protocol", "sample" }
497 , { "Upgrade", "WebSocket" }
498 , { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
499 , { "Origin", "http://example.com" }
500 }
501 ,.body= ""
502 }
503
504#define CONNECT_REQUEST 17
505, {.name = "connect request"
506 ,.type= HTTP_REQUEST
507 ,.raw= "CONNECT 0-home0.netscape.com:443 HTTP/1.0\r\n"
508 "User-agent: Mozilla/1.1N\r\n"
509 "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n"
510 "\r\n"
511 "some data\r\n"
512 "and yet even more data"
513 ,.should_keep_alive= FALSE
514 ,.message_complete_on_eof= FALSE
515 ,.http_major= 1
516 ,.http_minor= 0
517 ,.method= HTTP_CONNECT
518 ,.query_string= ""
519 ,.fragment= ""
520 ,.request_path= ""
521 ,.request_url= "0-home0.netscape.com:443"
522 ,.num_headers= 2
523 ,.upgrade="some data\r\nand yet even more data"
524 ,.headers= { { "User-agent", "Mozilla/1.1N" }
525 , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" }
526 }
527 ,.body= ""
528 }
529
530#define REPORT_REQ 18
531, {.name= "report request"
532 ,.type= HTTP_REQUEST
533 ,.raw= "REPORT /test HTTP/1.1\r\n"
534 "\r\n"
535 ,.should_keep_alive= TRUE
536 ,.message_complete_on_eof= FALSE
537 ,.http_major= 1
538 ,.http_minor= 1
539 ,.method= HTTP_REPORT
540 ,.query_string= ""
541 ,.fragment= ""
542 ,.request_path= "/test"
543 ,.request_url= "/test"
544 ,.num_headers= 0
545 ,.headers= {}
546 ,.body= ""
547 }
548
549#define NO_HTTP_VERSION 19
550, {.name= "request with no http version"
551 ,.type= HTTP_REQUEST
552 ,.raw= "GET /\r\n"
553 "\r\n"
554 ,.should_keep_alive= FALSE
555 ,.message_complete_on_eof= FALSE
556 ,.http_major= 0
557 ,.http_minor= 9
558 ,.method= HTTP_GET
559 ,.query_string= ""
560 ,.fragment= ""
561 ,.request_path= "/"
562 ,.request_url= "/"
563 ,.num_headers= 0
564 ,.headers= {}
565 ,.body= ""
566 }
567
568#define MSEARCH_REQ 20
569, {.name= "m-search request"
570 ,.type= HTTP_REQUEST
571 ,.raw= "M-SEARCH * HTTP/1.1\r\n"
572 "HOST: 239.255.255.250:1900\r\n"
573 "MAN: \"ssdp:discover\"\r\n"
574 "ST: \"ssdp:all\"\r\n"
575 "\r\n"
576 ,.should_keep_alive= TRUE
577 ,.message_complete_on_eof= FALSE
578 ,.http_major= 1
579 ,.http_minor= 1
580 ,.method= HTTP_MSEARCH
581 ,.query_string= ""
582 ,.fragment= ""
583 ,.request_path= "*"
584 ,.request_url= "*"
585 ,.num_headers= 3
586 ,.headers= { { "HOST", "239.255.255.250:1900" }
587 , { "MAN", "\"ssdp:discover\"" }
588 , { "ST", "\"ssdp:all\"" }
589 }
590 ,.body= ""
591 }
592
593#define LINE_FOLDING_IN_HEADER 21
594, {.name= "line folding in header value"
595 ,.type= HTTP_REQUEST
596 ,.raw= "GET / HTTP/1.1\r\n"
597 "Line1: abc\r\n"
598 "\tdef\r\n"
599 " ghi\r\n"
600 "\t\tjkl\r\n"
601 " mno \r\n"
602 "\t \tqrs\r\n"
603 "Line2: \t line2\t\r\n"
604 "\r\n"
605 ,.should_keep_alive= TRUE
606 ,.message_complete_on_eof= FALSE
607 ,.http_major= 1
608 ,.http_minor= 1
609 ,.method= HTTP_GET
610 ,.query_string= ""
611 ,.fragment= ""
612 ,.request_path= "/"
613 ,.request_url= "/"
614 ,.num_headers= 2
615 ,.headers= { { "Line1", "abcdefghijklmno qrs" }
616 , { "Line2", "line2\t" }
617 }
618 ,.body= ""
619 }
620
621
622#define QUERY_TERMINATED_HOST 22
623, {.name= "host terminated by a query string"
624 ,.type= HTTP_REQUEST
625 ,.raw= "GET http://hypnotoad.org?hail=all HTTP/1.1\r\n"
626 "\r\n"
627 ,.should_keep_alive= TRUE
628 ,.message_complete_on_eof= FALSE
629 ,.http_major= 1
630 ,.http_minor= 1
631 ,.method= HTTP_GET
632 ,.query_string= "hail=all"
633 ,.fragment= ""
634 ,.request_path= ""
635 ,.request_url= "http://hypnotoad.org?hail=all"
636 ,.host= "hypnotoad.org"
637 ,.num_headers= 0
638 ,.headers= { }
639 ,.body= ""
640 }
641
642#define QUERY_TERMINATED_HOSTPORT 23
643, {.name= "host:port terminated by a query string"
644 ,.type= HTTP_REQUEST
645 ,.raw= "GET http://hypnotoad.org:1234?hail=all HTTP/1.1\r\n"
646 "\r\n"
647 ,.should_keep_alive= TRUE
648 ,.message_complete_on_eof= FALSE
649 ,.http_major= 1
650