Commit e52c11d19dd2029ffe27072b82f7d1345e92a349
1 parent
b7c43193
added http-parser
Showing
17 changed files
with
6622 additions
and
6 deletions
3rdparty/http-parser-2.1/.gitignore
0 → 100644
3rdparty/http-parser-2.1/.mailmap
0 → 100644
| 1 | +# update AUTHORS with: | |
| 2 | +# git log --all --reverse --format='%aN <%aE>' | perl -ne 'BEGIN{print "# Authors ordered by first contribution.\n"} print unless $h{$_}; $h{$_} = 1' > AUTHORS | |
| 3 | +Ryan Dahl <ry@tinyclouds.org> | |
| 4 | +Salman Haq <salman.haq@asti-usa.com> | |
| 5 | +Simon Zimmermann <simonz05@gmail.com> | |
| 6 | +Thomas LE ROUX <thomas@november-eleven.fr> LE ROUX Thomas <thomas@procheo.fr> | |
| 7 | +Thomas LE ROUX <thomas@november-eleven.fr> Thomas LE ROUX <thomas@procheo.fr> | ... | ... |
3rdparty/http-parser-2.1/.travis.yml
0 → 100644
3rdparty/http-parser-2.1/AUTHORS
0 → 100644
| 1 | +# Authors ordered by first contribution. | |
| 2 | +Ryan Dahl <ry@tinyclouds.org> | |
| 3 | +Jeremy Hinegardner <jeremy@hinegardner.org> | |
| 4 | +Sergey Shepelev <temotor@gmail.com> | |
| 5 | +Joe Damato <ice799@gmail.com> | |
| 6 | +tomika <tomika_nospam@freemail.hu> | |
| 7 | +Phoenix Sol <phoenix@burninglabs.com> | |
| 8 | +Cliff Frey <cliff@meraki.com> | |
| 9 | +Ewen Cheslack-Postava <ewencp@cs.stanford.edu> | |
| 10 | +Santiago Gala <sgala@apache.org> | |
| 11 | +Tim Becker <tim.becker@syngenio.de> | |
| 12 | +Jeff Terrace <jterrace@gmail.com> | |
| 13 | +Ben Noordhuis <info@bnoordhuis.nl> | |
| 14 | +Nathan Rajlich <nathan@tootallnate.net> | |
| 15 | +Mark Nottingham <mnot@mnot.net> | |
| 16 | +Aman Gupta <aman@tmm1.net> | |
| 17 | +Tim Becker <tim.becker@kuriositaet.de> | |
| 18 | +Sean Cunningham <sean.cunningham@mandiant.com> | |
| 19 | +Peter Griess <pg@std.in> | |
| 20 | +Salman Haq <salman.haq@asti-usa.com> | |
| 21 | +Cliff Frey <clifffrey@gmail.com> | |
| 22 | +Jon Kolb <jon@b0g.us> | |
| 23 | +Fouad Mardini <f.mardini@gmail.com> | |
| 24 | +Paul Querna <pquerna@apache.org> | |
| 25 | +Felix Geisendörfer <felix@debuggable.com> | |
| 26 | +koichik <koichik@improvement.jp> | |
| 27 | +Andre Caron <andre.l.caron@gmail.com> | |
| 28 | +Ivo Raisr <ivosh@ivosh.net> | |
| 29 | +James McLaughlin <jamie@lacewing-project.org> | |
| 30 | +David Gwynne <loki@animata.net> | |
| 31 | +Thomas LE ROUX <thomas@november-eleven.fr> | |
| 32 | +Randy Rizun <rrizun@ortivawireless.com> | |
| 33 | +Andre Louis Caron <andre.louis.caron@usherbrooke.ca> | |
| 34 | +Simon Zimmermann <simonz05@gmail.com> | |
| 35 | +Erik Dubbelboer <erik@dubbelboer.com> | |
| 36 | +Martell Malone <martellmalone@gmail.com> | |
| 37 | +Bertrand Paquet <bpaquet@octo.com> | |
| 38 | +BogDan Vatra <bogdan@kde.org> | |
| 39 | +Peter Faiman <peter@thepicard.org> | |
| 40 | +Corey Richardson <corey@octayn.net> | |
| 41 | +Tóth Tamás <tomika_nospam@freemail.hu> | ... | ... |
3rdparty/http-parser-2.1/CONTRIBUTIONS
0 → 100644
3rdparty/http-parser-2.1/LICENSE-MIT
0 → 100644
| 1 | +http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright | |
| 2 | +Igor Sysoev. | |
| 3 | + | |
| 4 | +Additional changes are licensed under the same terms as NGINX and | |
| 5 | +copyright Joyent, Inc. and other Node contributors. All rights reserved. | |
| 6 | + | |
| 7 | +Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 8 | +of this software and associated documentation files (the "Software"), to | |
| 9 | +deal in the Software without restriction, including without limitation the | |
| 10 | +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
| 11 | +sell copies of the Software, and to permit persons to whom the Software is | |
| 12 | +furnished to do so, subject to the following conditions: | |
| 13 | + | |
| 14 | +The above copyright notice and this permission notice shall be included in | |
| 15 | +all copies or substantial portions of the Software. | |
| 16 | + | |
| 17 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 18 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 19 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 20 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 21 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 22 | +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 23 | +IN THE SOFTWARE. | ... | ... |
3rdparty/http-parser-2.1/Makefile
0 → 100644
| 1 | +CC?=gcc | |
| 2 | +AR?=ar | |
| 3 | + | |
| 4 | +CPPFLAGS += -I. | |
| 5 | +CPPFLAGS_DEBUG = $(CPPFLAGS) -DHTTP_PARSER_STRICT=1 | |
| 6 | +CPPFLAGS_DEBUG += $(CPPFLAGS_DEBUG_EXTRA) | |
| 7 | +CPPFLAGS_FAST = $(CPPFLAGS) -DHTTP_PARSER_STRICT=0 | |
| 8 | +CPPFLAGS_FAST += $(CPPFLAGS_FAST_EXTRA) | |
| 9 | + | |
| 10 | +CFLAGS += -Wall -Wextra -Werror | |
| 11 | +CFLAGS_DEBUG = $(CFLAGS) -O0 -g $(CFLAGS_DEBUG_EXTRA) | |
| 12 | +CFLAGS_FAST = $(CFLAGS) -O3 $(CFLAGS_FAST_EXTRA) | |
| 13 | +CFLAGS_LIB = $(CFLAGS_FAST) -fPIC | |
| 14 | + | |
| 15 | +test: test_g test_fast | |
| 16 | + ./test_g | |
| 17 | + ./test_fast | |
| 18 | + | |
| 19 | +test_g: http_parser_g.o test_g.o | |
| 20 | + $(CC) $(CFLAGS_DEBUG) $(LDFLAGS) http_parser_g.o test_g.o -o $@ | |
| 21 | + | |
| 22 | +test_g.o: test.c http_parser.h Makefile | |
| 23 | + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c test.c -o $@ | |
| 24 | + | |
| 25 | +http_parser_g.o: http_parser.c http_parser.h Makefile | |
| 26 | + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) -c http_parser.c -o $@ | |
| 27 | + | |
| 28 | +test_fast: http_parser.o test.o http_parser.h | |
| 29 | + $(CC) $(CFLAGS_FAST) $(LDFLAGS) http_parser.o test.o -o $@ | |
| 30 | + | |
| 31 | +test.o: test.c http_parser.h Makefile | |
| 32 | + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c test.c -o $@ | |
| 33 | + | |
| 34 | +http_parser.o: http_parser.c http_parser.h Makefile | |
| 35 | + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) -c http_parser.c | |
| 36 | + | |
| 37 | +test-run-timed: test_fast | |
| 38 | + while(true) do time ./test_fast > /dev/null; done | |
| 39 | + | |
| 40 | +test-valgrind: test_g | |
| 41 | + valgrind ./test_g | |
| 42 | + | |
| 43 | +libhttp_parser.o: http_parser.c http_parser.h Makefile | |
| 44 | + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_LIB) -c http_parser.c -o libhttp_parser.o | |
| 45 | + | |
| 46 | +library: libhttp_parser.o | |
| 47 | + $(CC) -shared -o libhttp_parser.so libhttp_parser.o | |
| 48 | + | |
| 49 | +package: http_parser.o | |
| 50 | + $(AR) rcs libhttp_parser.a http_parser.o | |
| 51 | + | |
| 52 | +url_parser: http_parser.o contrib/url_parser.c | |
| 53 | + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o $@ | |
| 54 | + | |
| 55 | +url_parser_g: http_parser_g.o contrib/url_parser.c | |
| 56 | + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o $@ | |
| 57 | + | |
| 58 | +parsertrace: http_parser.o contrib/parsertrace.c | |
| 59 | + $(CC) $(CPPFLAGS_FAST) $(CFLAGS_FAST) $^ -o parsertrace | |
| 60 | + | |
| 61 | +parsertrace_g: http_parser_g.o contrib/parsertrace.c | |
| 62 | + $(CC) $(CPPFLAGS_DEBUG) $(CFLAGS_DEBUG) $^ -o parsertrace_g | |
| 63 | + | |
| 64 | +tags: http_parser.c http_parser.h test.c | |
| 65 | + ctags $^ | |
| 66 | + | |
| 67 | +clean: | |
| 68 | + rm -f *.o *.a tags test test_fast test_g \ | |
| 69 | + http_parser.tar libhttp_parser.so \ | |
| 70 | + url_parser url_parser_g parsertrace parsertrace_g | |
| 71 | + | |
| 72 | +contrib/url_parser.c: http_parser.h | |
| 73 | +contrib/parsertrace.c: http_parser.h | |
| 74 | + | |
| 75 | +.PHONY: clean package test-run test-run-timed test-valgrind | ... | ... |
3rdparty/http-parser-2.1/README.md
0 → 100644
| 1 | +HTTP Parser | |
| 2 | +=========== | |
| 3 | + | |
| 4 | +[](https://travis-ci.org/joyent/http-parser) | |
| 5 | + | |
| 6 | +This is a parser for HTTP messages written in C. It parses both requests and | |
| 7 | +responses. The parser is designed to be used in performance HTTP | |
| 8 | +applications. It does not make any syscalls nor allocations, it does not | |
| 9 | +buffer data, it can be interrupted at anytime. Depending on your | |
| 10 | +architecture, it only requires about 40 bytes of data per message | |
| 11 | +stream (in a web server that is per connection). | |
| 12 | + | |
| 13 | +Features: | |
| 14 | + | |
| 15 | + * No dependencies | |
| 16 | + * Handles persistent streams (keep-alive). | |
| 17 | + * Decodes chunked encoding. | |
| 18 | + * Upgrade support | |
| 19 | + * Defends against buffer overflow attacks. | |
| 20 | + | |
| 21 | +The 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 | + | |
| 33 | +Usage | |
| 34 | +----- | |
| 35 | + | |
| 36 | +One `http_parser` object is used per TCP connection. Initialize the struct | |
| 37 | +using `http_parser_init()` and set the callbacks. That might look something | |
| 38 | +like this for a request parser: | |
| 39 | + | |
| 40 | + http_parser_settings settings; | |
| 41 | + settings.on_url = my_url_callback; | |
| 42 | + settings.on_header_field = my_header_field_callback; | |
| 43 | + /* ... */ | |
| 44 | + | |
| 45 | + http_parser *parser = malloc(sizeof(http_parser)); | |
| 46 | + http_parser_init(parser, HTTP_REQUEST); | |
| 47 | + parser->data = my_socket; | |
| 48 | + | |
| 49 | +When data is received on the socket execute the parser and check for errors. | |
| 50 | + | |
| 51 | + size_t len = 80*1024, nparsed; | |
| 52 | + char buf[len]; | |
| 53 | + ssize_t recved; | |
| 54 | + | |
| 55 | + recved = recv(fd, buf, len, 0); | |
| 56 | + | |
| 57 | + if (recved < 0) { | |
| 58 | + /* Handle error. */ | |
| 59 | + } | |
| 60 | + | |
| 61 | + /* Start up / continue the parser. | |
| 62 | + * Note we pass recved==0 to signal that EOF has been recieved. | |
| 63 | + */ | |
| 64 | + nparsed = http_parser_execute(parser, &settings, buf, recved); | |
| 65 | + | |
| 66 | + if (parser->upgrade) { | |
| 67 | + /* handle new protocol */ | |
| 68 | + } else if (nparsed != recved) { | |
| 69 | + /* Handle error. Usually just close the connection. */ | |
| 70 | + } | |
| 71 | + | |
| 72 | +HTTP needs to know where the end of the stream is. For example, sometimes | |
| 73 | +servers send responses without Content-Length and expect the client to | |
| 74 | +consume input (for the body) until EOF. To tell http_parser about EOF, give | |
| 75 | +`0` as the forth parameter to `http_parser_execute()`. Callbacks and errors | |
| 76 | +can still be encountered during an EOF, so one must still be prepared | |
| 77 | +to receive them. | |
| 78 | + | |
| 79 | +Scalar valued message information such as `status_code`, `method`, and the | |
| 80 | +HTTP version are stored in the parser structure. This data is only | |
| 81 | +temporally stored in `http_parser` and gets reset on each new message. If | |
| 82 | +this information is needed later, copy it out of the structure during the | |
| 83 | +`headers_complete` callback. | |
| 84 | + | |
| 85 | +The parser decodes the transfer-encoding for both requests and responses | |
| 86 | +transparently. That is, a chunked encoding is decoded before being sent to | |
| 87 | +the on_body callback. | |
| 88 | + | |
| 89 | + | |
| 90 | +The Special Problem of Upgrade | |
| 91 | +------------------------------ | |
| 92 | + | |
| 93 | +HTTP supports upgrading the connection to a different protocol. An | |
| 94 | +increasingly common example of this is the Web Socket protocol which sends | |
| 95 | +a request like | |
| 96 | + | |
| 97 | + GET /demo HTTP/1.1 | |
| 98 | + Upgrade: WebSocket | |
| 99 | + Connection: Upgrade | |
| 100 | + Host: example.com | |
| 101 | + Origin: http://example.com | |
| 102 | + WebSocket-Protocol: sample | |
| 103 | + | |
| 104 | +followed by non-HTTP data. | |
| 105 | + | |
| 106 | +(See http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more | |
| 107 | +information the Web Socket protocol.) | |
| 108 | + | |
| 109 | +To support this, the parser will treat this as a normal HTTP message without a | |
| 110 | +body. Issuing both on_headers_complete and on_message_complete callbacks. However | |
| 111 | +http_parser_execute() will stop parsing at the end of the headers and return. | |
| 112 | + | |
| 113 | +The user is expected to check if `parser->upgrade` has been set to 1 after | |
| 114 | +`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied | |
| 115 | +offset by the return value of `http_parser_execute()`. | |
| 116 | + | |
| 117 | + | |
| 118 | +Callbacks | |
| 119 | +--------- | |
| 120 | + | |
| 121 | +During the `http_parser_execute()` call, the callbacks set in | |
| 122 | +`http_parser_settings` will be executed. The parser maintains state and | |
| 123 | +never looks behind, so buffering the data is not necessary. If you need to | |
| 124 | +save certain data for later usage, you can do that from the callbacks. | |
| 125 | + | |
| 126 | +There are two types of callbacks: | |
| 127 | + | |
| 128 | +* notification `typedef int (*http_cb) (http_parser*);` | |
| 129 | + Callbacks: on_message_begin, on_headers_complete, on_message_complete. | |
| 130 | +* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` | |
| 131 | + Callbacks: (requests only) on_uri, | |
| 132 | + (common) on_header_field, on_header_value, on_body; | |
| 133 | + | |
| 134 | +Callbacks must return 0 on success. Returning a non-zero value indicates | |
| 135 | +error to the parser, making it exit immediately. | |
| 136 | + | |
| 137 | +In case you parse HTTP message in chunks (i.e. `read()` request line | |
| 138 | +from socket, parse, read half headers, parse, etc) your data callbacks | |
| 139 | +may be called more than once. Http-parser guarantees that data pointer is only | |
| 140 | +valid for the lifetime of callback. You can also `read()` into a heap allocated | |
| 141 | +buffer to avoid copying memory around if this fits your application. | |
| 142 | + | |
| 143 | +Reading headers may be a tricky task if you read/parse headers partially. | |
| 144 | +Basically, you need to remember whether last header callback was field or value | |
| 145 | +and apply following logic: | |
| 146 | + | |
| 147 | + (on_header_field and on_header_value shortened to on_h_*) | |
| 148 | + ------------------------ ------------ -------------------------------------------- | |
| 149 | + | State (prev. callback) | Callback | Description/action | | |
| 150 | + ------------------------ ------------ -------------------------------------------- | |
| 151 | + | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | | |
| 152 | + | | | into it | | |
| 153 | + ------------------------ ------------ -------------------------------------------- | |
| 154 | + | value | on_h_field | New header started. | | |
| 155 | + | | | Copy current name,value buffers to headers | | |
| 156 | + | | | list and allocate new buffer for new name | | |
| 157 | + ------------------------ ------------ -------------------------------------------- | |
| 158 | + | field | on_h_field | Previous name continues. Reallocate name | | |
| 159 | + | | | buffer and append callback data to it | | |
| 160 | + ------------------------ ------------ -------------------------------------------- | |
| 161 | + | field | on_h_value | Value for current header started. Allocate | | |
| 162 | + | | | new buffer and copy callback data to it | | |
| 163 | + ------------------------ ------------ -------------------------------------------- | |
| 164 | + | value | on_h_value | Value continues. Reallocate value buffer | | |
| 165 | + | | | and append callback data to it | | |
| 166 | + ------------------------ ------------ -------------------------------------------- | |
| 167 | + | |
| 168 | + | |
| 169 | +Parsing URLs | |
| 170 | +------------ | |
| 171 | + | |
| 172 | +A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. | |
| 173 | +Users of this library may wish to use it to parse URLs constructed from | |
| 174 | +consecutive `on_url` callbacks. | |
| 175 | + | |
| 176 | +See examples of reading in headers: | |
| 177 | + | |
| 178 | +* [partial example](http://gist.github.com/155877) in C | |
| 179 | +* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C | |
| 180 | +* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript | ... | ... |
3rdparty/http-parser-2.1/contrib/parsertrace.c
0 → 100644
| 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 | + | |
| 32 | +int on_message_begin(http_parser* _) { | |
| 33 | + (void)_; | |
| 34 | + printf("\n***MESSAGE BEGIN***\n\n"); | |
| 35 | + return 0; | |
| 36 | +} | |
| 37 | + | |
| 38 | +int on_headers_complete(http_parser* _) { | |
| 39 | + (void)_; | |
| 40 | + printf("\n***HEADERS COMPLETE***\n\n"); | |
| 41 | + return 0; | |
| 42 | +} | |
| 43 | + | |
| 44 | +int on_message_complete(http_parser* _) { | |
| 45 | + (void)_; | |
| 46 | + printf("\n***MESSAGE COMPLETE***\n\n"); | |
| 47 | + return 0; | |
| 48 | +} | |
| 49 | + | |
| 50 | +int 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 | + | |
| 56 | +int 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 | + | |
| 62 | +int 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 | + | |
| 68 | +int 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 | + | |
| 74 | +void 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 | + | |
| 83 | +int 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 | +} | ... | ... |
3rdparty/http-parser-2.1/contrib/url_parser.c
0 → 100644
| 1 | +#include "http_parser.h" | |
| 2 | +#include <stdio.h> | |
| 3 | +#include <string.h> | |
| 4 | + | |
| 5 | +void | |
| 6 | +dump_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 | + | |
| 26 | +int 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 | +} | |
| 0 | 45 | \ No newline at end of file | ... | ... |
3rdparty/http-parser-2.1/http_parser.c
0 → 100644
| 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) \ | |
| 55 | +do { \ | |
| 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) \ | |
| 62 | +do { \ | |
| 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) \ | |
| 85 | +do { \ | |
| 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) \ | |
| 113 | +do { \ | |
| 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 | + | |
| 130 | +static 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 | + */ | |
| 145 | +static 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 | + | |
| 180 | +static 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 | + | |
| 199 | +static 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 | + | |
| 235 | +enum 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 | + | |
| 315 | +enum 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 | + | |
| 341 | +enum 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) \ | |
| 388 | +do { \ | |
| 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 }, | |
| 403 | +static 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 | + | |
| 411 | +int 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 | + */ | |
| 424 | +static enum state | |
| 425 | +parse_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 | + | |
| 572 | +size_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 | + /* Buffer overflow attack */ | |
| 638 | + if (parser->nread > HTTP_MAX_HEADER_SIZE) { | |
| 639 | + SET_ERRNO(HPE_HEADER_OVERFLOW); | |
| 640 | + goto error; | |
| 641 | + } | |
| 642 | + } | |
| 643 | + | |
| 644 | + reexecute_byte: | |
| 645 | + switch (parser->state) { | |
| 646 | + | |
| 647 | + case s_dead: | |
| 648 | + /* this state is used after a 'Connection: close' message | |
| 649 | + * the parser will error out if it reads another message | |
| 650 | + */ | |
| 651 | + if (ch == CR || ch == LF) | |
| 652 | + break; | |
| 653 | + | |
| 654 | + SET_ERRNO(HPE_CLOSED_CONNECTION); | |
| 655 | + goto error; | |
| 656 | + | |
| 657 | + case s_start_req_or_res: | |
| 658 | + { | |
| 659 | + if (ch == CR || ch == LF) | |
| 660 | + break; | |
| 661 | + parser->flags = 0; | |
| 662 | + parser->content_length = ULLONG_MAX; | |
| 663 | + | |
| 664 | + if (ch == 'H') { | |
| 665 | + parser->state = s_res_or_resp_H; | |
| 666 | + | |
| 667 | + CALLBACK_NOTIFY(message_begin); | |
| 668 | + } else { | |
| 669 | + parser->type = HTTP_REQUEST; | |
| 670 | + parser->state = s_start_req; | |
| 671 | + goto reexecute_byte; | |
| 672 | + } | |
| 673 | + | |
| 674 | + break; | |
| 675 | + } | |
| 676 | + | |
| 677 | + case s_res_or_resp_H: | |
| 678 | + if (ch == 'T') { | |
| 679 | + parser->type = HTTP_RESPONSE; | |
| 680 | + parser->state = s_res_HT; | |
| 681 | + } else { | |
| 682 | + if (ch != 'E') { | |
| 683 | + SET_ERRNO(HPE_INVALID_CONSTANT); | |
| 684 | + goto error; | |
| 685 | + } | |
| 686 | + | |
| 687 | + parser->type = HTTP_REQUEST; | |
| 688 | + parser->method = HTTP_HEAD; | |
| 689 | + parser->index = 2; | |
| 690 | + parser->state = s_req_method; | |
| 691 | + } | |
| 692 | + break; | |
| 693 | + | |
| 694 | + case s_start_res: | |
| 695 | + { | |
| 696 | + parser->flags = 0; | |
| 697 | + parser->content_length = ULLONG_MAX; | |
| 698 | + | |
| 699 | + switch (ch) { | |
| 700 | + case 'H': | |
| 701 | + parser->state = s_res_H; | |
| 702 | + break; | |
| 703 | + | |
| 704 | + case CR: | |
| 705 | + case LF: | |
| 706 | + break; | |
| 707 | + | |
| 708 | + default: | |
| 709 | + SET_ERRNO(HPE_INVALID_CONSTANT); | |
| 710 | + goto error; | |
| 711 | + } | |
| 712 | + | |
| 713 | + CALLBACK_NOTIFY(message_begin); | |
| 714 | + break; | |
| 715 | + } | |
| 716 | + | |
| 717 | + case s_res_H: | |
| 718 | + STRICT_CHECK(ch != 'T'); | |
| 719 | + parser->state = s_res_HT; | |
| 720 | + break; | |
| 721 | + | |
| 722 | + case s_res_HT: | |
| 723 | + STRICT_CHECK(ch != 'T'); | |
| 724 | + parser->state = s_res_HTT; | |
| 725 | + break; | |
| 726 | + | |
| 727 | + case s_res_HTT: | |
| 728 | + STRICT_CHECK(ch != 'P'); | |
| 729 | + parser->state = s_res_HTTP; | |
| 730 | + break; | |
| 731 | + | |
| 732 | + case s_res_HTTP: | |
| 733 | + STRICT_CHECK(ch != '/'); | |
| 734 | + parser->state = s_res_first_http_major; | |
| 735 | + break; | |
| 736 | + | |
| 737 | + case s_res_first_http_major: | |
| 738 | + if (ch < '0' || ch > '9') { | |
| 739 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 740 | + goto error; | |
| 741 | + } | |
| 742 | + | |
| 743 | + parser->http_major = ch - '0'; | |
| 744 | + parser->state = s_res_http_major; | |
| 745 | + break; | |
| 746 | + | |
| 747 | + /* major HTTP version or dot */ | |
| 748 | + case s_res_http_major: | |
| 749 | + { | |
| 750 | + if (ch == '.') { | |
| 751 | + parser->state = s_res_first_http_minor; | |
| 752 | + break; | |
| 753 | + } | |
| 754 | + | |
| 755 | + if (!IS_NUM(ch)) { | |
| 756 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 757 | + goto error; | |
| 758 | + } | |
| 759 | + | |
| 760 | + parser->http_major *= 10; | |
| 761 | + parser->http_major += ch - '0'; | |
| 762 | + | |
| 763 | + if (parser->http_major > 999) { | |
| 764 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 765 | + goto error; | |
| 766 | + } | |
| 767 | + | |
| 768 | + break; | |
| 769 | + } | |
| 770 | + | |
| 771 | + /* first digit of minor HTTP version */ | |
| 772 | + case s_res_first_http_minor: | |
| 773 | + if (!IS_NUM(ch)) { | |
| 774 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 775 | + goto error; | |
| 776 | + } | |
| 777 | + | |
| 778 | + parser->http_minor = ch - '0'; | |
| 779 | + parser->state = s_res_http_minor; | |
| 780 | + break; | |
| 781 | + | |
| 782 | + /* minor HTTP version or end of request line */ | |
| 783 | + case s_res_http_minor: | |
| 784 | + { | |
| 785 | + if (ch == ' ') { | |
| 786 | + parser->state = s_res_first_status_code; | |
| 787 | + break; | |
| 788 | + } | |
| 789 | + | |
| 790 | + if (!IS_NUM(ch)) { | |
| 791 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 792 | + goto error; | |
| 793 | + } | |
| 794 | + | |
| 795 | + parser->http_minor *= 10; | |
| 796 | + parser->http_minor += ch - '0'; | |
| 797 | + | |
| 798 | + if (parser->http_minor > 999) { | |
| 799 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 800 | + goto error; | |
| 801 | + } | |
| 802 | + | |
| 803 | + break; | |
| 804 | + } | |
| 805 | + | |
| 806 | + case s_res_first_status_code: | |
| 807 | + { | |
| 808 | + if (!IS_NUM(ch)) { | |
| 809 | + if (ch == ' ') { | |
| 810 | + break; | |
| 811 | + } | |
| 812 | + | |
| 813 | + SET_ERRNO(HPE_INVALID_STATUS); | |
| 814 | + goto error; | |
| 815 | + } | |
| 816 | + parser->status_code = ch - '0'; | |
| 817 | + parser->state = s_res_status_code; | |
| 818 | + break; | |
| 819 | + } | |
| 820 | + | |
| 821 | + case s_res_status_code: | |
| 822 | + { | |
| 823 | + if (!IS_NUM(ch)) { | |
| 824 | + switch (ch) { | |
| 825 | + case ' ': | |
| 826 | + parser->state = s_res_status; | |
| 827 | + break; | |
| 828 | + case CR: | |
| 829 | + parser->state = s_res_line_almost_done; | |
| 830 | + break; | |
| 831 | + case LF: | |
| 832 | + parser->state = s_header_field_start; | |
| 833 | + break; | |
| 834 | + default: | |
| 835 | + SET_ERRNO(HPE_INVALID_STATUS); | |
| 836 | + goto error; | |
| 837 | + } | |
| 838 | + break; | |
| 839 | + } | |
| 840 | + | |
| 841 | + parser->status_code *= 10; | |
| 842 | + parser->status_code += ch - '0'; | |
| 843 | + | |
| 844 | + if (parser->status_code > 999) { | |
| 845 | + SET_ERRNO(HPE_INVALID_STATUS); | |
| 846 | + goto error; | |
| 847 | + } | |
| 848 | + | |
| 849 | + break; | |
| 850 | + } | |
| 851 | + | |
| 852 | + case s_res_status: | |
| 853 | + /* the human readable status. e.g. "NOT FOUND" | |
| 854 | + * we are not humans so just ignore this */ | |
| 855 | + if (ch == CR) { | |
| 856 | + parser->state = s_res_line_almost_done; | |
| 857 | + break; | |
| 858 | + } | |
| 859 | + | |
| 860 | + if (ch == LF) { | |
| 861 | + parser->state = s_header_field_start; | |
| 862 | + break; | |
| 863 | + } | |
| 864 | + break; | |
| 865 | + | |
| 866 | + case s_res_line_almost_done: | |
| 867 | + STRICT_CHECK(ch != LF); | |
| 868 | + parser->state = s_header_field_start; | |
| 869 | + CALLBACK_NOTIFY(status_complete); | |
| 870 | + break; | |
| 871 | + | |
| 872 | + case s_start_req: | |
| 873 | + { | |
| 874 | + if (ch == CR || ch == LF) | |
| 875 | + break; | |
| 876 | + parser->flags = 0; | |
| 877 | + parser->content_length = ULLONG_MAX; | |
| 878 | + | |
| 879 | + if (!IS_ALPHA(ch)) { | |
| 880 | + SET_ERRNO(HPE_INVALID_METHOD); | |
| 881 | + goto error; | |
| 882 | + } | |
| 883 | + | |
| 884 | + parser->method = (enum http_method) 0; | |
| 885 | + parser->index = 1; | |
| 886 | + switch (ch) { | |
| 887 | + case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; | |
| 888 | + case 'D': parser->method = HTTP_DELETE; break; | |
| 889 | + case 'G': parser->method = HTTP_GET; break; | |
| 890 | + case 'H': parser->method = HTTP_HEAD; break; | |
| 891 | + case 'L': parser->method = HTTP_LOCK; break; | |
| 892 | + case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; | |
| 893 | + case 'N': parser->method = HTTP_NOTIFY; break; | |
| 894 | + case 'O': parser->method = HTTP_OPTIONS; break; | |
| 895 | + case 'P': parser->method = HTTP_POST; | |
| 896 | + /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ | |
| 897 | + break; | |
| 898 | + case 'R': parser->method = HTTP_REPORT; break; | |
| 899 | + case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; | |
| 900 | + case 'T': parser->method = HTTP_TRACE; break; | |
| 901 | + case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; | |
| 902 | + default: | |
| 903 | + SET_ERRNO(HPE_INVALID_METHOD); | |
| 904 | + goto error; | |
| 905 | + } | |
| 906 | + parser->state = s_req_method; | |
| 907 | + | |
| 908 | + CALLBACK_NOTIFY(message_begin); | |
| 909 | + | |
| 910 | + break; | |
| 911 | + } | |
| 912 | + | |
| 913 | + case s_req_method: | |
| 914 | + { | |
| 915 | + const char *matcher; | |
| 916 | + if (ch == '\0') { | |
| 917 | + SET_ERRNO(HPE_INVALID_METHOD); | |
| 918 | + goto error; | |
| 919 | + } | |
| 920 | + | |
| 921 | + matcher = method_strings[parser->method]; | |
| 922 | + if (ch == ' ' && matcher[parser->index] == '\0') { | |
| 923 | + parser->state = s_req_spaces_before_url; | |
| 924 | + } else if (ch == matcher[parser->index]) { | |
| 925 | + ; /* nada */ | |
| 926 | + } else if (parser->method == HTTP_CONNECT) { | |
| 927 | + if (parser->index == 1 && ch == 'H') { | |
| 928 | + parser->method = HTTP_CHECKOUT; | |
| 929 | + } else if (parser->index == 2 && ch == 'P') { | |
| 930 | + parser->method = HTTP_COPY; | |
| 931 | + } else { | |
| 932 | + goto error; | |
| 933 | + } | |
| 934 | + } else if (parser->method == HTTP_MKCOL) { | |
| 935 | + if (parser->index == 1 && ch == 'O') { | |
| 936 | + parser->method = HTTP_MOVE; | |
| 937 | + } else if (parser->index == 1 && ch == 'E') { | |
| 938 | + parser->method = HTTP_MERGE; | |
| 939 | + } else if (parser->index == 1 && ch == '-') { | |
| 940 | + parser->method = HTTP_MSEARCH; | |
| 941 | + } else if (parser->index == 2 && ch == 'A') { | |
| 942 | + parser->method = HTTP_MKACTIVITY; | |
| 943 | + } else { | |
| 944 | + goto error; | |
| 945 | + } | |
| 946 | + } else if (parser->method == HTTP_SUBSCRIBE) { | |
| 947 | + if (parser->index == 1 && ch == 'E') { | |
| 948 | + parser->method = HTTP_SEARCH; | |
| 949 | + } else { | |
| 950 | + goto error; | |
| 951 | + } | |
| 952 | + } else if (parser->index == 1 && parser->method == HTTP_POST) { | |
| 953 | + if (ch == 'R') { | |
| 954 | + parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ | |
| 955 | + } else if (ch == 'U') { | |
| 956 | + parser->method = HTTP_PUT; /* or HTTP_PURGE */ | |
| 957 | + } else if (ch == 'A') { | |
| 958 | + parser->method = HTTP_PATCH; | |
| 959 | + } else { | |
| 960 | + goto error; | |
| 961 | + } | |
| 962 | + } else if (parser->index == 2) { | |
| 963 | + if (parser->method == HTTP_PUT) { | |
| 964 | + if (ch == 'R') parser->method = HTTP_PURGE; | |
| 965 | + } else if (parser->method == HTTP_UNLOCK) { | |
| 966 | + if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE; | |
| 967 | + } | |
| 968 | + } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { | |
| 969 | + parser->method = HTTP_PROPPATCH; | |
| 970 | + } else { | |
| 971 | + SET_ERRNO(HPE_INVALID_METHOD); | |
| 972 | + goto error; | |
| 973 | + } | |
| 974 | + | |
| 975 | + ++parser->index; | |
| 976 | + break; | |
| 977 | + } | |
| 978 | + | |
| 979 | + case s_req_spaces_before_url: | |
| 980 | + { | |
| 981 | + if (ch == ' ') break; | |
| 982 | + | |
| 983 | + MARK(url); | |
| 984 | + if (parser->method == HTTP_CONNECT) { | |
| 985 | + parser->state = s_req_server_start; | |
| 986 | + } | |
| 987 | + | |
| 988 | + parser->state = parse_url_char((enum state)parser->state, ch); | |
| 989 | + if (parser->state == s_dead) { | |
| 990 | + SET_ERRNO(HPE_INVALID_URL); | |
| 991 | + goto error; | |
| 992 | + } | |
| 993 | + | |
| 994 | + break; | |
| 995 | + } | |
| 996 | + | |
| 997 | + case s_req_schema: | |
| 998 | + case s_req_schema_slash: | |
| 999 | + case s_req_schema_slash_slash: | |
| 1000 | + case s_req_server_start: | |
| 1001 | + { | |
| 1002 | + switch (ch) { | |
| 1003 | + /* No whitespace allowed here */ | |
| 1004 | + case ' ': | |
| 1005 | + case CR: | |
| 1006 | + case LF: | |
| 1007 | + SET_ERRNO(HPE_INVALID_URL); | |
| 1008 | + goto error; | |
| 1009 | + default: | |
| 1010 | + parser->state = parse_url_char((enum state)parser->state, ch); | |
| 1011 | + if (parser->state == s_dead) { | |
| 1012 | + SET_ERRNO(HPE_INVALID_URL); | |
| 1013 | + goto error; | |
| 1014 | + } | |
| 1015 | + } | |
| 1016 | + | |
| 1017 | + break; | |
| 1018 | + } | |
| 1019 | + | |
| 1020 | + case s_req_server: | |
| 1021 | + case s_req_server_with_at: | |
| 1022 | + case s_req_path: | |
| 1023 | + case s_req_query_string_start: | |
| 1024 | + case s_req_query_string: | |
| 1025 | + case s_req_fragment_start: | |
| 1026 | + case s_req_fragment: | |
| 1027 | + { | |
| 1028 | + switch (ch) { | |
| 1029 | + case ' ': | |
| 1030 | + parser->state = s_req_http_start; | |
| 1031 | + CALLBACK_DATA(url); | |
| 1032 | + break; | |
| 1033 | + case CR: | |
| 1034 | + case LF: | |
| 1035 | + parser->http_major = 0; | |
| 1036 | + parser->http_minor = 9; | |
| 1037 | + parser->state = (ch == CR) ? | |
| 1038 | + s_req_line_almost_done : | |
| 1039 | + s_header_field_start; | |
| 1040 | + CALLBACK_DATA(url); | |
| 1041 | + break; | |
| 1042 | + default: | |
| 1043 | + parser->state = parse_url_char((enum state)parser->state, ch); | |
| 1044 | + if (parser->state == s_dead) { | |
| 1045 | + SET_ERRNO(HPE_INVALID_URL); | |
| 1046 | + goto error; | |
| 1047 | + } | |
| 1048 | + } | |
| 1049 | + break; | |
| 1050 | + } | |
| 1051 | + | |
| 1052 | + case s_req_http_start: | |
| 1053 | + switch (ch) { | |
| 1054 | + case 'H': | |
| 1055 | + parser->state = s_req_http_H; | |
| 1056 | + break; | |
| 1057 | + case ' ': | |
| 1058 | + break; | |
| 1059 | + default: | |
| 1060 | + SET_ERRNO(HPE_INVALID_CONSTANT); | |
| 1061 | + goto error; | |
| 1062 | + } | |
| 1063 | + break; | |
| 1064 | + | |
| 1065 | + case s_req_http_H: | |
| 1066 | + STRICT_CHECK(ch != 'T'); | |
| 1067 | + parser->state = s_req_http_HT; | |
| 1068 | + break; | |
| 1069 | + | |
| 1070 | + case s_req_http_HT: | |
| 1071 | + STRICT_CHECK(ch != 'T'); | |
| 1072 | + parser->state = s_req_http_HTT; | |
| 1073 | + break; | |
| 1074 | + | |
| 1075 | + case s_req_http_HTT: | |
| 1076 | + STRICT_CHECK(ch != 'P'); | |
| 1077 | + parser->state = s_req_http_HTTP; | |
| 1078 | + break; | |
| 1079 | + | |
| 1080 | + case s_req_http_HTTP: | |
| 1081 | + STRICT_CHECK(ch != '/'); | |
| 1082 | + parser->state = s_req_first_http_major; | |
| 1083 | + break; | |
| 1084 | + | |
| 1085 | + /* first digit of major HTTP version */ | |
| 1086 | + case s_req_first_http_major: | |
| 1087 | + if (ch < '1' || ch > '9') { | |
| 1088 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1089 | + goto error; | |
| 1090 | + } | |
| 1091 | + | |
| 1092 | + parser->http_major = ch - '0'; | |
| 1093 | + parser->state = s_req_http_major; | |
| 1094 | + break; | |
| 1095 | + | |
| 1096 | + /* major HTTP version or dot */ | |
| 1097 | + case s_req_http_major: | |
| 1098 | + { | |
| 1099 | + if (ch == '.') { | |
| 1100 | + parser->state = s_req_first_http_minor; | |
| 1101 | + break; | |
| 1102 | + } | |
| 1103 | + | |
| 1104 | + if (!IS_NUM(ch)) { | |
| 1105 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1106 | + goto error; | |
| 1107 | + } | |
| 1108 | + | |
| 1109 | + parser->http_major *= 10; | |
| 1110 | + parser->http_major += ch - '0'; | |
| 1111 | + | |
| 1112 | + if (parser->http_major > 999) { | |
| 1113 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1114 | + goto error; | |
| 1115 | + } | |
| 1116 | + | |
| 1117 | + break; | |
| 1118 | + } | |
| 1119 | + | |
| 1120 | + /* first digit of minor HTTP version */ | |
| 1121 | + case s_req_first_http_minor: | |
| 1122 | + if (!IS_NUM(ch)) { | |
| 1123 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1124 | + goto error; | |
| 1125 | + } | |
| 1126 | + | |
| 1127 | + parser->http_minor = ch - '0'; | |
| 1128 | + parser->state = s_req_http_minor; | |
| 1129 | + break; | |
| 1130 | + | |
| 1131 | + /* minor HTTP version or end of request line */ | |
| 1132 | + case s_req_http_minor: | |
| 1133 | + { | |
| 1134 | + if (ch == CR) { | |
| 1135 | + parser->state = s_req_line_almost_done; | |
| 1136 | + break; | |
| 1137 | + } | |
| 1138 | + | |
| 1139 | + if (ch == LF) { | |
| 1140 | + parser->state = s_header_field_start; | |
| 1141 | + break; | |
| 1142 | + } | |
| 1143 | + | |
| 1144 | + /* XXX allow spaces after digit? */ | |
| 1145 | + | |
| 1146 | + if (!IS_NUM(ch)) { | |
| 1147 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1148 | + goto error; | |
| 1149 | + } | |
| 1150 | + | |
| 1151 | + parser->http_minor *= 10; | |
| 1152 | + parser->http_minor += ch - '0'; | |
| 1153 | + | |
| 1154 | + if (parser->http_minor > 999) { | |
| 1155 | + SET_ERRNO(HPE_INVALID_VERSION); | |
| 1156 | + goto error; | |
| 1157 | + } | |
| 1158 | + | |
| 1159 | + break; | |
| 1160 | + } | |
| 1161 | + | |
| 1162 | + /* end of request line */ | |
| 1163 | + case s_req_line_almost_done: | |
| 1164 | + { | |
| 1165 | + if (ch != LF) { | |
| 1166 | + SET_ERRNO(HPE_LF_EXPECTED); | |
| 1167 | + goto error; | |
| 1168 | + } | |
| 1169 | + | |
| 1170 | + parser->state = s_header_field_start; | |
| 1171 | + break; | |
| 1172 | + } | |
| 1173 | + | |
| 1174 | + case s_header_field_start: | |
| 1175 | + { | |
| 1176 | + if (ch == CR) { | |
| 1177 | + parser->state = s_headers_almost_done; | |
| 1178 | + break; | |
| 1179 | + } | |
| 1180 | + | |
| 1181 | + if (ch == LF) { | |
| 1182 | + /* they might be just sending \n instead of \r\n so this would be | |
| 1183 | + * the second \n to denote the end of headers*/ | |
| 1184 | + parser->state = s_headers_almost_done; | |
| 1185 | + goto reexecute_byte; | |
| 1186 | + } | |
| 1187 | + | |
| 1188 | + c = TOKEN(ch); | |
| 1189 | + | |
| 1190 | + if (!c) { | |
| 1191 | + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); | |
| 1192 | + goto error; | |
| 1193 | + } | |
| 1194 | + | |
| 1195 | + MARK(header_field); | |
| 1196 | + | |
| 1197 | + parser->index = 0; | |
| 1198 | + parser->state = s_header_field; | |
| 1199 | + | |
| 1200 | + switch (c) { | |
| 1201 | + case 'c': | |
| 1202 | + parser->header_state = h_C; | |
| 1203 | + break; | |
| 1204 | + | |
| 1205 | + case 'p': | |
| 1206 | + parser->header_state = h_matching_proxy_connection; | |
| 1207 | + break; | |
| 1208 | + | |
| 1209 | + case 't': | |
| 1210 | + parser->header_state = h_matching_transfer_encoding; | |
| 1211 | + break; | |
| 1212 | + | |
| 1213 | + case 'u': | |
| 1214 | + parser->header_state = h_matching_upgrade; | |
| 1215 | + break; | |
| 1216 | + | |
| 1217 | + default: | |
| 1218 | + parser->header_state = h_general; | |
| 1219 | + break; | |
| 1220 | + } | |
| 1221 | + break; | |
| 1222 | + } | |
| 1223 | + | |
| 1224 | + case s_header_field: | |
| 1225 | + { | |
| 1226 | + c = TOKEN(ch); | |
| 1227 | + | |
| 1228 | + if (c) { | |
| 1229 | + switch (parser->header_state) { | |
| 1230 | + case h_general: | |
| 1231 | + break; | |
| 1232 | + | |
| 1233 | + case h_C: | |
| 1234 | + parser->index++; | |
| 1235 | + parser->header_state = (c == 'o' ? h_CO : h_general); | |
| 1236 | + break; | |
| 1237 | + | |
| 1238 | + case h_CO: | |
| 1239 | + parser->index++; | |
| 1240 | + parser->header_state = (c == 'n' ? h_CON : h_general); | |
| 1241 | + break; | |
| 1242 | + | |
| 1243 | + case h_CON: | |
| 1244 | + parser->index++; | |
| 1245 | + switch (c) { | |
| 1246 | + case 'n': | |
| 1247 | + parser->header_state = h_matching_connection; | |
| 1248 | + break; | |
| 1249 | + case 't': | |
| 1250 | + parser->header_state = h_matching_content_length; | |
| 1251 | + break; | |
| 1252 | + default: | |
| 1253 | + parser->header_state = h_general; | |
| 1254 | + break; | |
| 1255 | + } | |
| 1256 | + break; | |
| 1257 | + | |
| 1258 | + /* connection */ | |
| 1259 | + | |
| 1260 | + case h_matching_connection: | |
| 1261 | + parser->index++; | |
| 1262 | + if (parser->index > sizeof(CONNECTION)-1 | |
| 1263 | + || c != CONNECTION[parser->index]) { | |
| 1264 | + parser->header_state = h_general; | |
| 1265 | + } else if (parser->index == sizeof(CONNECTION)-2) { | |
| 1266 | + parser->header_state = h_connection; | |
| 1267 | + } | |
| 1268 | + break; | |
| 1269 | + | |
| 1270 | + /* proxy-connection */ | |
| 1271 | + | |
| 1272 | + case h_matching_proxy_connection: | |
| 1273 | + parser->index++; | |
| 1274 | + if (parser->index > sizeof(PROXY_CONNECTION)-1 | |
| 1275 | + || c != PROXY_CONNECTION[parser->index]) { | |
| 1276 | + parser->header_state = h_general; | |
| 1277 | + } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { | |
| 1278 | + parser->header_state = h_connection; | |
| 1279 | + } | |
| 1280 | + break; | |
| 1281 | + | |
| 1282 | + /* content-length */ | |
| 1283 | + | |
| 1284 | + case h_matching_content_length: | |
| 1285 | + parser->index++; | |
| 1286 | + if (parser->index > sizeof(CONTENT_LENGTH)-1 | |
| 1287 | + || c != CONTENT_LENGTH[parser->index]) { | |
| 1288 | + parser->header_state = h_general; | |
| 1289 | + } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { | |
| 1290 | + parser->header_state = h_content_length; | |
| 1291 | + } | |
| 1292 | + break; | |
| 1293 | + | |
| 1294 | + /* transfer-encoding */ | |
| 1295 | + | |
| 1296 | + case h_matching_transfer_encoding: | |
| 1297 | + parser->index++; | |
| 1298 | + if (parser->index > sizeof(TRANSFER_ENCODING)-1 | |
| 1299 | + || c != TRANSFER_ENCODING[parser->index]) { | |
| 1300 | + parser->header_state = h_general; | |
| 1301 | + } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { | |
| 1302 | + parser->header_state = h_transfer_encoding; | |
| 1303 | + } | |
| 1304 | + break; | |
| 1305 | + | |
| 1306 | + /* upgrade */ | |
| 1307 | + | |
| 1308 | + case h_matching_upgrade: | |
| 1309 | + parser->index++; | |
| 1310 | + if (parser->index > sizeof(UPGRADE)-1 | |
| 1311 | + || c != UPGRADE[parser->index]) { | |
| 1312 | + parser->header_state = h_general; | |
| 1313 | + } else if (parser->index == sizeof(UPGRADE)-2) { | |
| 1314 | + parser->header_state = h_upgrade; | |
| 1315 | + } | |
| 1316 | + break; | |
| 1317 | + | |
| 1318 | + case h_connection: | |
| 1319 | + case h_content_length: | |
| 1320 | + case h_transfer_encoding: | |
| 1321 | + case h_upgrade: | |
| 1322 | + if (ch != ' ') parser->header_state = h_general; | |
| 1323 | + break; | |
| 1324 | + | |
| 1325 | + default: | |
| 1326 | + assert(0 && "Unknown header_state"); | |
| 1327 | + break; | |
| 1328 | + } | |
| 1329 | + break; | |
| 1330 | + } | |
| 1331 | + | |
| 1332 | + if (ch == ':') { | |
| 1333 | + parser->state = s_header_value_start; | |
| 1334 | + CALLBACK_DATA(header_field); | |
| 1335 | + break; | |
| 1336 | + } | |
| 1337 | + | |
| 1338 | + if (ch == CR) { | |
| 1339 | + parser->state = s_header_almost_done; | |
| 1340 | + CALLBACK_DATA(header_field); | |
| 1341 | + break; | |
| 1342 | + } | |
| 1343 | + | |
| 1344 | + if (ch == LF) { | |
| 1345 | + parser->state = s_header_field_start; | |
| 1346 | + CALLBACK_DATA(header_field); | |
| 1347 | + break; | |
| 1348 | + } | |
| 1349 | + | |
| 1350 | + SET_ERRNO(HPE_INVALID_HEADER_TOKEN); | |
| 1351 | + goto error; | |
| 1352 | + } | |
| 1353 | + | |
| 1354 | + case s_header_value_start: | |
| 1355 | + { | |
| 1356 | + if (ch == ' ' || ch == '\t') break; | |
| 1357 | + | |
| 1358 | + MARK(header_value); | |
| 1359 | + | |
| 1360 | + parser->state = s_header_value; | |
| 1361 | + parser->index = 0; | |
| 1362 | + | |
| 1363 | + if (ch == CR) { | |
| 1364 | + parser->header_state = h_general; | |
| 1365 | + parser->state = s_header_almost_done; | |
| 1366 | + CALLBACK_DATA(header_value); | |
| 1367 | + break; | |
| 1368 | + } | |
| 1369 | + | |
| 1370 | + if (ch == LF) { | |
| 1371 | + parser->state = s_header_field_start; | |
| 1372 | + CALLBACK_DATA(header_value); | |
| 1373 | + break; | |
| 1374 | + } | |
| 1375 | + | |
| 1376 | + c = LOWER(ch); | |
| 1377 | + | |
| 1378 | + switch (parser->header_state) { | |
| 1379 | + case h_upgrade: | |
| 1380 | + parser->flags |= F_UPGRADE; | |
| 1381 | + parser->header_state = h_general; | |
| 1382 | + break; | |
| 1383 | + | |
| 1384 | + case h_transfer_encoding: | |
| 1385 | + /* looking for 'Transfer-Encoding: chunked' */ | |
| 1386 | + if ('c' == c) { | |
| 1387 | + parser->header_state = h_matching_transfer_encoding_chunked; | |
| 1388 | + } else { | |
| 1389 | + parser->header_state = h_general; | |
| 1390 | + } | |
| 1391 | + break; | |
| 1392 | + | |
| 1393 | + case h_content_length: | |
| 1394 | + if (!IS_NUM(ch)) { | |
| 1395 | + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); | |
| 1396 | + goto error; | |
| 1397 | + } | |
| 1398 | + | |
| 1399 | + parser->content_length = ch - '0'; | |
| 1400 | + break; | |
| 1401 | + | |
| 1402 | + case h_connection: | |
| 1403 | + /* looking for 'Connection: keep-alive' */ | |
| 1404 | + if (c == 'k') { | |
| 1405 | + parser->header_state = h_matching_connection_keep_alive; | |
| 1406 | + /* looking for 'Connection: close' */ | |
| 1407 | + } else if (c == 'c') { | |
| 1408 | + parser->header_state = h_matching_connection_close; | |
| 1409 | + } else { | |
| 1410 | + parser->header_state = h_general; | |
| 1411 | + } | |
| 1412 | + break; | |
| 1413 | + | |
| 1414 | + default: | |
| 1415 | + parser->header_state = h_general; | |
| 1416 | + break; | |
| 1417 | + } | |
| 1418 | + break; | |
| 1419 | + } | |
| 1420 | + | |
| 1421 | + case s_header_value: | |
| 1422 | + { | |
| 1423 | + | |
| 1424 | + if (ch == CR) { | |
| 1425 | + parser->state = s_header_almost_done; | |
| 1426 | + CALLBACK_DATA(header_value); | |
| 1427 | + break; | |
| 1428 | + } | |
| 1429 | + | |
| 1430 | + if (ch == LF) { | |
| 1431 | + parser->state = s_header_almost_done; | |
| 1432 | + CALLBACK_DATA_NOADVANCE(header_value); | |
| 1433 | + goto reexecute_byte; | |
| 1434 | + } | |
| 1435 | + | |
| 1436 | + c = LOWER(ch); | |
| 1437 | + | |
| 1438 | + switch (parser->header_state) { | |
| 1439 | + case h_general: | |
| 1440 | + break; | |
| 1441 | + | |
| 1442 | + case h_connection: | |
| 1443 | + case h_transfer_encoding: | |
| 1444 | + assert(0 && "Shouldn't get here."); | |
| 1445 | + break; | |
| 1446 | + | |
| 1447 | + case h_content_length: | |
| 1448 | + { | |
| 1449 | + uint64_t t; | |
| 1450 | + | |
| 1451 | + if (ch == ' ') break; | |
| 1452 | + | |
| 1453 | + if (!IS_NUM(ch)) { | |
| 1454 | + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); | |
| 1455 | + goto error; | |
| 1456 | + } | |
| 1457 | + | |
| 1458 | + t = parser->content_length; | |
| 1459 | + t *= 10; | |
| 1460 | + t += ch - '0'; | |
| 1461 | + | |
| 1462 | + /* Overflow? */ | |
| 1463 | + if (t < parser->content_length || t == ULLONG_MAX) { | |
| 1464 | + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); | |
| 1465 | + goto error; | |
| 1466 | + } | |
| 1467 | + | |
| 1468 | + parser->content_length = t; | |
| 1469 | + break; | |
| 1470 | + } | |
| 1471 | + | |
| 1472 | + /* Transfer-Encoding: chunked */ | |
| 1473 | + case h_matching_transfer_encoding_chunked: | |
| 1474 | + parser->index++; | |
| 1475 | + if (parser->index > sizeof(CHUNKED)-1 | |
| 1476 | + || c != CHUNKED[parser->index]) { | |
| 1477 | + parser->header_state = h_general; | |
| 1478 | + } else if (parser->index == sizeof(CHUNKED)-2) { | |
| 1479 | + parser->header_state = h_transfer_encoding_chunked; | |
| 1480 | + } | |
| 1481 | + break; | |
| 1482 | + | |
| 1483 | + /* looking for 'Connection: keep-alive' */ | |
| 1484 | + case h_matching_connection_keep_alive: | |
| 1485 | + parser->index++; | |
| 1486 | + if (parser->index > sizeof(KEEP_ALIVE)-1 | |
| 1487 | + || c != KEEP_ALIVE[parser->index]) { | |
| 1488 | + parser->header_state = h_general; | |
| 1489 | + } else if (parser->index == sizeof(KEEP_ALIVE)-2) { | |
| 1490 | + parser->header_state = h_connection_keep_alive; | |
| 1491 | + } | |
| 1492 | + break; | |
| 1493 | + | |
| 1494 | + /* looking for 'Connection: close' */ | |
| 1495 | + case h_matching_connection_close: | |
| 1496 | + parser->index++; | |
| 1497 | + if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { | |
| 1498 | + parser->header_state = h_general; | |
| 1499 | + } else if (parser->index == sizeof(CLOSE)-2) { | |
| 1500 | + parser->header_state = h_connection_close; | |
| 1501 | + } | |
| 1502 | + break; | |
| 1503 | + | |
| 1504 | + case h_transfer_encoding_chunked: | |
| 1505 | + case h_connection_keep_alive: | |
| 1506 | + case h_connection_close: | |
| 1507 | + if (ch != ' ') parser->header_state = h_general; | |
| 1508 | + break; | |
| 1509 | + | |
| 1510 | + default: | |
| 1511 | + parser->state = s_header_value; | |
| 1512 | + parser->header_state = h_general; | |
| 1513 | + break; | |
| 1514 | + } | |
| 1515 | + break; | |
| 1516 | + } | |
| 1517 | + | |
| 1518 | + case s_header_almost_done: | |
| 1519 | + { | |
| 1520 | + STRICT_CHECK(ch != LF); | |
| 1521 | + | |
| 1522 | + parser->state = s_header_value_lws; | |
| 1523 | + | |
| 1524 | + switch (parser->header_state) { | |
| 1525 | + case h_connection_keep_alive: | |
| 1526 | + parser->flags |= F_CONNECTION_KEEP_ALIVE; | |
| 1527 | + break; | |
| 1528 | + case h_connection_close: | |
| 1529 | + parser->flags |= F_CONNECTION_CLOSE; | |
| 1530 | + break; | |
| 1531 | + case h_transfer_encoding_chunked: | |
| 1532 | + parser->flags |= F_CHUNKED; | |
| 1533 | + break; | |
| 1534 | + default: | |
| 1535 | + break; | |
| 1536 | + } | |
| 1537 | + | |
| 1538 | + break; | |
| 1539 | + } | |
| 1540 | + | |
| 1541 | + case s_header_value_lws: | |
| 1542 | + { | |
| 1543 | + if (ch == ' ' || ch == '\t') | |
| 1544 | + parser->state = s_header_value_start; | |
| 1545 | + else | |
| 1546 | + { | |
| 1547 | + parser->state = s_header_field_start; | |
| 1548 | + goto reexecute_byte; | |
| 1549 | + } | |
| 1550 | + break; | |
| 1551 | + } | |
| 1552 | + | |
| 1553 | + case s_headers_almost_done: | |
| 1554 | + { | |
| 1555 | + STRICT_CHECK(ch != LF); | |
| 1556 | + | |
| 1557 | + if (parser->flags & F_TRAILING) { | |
| 1558 | + /* End of a chunked request */ | |
| 1559 | + parser->state = NEW_MESSAGE(); | |
| 1560 | + CALLBACK_NOTIFY(message_complete); | |
| 1561 | + break; | |
| 1562 | + } | |
| 1563 | + | |
| 1564 | + parser->state = s_headers_done; | |
| 1565 | + | |
| 1566 | + /* Set this here so that on_headers_complete() callbacks can see it */ | |
| 1567 | + parser->upgrade = | |
| 1568 | + (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); | |
| 1569 | + | |
| 1570 | + /* Here we call the headers_complete callback. This is somewhat | |
| 1571 | + * different than other callbacks because if the user returns 1, we | |
| 1572 | + * will interpret that as saying that this message has no body. This | |
| 1573 | + * is needed for the annoying case of recieving a response to a HEAD | |
| 1574 | + * request. | |
| 1575 | + * | |
| 1576 | + * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so | |
| 1577 | + * we have to simulate it by handling a change in errno below. | |
| 1578 | + */ | |
| 1579 | + if (settings->on_headers_complete) { | |
| 1580 | + switch (settings->on_headers_complete(parser)) { | |
| 1581 | + case 0: | |
| 1582 | + break; | |
| 1583 | + | |
| 1584 | + case 1: | |
| 1585 | + parser->flags |= F_SKIPBODY; | |
| 1586 | + break; | |
| 1587 | + | |
| 1588 | + default: | |
| 1589 | + SET_ERRNO(HPE_CB_headers_complete); | |
| 1590 | + return p - data; /* Error */ | |
| 1591 | + } | |
| 1592 | + } | |
| 1593 | + | |
| 1594 | + if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { | |
| 1595 | + return p - data; | |
| 1596 | + } | |
| 1597 | + | |
| 1598 | + goto reexecute_byte; | |
| 1599 | + } | |
| 1600 | + | |
| 1601 | + case s_headers_done: | |
| 1602 | + { | |
| 1603 | + STRICT_CHECK(ch != LF); | |
| 1604 | + | |
| 1605 | + parser->nread = 0; | |
| 1606 | + | |
| 1607 | + /* Exit, the rest of the connect is in a different protocol. */ | |
| 1608 | + if (parser->upgrade) { | |
| 1609 | + parser->state = NEW_MESSAGE(); | |
| 1610 | + CALLBACK_NOTIFY(message_complete); | |
| 1611 | + return (p - data) + 1; | |
| 1612 | + } | |
| 1613 | + | |
| 1614 | + if (parser->flags & F_SKIPBODY) { | |
| 1615 | + parser->state = NEW_MESSAGE(); | |
| 1616 | + CALLBACK_NOTIFY(message_complete); | |
| 1617 | + } else if (parser->flags & F_CHUNKED) { | |
| 1618 | + /* chunked encoding - ignore Content-Length header */ | |
| 1619 | + parser->state = s_chunk_size_start; | |
| 1620 | + } else { | |
| 1621 | + if (parser->content_length == 0) { | |
| 1622 | + /* Content-Length header given but zero: Content-Length: 0\r\n */ | |
| 1623 | + parser->state = NEW_MESSAGE(); | |
| 1624 | + CALLBACK_NOTIFY(message_complete); | |
| 1625 | + } else if (parser->content_length != ULLONG_MAX) { | |
| 1626 | + /* Content-Length header given and non-zero */ | |
| 1627 | + parser->state = s_body_identity; | |
| 1628 | + } else { | |
| 1629 | + if (parser->type == HTTP_REQUEST || | |
| 1630 | + !http_message_needs_eof(parser)) { | |
| 1631 | + /* Assume content-length 0 - read the next */ | |
| 1632 | + parser->state = NEW_MESSAGE(); | |
| 1633 | + CALLBACK_NOTIFY(message_complete); | |
| 1634 | + } else { | |
| 1635 | + /* Read body until EOF */ | |
| 1636 | + parser->state = s_body_identity_eof; | |
| 1637 | + } | |
| 1638 | + } | |
| 1639 | + } | |
| 1640 | + | |
| 1641 | + break; | |
| 1642 | + } | |
| 1643 | + | |
| 1644 | + case s_body_identity: | |
| 1645 | + { | |
| 1646 | + uint64_t to_read = MIN(parser->content_length, | |
| 1647 | + (uint64_t) ((data + len) - p)); | |
| 1648 | + | |
| 1649 | + assert(parser->content_length != 0 | |
| 1650 | + && parser->content_length != ULLONG_MAX); | |
| 1651 | + | |
| 1652 | + /* The difference between advancing content_length and p is because | |
| 1653 | + * the latter will automaticaly advance on the next loop iteration. | |
| 1654 | + * Further, if content_length ends up at 0, we want to see the last | |
| 1655 | + * byte again for our message complete callback. | |
| 1656 | + */ | |
| 1657 | + MARK(body); | |
| 1658 | + parser->content_length -= to_read; | |
| 1659 | + p += to_read - 1; | |
| 1660 | + | |
| 1661 | + if (parser->content_length == 0) { | |
| 1662 | + parser->state = s_message_done; | |
| 1663 | + | |
| 1664 | + /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. | |
| 1665 | + * | |
| 1666 | + * The alternative to doing this is to wait for the next byte to | |
| 1667 | + * trigger the data callback, just as in every other case. The | |
| 1668 | + * problem with this is that this makes it difficult for the test | |
| 1669 | + * harness to distinguish between complete-on-EOF and | |
| 1670 | + * complete-on-length. It's not clear that this distinction is | |
| 1671 | + * important for applications, but let's keep it for now. | |
| 1672 | + */ | |
| 1673 | + CALLBACK_DATA_(body, p - body_mark + 1, p - data); | |
| 1674 | + goto reexecute_byte; | |
| 1675 | + } | |
| 1676 | + | |
| 1677 | + break; | |
| 1678 | + } | |
| 1679 | + | |
| 1680 | + /* read until EOF */ | |
| 1681 | + case s_body_identity_eof: | |
| 1682 | + MARK(body); | |
| 1683 | + p = data + len - 1; | |
| 1684 | + | |
| 1685 | + break; | |
| 1686 | + | |
| 1687 | + case s_message_done: | |
| 1688 | + parser->state = NEW_MESSAGE(); | |
| 1689 | + CALLBACK_NOTIFY(message_complete); | |
| 1690 | + break; | |
| 1691 | + | |
| 1692 | + case s_chunk_size_start: | |
| 1693 | + { | |
| 1694 | + assert(parser->nread == 1); | |
| 1695 | + assert(parser->flags & F_CHUNKED); | |
| 1696 | + | |
| 1697 | + unhex_val = unhex[(unsigned char)ch]; | |
| 1698 | + if (unhex_val == -1) { | |
| 1699 | + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); | |
| 1700 | + goto error; | |
| 1701 | + } | |
| 1702 | + | |
| 1703 | + parser->content_length = unhex_val; | |
| 1704 | + parser->state = s_chunk_size; | |
| 1705 | + break; | |
| 1706 | + } | |
| 1707 | + | |
| 1708 | + case s_chunk_size: | |
| 1709 | + { | |
| 1710 | + uint64_t t; | |
| 1711 | + | |
| 1712 | + assert(parser->flags & F_CHUNKED); | |
| 1713 | + | |
| 1714 | + if (ch == CR) { | |
| 1715 | + parser->state = s_chunk_size_almost_done; | |
| 1716 | + break; | |
| 1717 | + } | |
| 1718 | + | |
| 1719 | + unhex_val = unhex[(unsigned char)ch]; | |
| 1720 | + | |
| 1721 | + if (unhex_val == -1) { | |
| 1722 | + if (ch == ';' || ch == ' ') { | |
| 1723 | + parser->state = s_chunk_parameters; | |
| 1724 | + break; | |
| 1725 | + } | |
| 1726 | + | |
| 1727 | + SET_ERRNO(HPE_INVALID_CHUNK_SIZE); | |
| 1728 | + goto error; | |
| 1729 | + } | |
| 1730 | + | |
| 1731 | + t = parser->content_length; | |
| 1732 | + t *= 16; | |
| 1733 | + t += unhex_val; | |
| 1734 | + | |
| 1735 | + /* Overflow? */ | |
| 1736 | + if (t < parser->content_length || t == ULLONG_MAX) { | |
| 1737 | + SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); | |
| 1738 | + goto error; | |
| 1739 | + } | |
| 1740 | + | |
| 1741 | + parser->content_length = t; | |
| 1742 | + break; | |
| 1743 | + } | |
| 1744 | + | |
| 1745 | + case s_chunk_parameters: | |
| 1746 | + { | |
| 1747 | + assert(parser->flags & F_CHUNKED); | |
| 1748 | + /* just ignore this shit. TODO check for overflow */ | |
| 1749 | + if (ch == CR) { | |
| 1750 | + parser->state = s_chunk_size_almost_done; | |
| 1751 | + break; | |
| 1752 | + } | |
| 1753 | + break; | |
| 1754 | + } | |
| 1755 | + | |
| 1756 | + case s_chunk_size_almost_done: | |
| 1757 | + { | |
| 1758 | + assert(parser->flags & F_CHUNKED); | |
| 1759 | + STRICT_CHECK(ch != LF); | |
| 1760 | + | |
| 1761 | + parser->nread = 0; | |
| 1762 | + | |
| 1763 | + if (parser->content_length == 0) { | |
| 1764 | + parser->flags |= F_TRAILING; | |
| 1765 | + parser->state = s_header_field_start; | |
| 1766 | + } else { | |
| 1767 | + parser->state = s_chunk_data; | |
| 1768 | + } | |
| 1769 | + break; | |
| 1770 | + } | |
| 1771 | + | |
| 1772 | + case s_chunk_data: | |
| 1773 | + { | |
| 1774 | + uint64_t to_read = MIN(parser->content_length, | |
| 1775 | + (uint64_t) ((data + len) - p)); | |
| 1776 | + | |
| 1777 | + assert(parser->flags & F_CHUNKED); | |
| 1778 | + assert(parser->content_length != 0 | |
| 1779 | + && parser->content_length != ULLONG_MAX); | |
| 1780 | + | |
| 1781 | + /* See the explanation in s_body_identity for why the content | |
| 1782 | + * length and data pointers are managed this way. | |
| 1783 | + */ | |
| 1784 | + MARK(body); | |
| 1785 | + parser->content_length -= to_read; | |
| 1786 | + p += to_read - 1; | |
| 1787 | + | |
| 1788 | + if (parser->content_length == 0) { | |
| 1789 | + parser->state = s_chunk_data_almost_done; | |
| 1790 | + } | |
| 1791 | + | |
| 1792 | + break; | |
| 1793 | + } | |
| 1794 | + | |
| 1795 | + case s_chunk_data_almost_done: | |
| 1796 | + assert(parser->flags & F_CHUNKED); | |
| 1797 | + assert(parser->content_length == 0); | |
| 1798 | + STRICT_CHECK(ch != CR); | |
| 1799 | + parser->state = s_chunk_data_done; | |
| 1800 | + CALLBACK_DATA(body); | |
| 1801 | + break; | |
| 1802 | + | |
| 1803 | + case s_chunk_data_done: | |
| 1804 | + assert(parser->flags & F_CHUNKED); | |
| 1805 | + STRICT_CHECK(ch != LF); | |
| 1806 | + parser->nread = 0; | |
| 1807 | + parser->state = s_chunk_size_start; | |
| 1808 | + break; | |
| 1809 | + | |
| 1810 | + default: | |
| 1811 | + assert(0 && "unhandled state"); | |
| 1812 | + SET_ERRNO(HPE_INVALID_INTERNAL_STATE); | |
| 1813 | + goto error; | |
| 1814 | + } | |
| 1815 | + } | |
| 1816 | + | |
| 1817 | + /* Run callbacks for any marks that we have leftover after we ran our of | |
| 1818 | + * bytes. There should be at most one of these set, so it's OK to invoke | |
| 1819 | + * them in series (unset marks will not result in callbacks). | |
| 1820 | + * | |
| 1821 | + * We use the NOADVANCE() variety of callbacks here because 'p' has already | |
| 1822 | + * overflowed 'data' and this allows us to correct for the off-by-one that | |
| 1823 | + * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' | |
| 1824 | + * value that's in-bounds). | |
| 1825 | + */ | |
| 1826 | + | |
| 1827 | + assert(((header_field_mark ? 1 : 0) + | |
| 1828 | + (header_value_mark ? 1 : 0) + | |
| 1829 | + (url_mark ? 1 : 0) + | |
| 1830 | + (body_mark ? 1 : 0)) <= 1); | |
| 1831 | + | |
| 1832 | + CALLBACK_DATA_NOADVANCE(header_field); | |
| 1833 | + CALLBACK_DATA_NOADVANCE(header_value); | |
| 1834 | + CALLBACK_DATA_NOADVANCE(url); | |
| 1835 | + CALLBACK_DATA_NOADVANCE(body); | |
| 1836 | + | |
| 1837 | + return len; | |
| 1838 | + | |
| 1839 | +error: | |
| 1840 | + if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { | |
| 1841 | + SET_ERRNO(HPE_UNKNOWN); | |
| 1842 | + } | |
| 1843 | + | |
| 1844 | + return (p - data); | |
| 1845 | +} | |
| 1846 | + | |
| 1847 | + | |
| 1848 | +/* Does the parser need to see an EOF to find the end of the message? */ | |
| 1849 | +int | |
| 1850 | +http_message_needs_eof (const http_parser *parser) | |
| 1851 | +{ | |
| 1852 | + if (parser->type == HTTP_REQUEST) { | |
| 1853 | + return 0; | |
| 1854 | + } | |
| 1855 | + | |
| 1856 | + /* See RFC 2616 section 4.4 */ | |
| 1857 | + if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ | |
| 1858 | + parser->status_code == 204 || /* No Content */ | |
| 1859 | + parser->status_code == 304 || /* Not Modified */ | |
| 1860 | + parser->flags & F_SKIPBODY) { /* response to a HEAD request */ | |
| 1861 | + return 0; | |
| 1862 | + } | |
| 1863 | + | |
| 1864 | + if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { | |
| 1865 | + return 0; | |
| 1866 | + } | |
| 1867 | + | |
| 1868 | + return 1; | |
| 1869 | +} | |
| 1870 | + | |
| 1871 | + | |
| 1872 | +int | |
| 1873 | +http_should_keep_alive (const http_parser *parser) | |
| 1874 | +{ | |
| 1875 | + if (parser->http_major > 0 && parser->http_minor > 0) { | |
| 1876 | + /* HTTP/1.1 */ | |
| 1877 | + if (parser->flags & F_CONNECTION_CLOSE) { | |
| 1878 | + return 0; | |
| 1879 | + } | |
| 1880 | + } else { | |
| 1881 | + /* HTTP/1.0 or earlier */ | |
| 1882 | + if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { | |
| 1883 | + return 0; | |
| 1884 | + } | |
| 1885 | + } | |
| 1886 | + | |
| 1887 | + return !http_message_needs_eof(parser); | |
| 1888 | +} | |
| 1889 | + | |
| 1890 | + | |
| 1891 | +const char * | |
| 1892 | +http_method_str (enum http_method m) | |
| 1893 | +{ | |
| 1894 | + return ELEM_AT(method_strings, m, "<unknown>"); | |
| 1895 | +} | |
| 1896 | + | |
| 1897 | + | |
| 1898 | +void | |
| 1899 | +http_parser_init (http_parser *parser, enum http_parser_type t) | |
| 1900 | +{ | |
| 1901 | + void *data = parser->data; /* preserve application data */ | |
| 1902 | + memset(parser, 0, sizeof(*parser)); | |
| 1903 | + parser->data = data; | |
| 1904 | + parser->type = t; | |
| 1905 | + parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); | |
| 1906 | + parser->http_errno = HPE_OK; | |
| 1907 | +} | |
| 1908 | + | |
| 1909 | +const char * | |
| 1910 | +http_errno_name(enum http_errno err) { | |
| 1911 | + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); | |
| 1912 | + return http_strerror_tab[err].name; | |
| 1913 | +} | |
| 1914 | + | |
| 1915 | +const char * | |
| 1916 | +http_errno_description(enum http_errno err) { | |
| 1917 | + assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); | |
| 1918 | + return http_strerror_tab[err].description; | |
| 1919 | +} | |
| 1920 | + | |
| 1921 | +static enum http_host_state | |
| 1922 | +http_parse_host_char(enum http_host_state s, const char ch) { | |
| 1923 | + switch(s) { | |
| 1924 | + case s_http_userinfo: | |
| 1925 | + case s_http_userinfo_start: | |
| 1926 | + if (ch == '@') { | |
| 1927 | + return s_http_host_start; | |
| 1928 | + } | |
| 1929 | + | |
| 1930 | + if (IS_USERINFO_CHAR(ch)) { | |
| 1931 | + return s_http_userinfo; | |
| 1932 | + } | |
| 1933 | + break; | |
| 1934 | + | |
| 1935 | + case s_http_host_start: | |
| 1936 | + if (ch == '[') { | |
| 1937 | + return s_http_host_v6_start; | |
| 1938 | + } | |
| 1939 | + | |
| 1940 | + if (IS_HOST_CHAR(ch)) { | |
| 1941 | + return s_http_host; | |
| 1942 | + } | |
| 1943 | + | |
| 1944 | + break; | |
| 1945 | + | |
| 1946 | + case s_http_host: | |
| 1947 | + if (IS_HOST_CHAR(ch)) { | |
| 1948 | + return s_http_host; | |
| 1949 | + } | |
| 1950 | + | |
| 1951 | + /* FALLTHROUGH */ | |
| 1952 | + case s_http_host_v6_end: | |
| 1953 | + if (ch == ':') { | |
| 1954 | + return s_http_host_port_start; | |
| 1955 | + } | |
| 1956 | + | |
| 1957 | + break; | |
| 1958 | + | |
| 1959 | + case s_http_host_v6: | |
| 1960 | + if (ch == ']') { | |
| 1961 | + return s_http_host_v6_end; | |
| 1962 | + } | |
| 1963 | + | |
| 1964 | + /* FALLTHROUGH */ | |
| 1965 | + case s_http_host_v6_start: | |
| 1966 | + if (IS_HEX(ch) || ch == ':' || ch == '.') { | |
| 1967 | + return s_http_host_v6; | |
| 1968 | + } | |
| 1969 | + | |
| 1970 | + break; | |
| 1971 | + | |
| 1972 | + case s_http_host_port: | |
| 1973 | + case s_http_host_port_start: | |
| 1974 | + if (IS_NUM(ch)) { | |
| 1975 | + return s_http_host_port; | |
| 1976 | + } | |
| 1977 | + | |
| 1978 | + break; | |
| 1979 | + | |
| 1980 | + default: | |
| 1981 | + break; | |
| 1982 | + } | |
| 1983 | + return s_http_host_dead; | |
| 1984 | +} | |
| 1985 | + | |
| 1986 | +static int | |
| 1987 | +http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { | |
| 1988 | + enum http_host_state s; | |
| 1989 | + | |
| 1990 | + const char *p; | |
| 1991 | + size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; | |
| 1992 | + | |
| 1993 | + u->field_data[UF_HOST].len = 0; | |
| 1994 | + | |
| 1995 | + s = found_at ? s_http_userinfo_start : s_http_host_start; | |
| 1996 | + | |
| 1997 | + for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { | |
| 1998 | + enum http_host_state new_s = http_parse_host_char(s, *p); | |
| 1999 | + | |
| 2000 | + if (new_s == s_http_host_dead) { | |
| 2001 | + return 1; | |
| 2002 | + } | |
| 2003 | + | |
| 2004 | + switch(new_s) { | |
| 2005 | + case s_http_host: | |
| 2006 | + if (s != s_http_host) { | |
| 2007 | + u->field_data[UF_HOST].off = p - buf; | |
| 2008 | + } | |
| 2009 | + u->field_data[UF_HOST].len++; | |
| 2010 | + break; | |
| 2011 | + | |
| 2012 | + case s_http_host_v6: | |
| 2013 | + if (s != s_http_host_v6) { | |
| 2014 | + u->field_data[UF_HOST].off = p - buf; | |
| 2015 | + } | |
| 2016 | + u->field_data[UF_HOST].len++; | |
| 2017 | + break; | |
| 2018 | + | |
| 2019 | + case s_http_host_port: | |
| 2020 | + if (s != s_http_host_port) { | |
| 2021 | + u->field_data[UF_PORT].off = p - buf; | |
| 2022 | + u->field_data[UF_PORT].len = 0; | |
| 2023 | + u->field_set |= (1 << UF_PORT); | |
| 2024 | + } | |
| 2025 | + u->field_data[UF_PORT].len++; | |
| 2026 | + break; | |
| 2027 | + | |
| 2028 | + case s_http_userinfo: | |
| 2029 | + if (s != s_http_userinfo) { | |
| 2030 | + u->field_data[UF_USERINFO].off = p - buf ; | |
| 2031 | + u->field_data[UF_USERINFO].len = 0; | |
| 2032 | + u->field_set |= (1 << UF_USERINFO); | |
| 2033 | + } | |
| 2034 | + u->field_data[UF_USERINFO].len++; | |
| 2035 | + break; | |
| 2036 | + | |
| 2037 | + default: | |
| 2038 | + break; | |
| 2039 | + } | |
| 2040 | + s = new_s; | |
| 2041 | + } | |
| 2042 | + | |
| 2043 | + /* Make sure we don't end somewhere unexpected */ | |
| 2044 | + switch (s) { | |
| 2045 | + case s_http_host_start: | |
| 2046 | + case s_http_host_v6_start: | |
| 2047 | + case s_http_host_v6: | |
| 2048 | + case s_http_host_port_start: | |
| 2049 | + case s_http_userinfo: | |
| 2050 | + case s_http_userinfo_start: | |
| 2051 | + return 1; | |
| 2052 | + default: | |
| 2053 | + break; | |
| 2054 | + } | |
| 2055 | + | |
| 2056 | + return 0; | |
| 2057 | +} | |
| 2058 | + | |
| 2059 | +int | |
| 2060 | +http_parser_parse_url(const char *buf, size_t buflen, int is_connect, | |
| 2061 | + struct http_parser_url *u) | |
| 2062 | +{ | |
| 2063 | + enum state s; | |
| 2064 | + const char *p; | |
| 2065 | + enum http_parser_url_fields uf, old_uf; | |
| 2066 | + int found_at = 0; | |
| 2067 | + | |
| 2068 | + u->port = u->field_set = 0; | |
| 2069 | + s = is_connect ? s_req_server_start : s_req_spaces_before_url; | |
| 2070 | + uf = old_uf = UF_MAX; | |
| 2071 | + | |
| 2072 | + for (p = buf; p < buf + buflen; p++) { | |
| 2073 | + s = parse_url_char(s, *p); | |
| 2074 | + | |
| 2075 | + /* Figure out the next field that we're operating on */ | |
| 2076 | + switch (s) { | |
| 2077 | + case s_dead: | |
| 2078 | + return 1; | |
| 2079 | + | |
| 2080 | + /* Skip delimeters */ | |
| 2081 | + case s_req_schema_slash: | |
| 2082 | + case s_req_schema_slash_slash: | |
| 2083 | + case s_req_server_start: | |
| 2084 | + case s_req_query_string_start: | |
| 2085 | + case s_req_fragment_start: | |
| 2086 | + continue; | |
| 2087 | + | |
| 2088 | + case s_req_schema: | |
| 2089 | + uf = UF_SCHEMA; | |
| 2090 | + break; | |
| 2091 | + | |
| 2092 | + case s_req_server_with_at: | |
| 2093 | + found_at = 1; | |
| 2094 | + | |
| 2095 | + /* FALLTROUGH */ | |
| 2096 | + case s_req_server: | |
| 2097 | + uf = UF_HOST; | |
| 2098 | + break; | |
| 2099 | + | |
| 2100 | + case s_req_path: | |
| 2101 | + uf = UF_PATH; | |
| 2102 | + break; | |
| 2103 | + | |
| 2104 | + case s_req_query_string: | |
| 2105 | + uf = UF_QUERY; | |
| 2106 | + break; | |
| 2107 | + | |
| 2108 | + case s_req_fragment: | |
| 2109 | + uf = UF_FRAGMENT; | |
| 2110 | + break; | |
| 2111 | + | |
| 2112 | + default: | |
| 2113 | + assert(!"Unexpected state"); | |
| 2114 | + return 1; | |
| 2115 | + } | |
| 2116 | + | |
| 2117 | + /* Nothing's changed; soldier on */ | |
| 2118 | + if (uf == old_uf) { | |
| 2119 | + u->field_data[uf].len++; | |
| 2120 | + continue; | |
| 2121 | + } | |
| 2122 | + | |
| 2123 | + u->field_data[uf].off = p - buf; | |
| 2124 | + u->field_data[uf].len = 1; | |
| 2125 | + | |
| 2126 | + u->field_set |= (1 << uf); | |
| 2127 | + old_uf = uf; | |
| 2128 | + } | |
| 2129 | + | |
| 2130 | + /* host must be present if there is a schema */ | |
| 2131 | + /* parsing http:///toto will fail */ | |
| 2132 | + if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { | |
| 2133 | + if (http_parse_host(buf, u, found_at) != 0) { | |
| 2134 | + return 1; | |
| 2135 | + } | |
| 2136 | + } | |
| 2137 | + | |
| 2138 | + /* CONNECT requests can only contain "hostname:port" */ | |
| 2139 | + if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { | |
| 2140 | + return 1; | |
| 2141 | + } | |
| 2142 | + | |
| 2143 | + if (u->field_set & (1 << UF_PORT)) { | |
| 2144 | + /* Don't bother with endp; we've already validated the string */ | |
| 2145 | + unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); | |
| 2146 | + | |
| 2147 | + /* Ports have a max value of 2^16 */ | |
| 2148 | + if (v > 0xffff) { | |
| 2149 | + return 1; | |
| 2150 | + } | |
| 2151 | + | |
| 2152 | + u->port = (uint16_t) v; | |
| 2153 | + } | |
| 2154 | + | |
| 2155 | + return 0; | |
| 2156 | +} | |
| 2157 | + | |
| 2158 | +void | |
| 2159 | +http_parser_pause(http_parser *parser, int paused) { | |
| 2160 | + /* Users should only be pausing/unpausing a parser that is not in an error | |
| 2161 | + * state. In non-debug builds, there's not much that we can do about this | |
| 2162 | + * other than ignore it. | |
| 2163 | + */ | |
| 2164 | + if (HTTP_PARSER_ERRNO(parser) == HPE_OK || | |
| 2165 | + HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { | |
| 2166 | + SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); | |
| 2167 | + } else { | |
| 2168 | + assert(0 && "Attempting to pause parser in error state"); | |
| 2169 | + } | |
| 2170 | +} | |
| 2171 | + | |
| 2172 | +int | |
| 2173 | +http_body_is_final(const struct http_parser *parser) { | |
| 2174 | + return parser->state == s_message_done; | |
| 2175 | +} | ... | ... |
3rdparty/http-parser-2.1/http_parser.gyp
0 → 100644
| 1 | +# This file is used with the GYP meta build system. | |
| 2 | +# http://code.google.com/p/gyp/ | |
| 3 | +# To build try this: | |
| 4 | +# svn co http://gyp.googlecode.com/svn/trunk gyp | |
| 5 | +# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp | |
| 6 | +# ./out/Debug/test | |
| 7 | +{ | |
| 8 | + 'target_defaults': { | |
| 9 | + 'default_configuration': 'Debug', | |
| 10 | + 'configurations': { | |
| 11 | + # TODO: hoist these out and put them somewhere common, because | |
| 12 | + # RuntimeLibrary MUST MATCH across the entire project | |
| 13 | + 'Debug': { | |
| 14 | + 'defines': [ 'DEBUG', '_DEBUG' ], | |
| 15 | + 'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ], | |
| 16 | + 'msvs_settings': { | |
| 17 | + 'VCCLCompilerTool': { | |
| 18 | + 'RuntimeLibrary': 1, # static debug | |
| 19 | + }, | |
| 20 | + }, | |
| 21 | + }, | |
| 22 | + 'Release': { | |
| 23 | + 'defines': [ 'NDEBUG' ], | |
| 24 | + 'cflags': [ '-Wall', '-Wextra', '-O3' ], | |
| 25 | + 'msvs_settings': { | |
| 26 | + 'VCCLCompilerTool': { | |
| 27 | + 'RuntimeLibrary': 0, # static release | |
| 28 | + }, | |
| 29 | + }, | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + 'msvs_settings': { | |
| 33 | + 'VCCLCompilerTool': { | |
| 34 | + }, | |
| 35 | + 'VCLibrarianTool': { | |
| 36 | + }, | |
| 37 | + 'VCLinkerTool': { | |
| 38 | + 'GenerateDebugInformation': 'true', | |
| 39 | + }, | |
| 40 | + }, | |
| 41 | + 'conditions': [ | |
| 42 | + ['OS == "win"', { | |
| 43 | + 'defines': [ | |
| 44 | + 'WIN32' | |
| 45 | + ], | |
| 46 | + }] | |
| 47 | + ], | |
| 48 | + }, | |
| 49 | + | |
| 50 | + 'targets': [ | |
| 51 | + { | |
| 52 | + 'target_name': 'http_parser', | |
| 53 | + 'type': 'static_library', | |
| 54 | + 'include_dirs': [ '.' ], | |
| 55 | + 'direct_dependent_settings': { | |
| 56 | + 'defines': [ 'HTTP_PARSER_STRICT=0' ], | |
| 57 | + 'include_dirs': [ '.' ], | |
| 58 | + }, | |
| 59 | + 'defines': [ 'HTTP_PARSER_STRICT=0' ], | |
| 60 | + 'sources': [ './http_parser.c', ], | |
| 61 | + 'conditions': [ | |
| 62 | + ['OS=="win"', { | |
| 63 | + 'msvs_settings': { | |
| 64 | + 'VCCLCompilerTool': { | |
| 65 | + # Compile as C++. http_parser.c is actually C99, but C++ is | |
| 66 | + # close enough in this case. | |
| 67 | + 'CompileAs': 2, | |
| 68 | + }, | |
| 69 | + }, | |
| 70 | + }] | |
| 71 | + ], | |
| 72 | + }, | |
| 73 | + | |
| 74 | + { | |
| 75 | + 'target_name': 'http_parser_strict', | |
| 76 | + 'type': 'static_library', | |
| 77 | + 'include_dirs': [ '.' ], | |
| 78 | + 'direct_dependent_settings': { | |
| 79 | + 'defines': [ 'HTTP_PARSER_STRICT=1' ], | |
| 80 | + 'include_dirs': [ '.' ], | |
| 81 | + }, | |
| 82 | + 'defines': [ 'HTTP_PARSER_STRICT=1' ], | |
| 83 | + 'sources': [ './http_parser.c', ], | |
| 84 | + 'conditions': [ | |
| 85 | + ['OS=="win"', { | |
| 86 | + 'msvs_settings': { | |
| 87 | + 'VCCLCompilerTool': { | |
| 88 | + # Compile as C++. http_parser.c is actually C99, but C++ is | |
| 89 | + # close enough in this case. | |
| 90 | + 'CompileAs': 2, | |
| 91 | + }, | |
| 92 | + }, | |
| 93 | + }] | |
| 94 | + ], | |
| 95 | + }, | |
| 96 | + | |
| 97 | + { | |
| 98 | + 'target_name': 'test-nonstrict', | |
| 99 | + 'type': 'executable', | |
| 100 | + 'dependencies': [ 'http_parser' ], | |
| 101 | + 'sources': [ 'test.c' ] | |
| 102 | + }, | |
| 103 | + | |
| 104 | + { | |
| 105 | + 'target_name': 'test-strict', | |
| 106 | + 'type': 'executable', | |
| 107 | + 'dependencies': [ 'http_parser_strict' ], | |
| 108 | + 'sources': [ 'test.c' ] | |
| 109 | + } | |
| 110 | + ] | |
| 111 | +} | ... | ... |
3rdparty/http-parser-2.1/http_parser.h
0 → 100644
| 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 | |
| 24 | +extern "C" { | |
| 25 | +#endif | |
| 26 | + | |
| 27 | +#define HTTP_PARSER_VERSION_MAJOR 2 | |
| 28 | +#define HTTP_PARSER_VERSION_MINOR 1 | |
| 29 | + | |
| 30 | +#include <sys/types.h> | |
| 31 | +#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) | |
| 32 | +#include <BaseTsd.h> | |
| 33 | +#include <stddef.h> | |
| 34 | +typedef __int8 int8_t; | |
| 35 | +typedef unsigned __int8 uint8_t; | |
| 36 | +typedef __int16 int16_t; | |
| 37 | +typedef unsigned __int16 uint16_t; | |
| 38 | +typedef __int32 int32_t; | |
| 39 | +typedef unsigned __int32 uint32_t; | |
| 40 | +typedef __int64 int64_t; | |
| 41 | +typedef unsigned __int64 uint64_t; | |
| 42 | +#else | |
| 43 | +#include <stdint.h> | |
| 44 | +#endif | |
| 45 | + | |
| 46 | +/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run | |
| 47 | + * faster | |
| 48 | + */ | |
| 49 | +#ifndef HTTP_PARSER_STRICT | |
| 50 | +# define HTTP_PARSER_STRICT 1 | |
| 51 | +#endif | |
| 52 | + | |
| 53 | +/* Maximium header size allowed */ | |
| 54 | +#define HTTP_MAX_HEADER_SIZE (80*1024) | |
| 55 | + | |
| 56 | + | |
| 57 | +typedef struct http_parser http_parser; | |
| 58 | +typedef struct http_parser_settings http_parser_settings; | |
| 59 | + | |
| 60 | + | |
| 61 | +/* Callbacks should return non-zero to indicate an error. The parser will | |
| 62 | + * then halt execution. | |
| 63 | + * | |
| 64 | + * The one exception is on_headers_complete. In a HTTP_RESPONSE parser | |
| 65 | + * returning '1' from on_headers_complete will tell the parser that it | |
| 66 | + * should not expect a body. This is used when receiving a response to a | |
| 67 | + * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: | |
| 68 | + * chunked' headers that indicate the presence of a body. | |
| 69 | + * | |
| 70 | + * http_data_cb does not return data chunks. It will be call arbitrarally | |
| 71 | + * many times for each string. E.G. you might get 10 callbacks for "on_url" | |
| 72 | + * each providing just a few characters more data. | |
| 73 | + */ | |
| 74 | +typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); | |
| 75 | +typedef int (*http_cb) (http_parser*); | |
| 76 | + | |
| 77 | + | |
| 78 | +/* Request Methods */ | |
| 79 | +#define HTTP_METHOD_MAP(XX) \ | |
| 80 | + XX(0, DELETE, DELETE) \ | |
| 81 | + XX(1, GET, GET) \ | |
| 82 | + XX(2, HEAD, HEAD) \ | |
| 83 | + XX(3, POST, POST) \ | |
| 84 | + XX(4, PUT, PUT) \ | |
| 85 | + /* pathological */ \ | |
| 86 | + XX(5, CONNECT, CONNECT) \ | |
| 87 | + XX(6, OPTIONS, OPTIONS) \ | |
| 88 | + XX(7, TRACE, TRACE) \ | |
| 89 | + /* webdav */ \ | |
| 90 | + XX(8, COPY, COPY) \ | |
| 91 | + XX(9, LOCK, LOCK) \ | |
| 92 | + XX(10, MKCOL, MKCOL) \ | |
| 93 | + XX(11, MOVE, MOVE) \ | |
| 94 | + XX(12, PROPFIND, PROPFIND) \ | |
| 95 | + XX(13, PROPPATCH, PROPPATCH) \ | |
| 96 | + XX(14, SEARCH, SEARCH) \ | |
| 97 | + XX(15, UNLOCK, UNLOCK) \ | |
| 98 | + /* subversion */ \ | |
| 99 | + XX(16, REPORT, REPORT) \ | |
| 100 | + XX(17, MKACTIVITY, MKACTIVITY) \ | |
| 101 | + XX(18, CHECKOUT, CHECKOUT) \ | |
| 102 | + XX(19, MERGE, MERGE) \ | |
| 103 | + /* upnp */ \ | |
| 104 | + XX(20, MSEARCH, M-SEARCH) \ | |
| 105 | + XX(21, NOTIFY, NOTIFY) \ | |
| 106 | + XX(22, SUBSCRIBE, SUBSCRIBE) \ | |
| 107 | + XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ | |
| 108 | + /* RFC-5789 */ \ | |
| 109 | + XX(24, PATCH, PATCH) \ | |
| 110 | + XX(25, PURGE, PURGE) \ | |
| 111 | + | |
| 112 | +enum http_method | |
| 113 | + { | |
| 114 | +#define XX(num, name, string) HTTP_##name = num, | |
| 115 | + HTTP_METHOD_MAP(XX) | |
| 116 | +#undef XX | |
| 117 | + }; | |
| 118 | + | |
| 119 | + | |
| 120 | +enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; | |
| 121 | + | |
| 122 | + | |
| 123 | +/* Flag values for http_parser.flags field */ | |
| 124 | +enum flags | |
| 125 | + { F_CHUNKED = 1 << 0 | |
| 126 | + , F_CONNECTION_KEEP_ALIVE = 1 << 1 | |
| 127 | + , F_CONNECTION_CLOSE = 1 << 2 | |
| 128 | + , F_TRAILING = 1 << 3 | |
| 129 | + , F_UPGRADE = 1 << 4 | |
| 130 | + , F_SKIPBODY = 1 << 5 | |
| 131 | + }; | |
| 132 | + | |
| 133 | + | |
| 134 | +/* Map for errno-related constants | |
| 135 | + * | |
| 136 | + * The provided argument should be a macro that takes 2 arguments. | |
| 137 | + */ | |
| 138 | +#define HTTP_ERRNO_MAP(XX) \ | |
| 139 | + /* No error */ \ | |
| 140 | + XX(OK, "success") \ | |
| 141 | + \ | |
| 142 | + /* Callback-related errors */ \ | |
| 143 | + XX(CB_message_begin, "the on_message_begin callback failed") \ | |
| 144 | + XX(CB_status_complete, "the on_status_complete callback failed") \ | |
| 145 | + XX(CB_url, "the on_url callback failed") \ | |
| 146 | + XX(CB_header_field, "the on_header_field callback failed") \ | |
| 147 | + XX(CB_header_value, "the on_header_value callback failed") \ | |
| 148 | + XX(CB_headers_complete, "the on_headers_complete callback failed") \ | |
| 149 | + XX(CB_body, "the on_body callback failed") \ | |
| 150 | + XX(CB_message_complete, "the on_message_complete callback failed") \ | |
| 151 | + \ | |
| 152 | + /* Parsing-related errors */ \ | |
| 153 | + XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ | |
| 154 | + XX(HEADER_OVERFLOW, \ | |
| 155 | + "too many header bytes seen; overflow detected") \ | |
| 156 | + XX(CLOSED_CONNECTION, \ | |
| 157 | + "data received after completed connection: close message") \ | |
| 158 | + XX(INVALID_VERSION, "invalid HTTP version") \ | |
| 159 | + XX(INVALID_STATUS, "invalid HTTP status code") \ | |
| 160 | + XX(INVALID_METHOD, "invalid HTTP method") \ | |
| 161 | + XX(INVALID_URL, "invalid URL") \ | |
| 162 | + XX(INVALID_HOST, "invalid host") \ | |
| 163 | + XX(INVALID_PORT, "invalid port") \ | |
| 164 | + XX(INVALID_PATH, "invalid path") \ | |
| 165 | + XX(INVALID_QUERY_STRING, "invalid query string") \ | |
| 166 | + XX(INVALID_FRAGMENT, "invalid fragment") \ | |
| 167 | + XX(LF_EXPECTED, "LF character expected") \ | |
| 168 | + XX(INVALID_HEADER_TOKEN, "invalid character in header") \ | |
| 169 | + XX(INVALID_CONTENT_LENGTH, \ | |
| 170 | + "invalid character in content-length header") \ | |
| 171 | + XX(INVALID_CHUNK_SIZE, \ | |
| 172 | + "invalid character in chunk size header") \ | |
| 173 | + XX(INVALID_CONSTANT, "invalid constant string") \ | |
| 174 | + XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ | |
| 175 | + XX(STRICT, "strict mode assertion failed") \ | |
| 176 | + XX(PAUSED, "parser is paused") \ | |
| 177 | + XX(UNKNOWN, "an unknown error occurred") | |
| 178 | + | |
| 179 | + | |
| 180 | +/* Define HPE_* values for each errno value above */ | |
| 181 | +#define HTTP_ERRNO_GEN(n, s) HPE_##n, | |
| 182 | +enum http_errno { | |
| 183 | + HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) | |
| 184 | +}; | |
| 185 | +#undef HTTP_ERRNO_GEN | |
| 186 | + | |
| 187 | + | |
| 188 | +/* Get an http_errno value from an http_parser */ | |
| 189 | +#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) | |
| 190 | + | |
| 191 | + | |
| 192 | +struct http_parser { | |
| 193 | + /** PRIVATE **/ | |
| 194 | + unsigned char type : 2; /* enum http_parser_type */ | |
| 195 | + unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ | |
| 196 | + unsigned char state; /* enum state from http_parser.c */ | |
| 197 | + unsigned char header_state; /* enum header_state from http_parser.c */ | |
| 198 | + unsigned char index; /* index into current matcher */ | |
| 199 | + | |
| 200 | + uint32_t nread; /* # bytes read in various scenarios */ | |
| 201 | + uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ | |
| 202 | + | |
| 203 | + /** READ-ONLY **/ | |
| 204 | + unsigned short http_major; | |
| 205 | + unsigned short http_minor; | |
| 206 | + unsigned short status_code; /* responses only */ | |
| 207 | + unsigned char method; /* requests only */ | |
| 208 | + unsigned char http_errno : 7; | |
| 209 | + | |
| 210 | + /* 1 = Upgrade header was present and the parser has exited because of that. | |
| 211 | + * 0 = No upgrade header present. | |
| 212 | + * Should be checked when http_parser_execute() returns in addition to | |
| 213 | + * error checking. | |
| 214 | + */ | |
| 215 | + unsigned char upgrade : 1; | |
| 216 | + | |
| 217 | + /** PUBLIC **/ | |
| 218 | + void *data; /* A pointer to get hook to the "connection" or "socket" object */ | |
| 219 | +}; | |
| 220 | + | |
| 221 | + | |
| 222 | +struct http_parser_settings { | |
| 223 | + http_cb on_message_begin; | |
| 224 | + http_data_cb on_url; | |
| 225 | + http_cb on_status_complete; | |
| 226 | + http_data_cb on_header_field; | |
| 227 | + http_data_cb on_header_value; | |
| 228 | + http_cb on_headers_complete; | |
| 229 | + http_data_cb on_body; | |
| 230 | + http_cb on_message_complete; | |
| 231 | +}; | |
| 232 | + | |
| 233 | + | |
| 234 | +enum http_parser_url_fields | |
| 235 | + { UF_SCHEMA = 0 | |
| 236 | + , UF_HOST = 1 | |
| 237 | + , UF_PORT = 2 | |
| 238 | + , UF_PATH = 3 | |
| 239 | + , UF_QUERY = 4 | |
| 240 | + , UF_FRAGMENT = 5 | |
| 241 | + , UF_USERINFO = 6 | |
| 242 | + , UF_MAX = 7 | |
| 243 | + }; | |
| 244 | + | |
| 245 | + | |
| 246 | +/* Result structure for http_parser_parse_url(). | |
| 247 | + * | |
| 248 | + * Callers should index into field_data[] with UF_* values iff field_set | |
| 249 | + * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and | |
| 250 | + * because we probably have padding left over), we convert any port to | |
| 251 | + * a uint16_t. | |
| 252 | + */ | |
| 253 | +struct http_parser_url { | |
| 254 | + uint16_t field_set; /* Bitmask of (1 << UF_*) values */ | |
| 255 | + uint16_t port; /* Converted UF_PORT string */ | |
| 256 | + | |
| 257 | + struct { | |
| 258 | + uint16_t off; /* Offset into buffer in which field starts */ | |
| 259 | + uint16_t len; /* Length of run in buffer */ | |
| 260 | + } field_data[UF_MAX]; | |
| 261 | +}; | |
| 262 | + | |
| 263 | + | |
| 264 | +void http_parser_init(http_parser *parser, enum http_parser_type type); | |
| 265 | + | |
| 266 | + | |
| 267 | +size_t http_parser_execute(http_parser *parser, | |
| 268 | + const http_parser_settings *settings, | |
| 269 | + const char *data, | |
| 270 | + size_t len); | |
| 271 | + | |
| 272 | + | |
| 273 | +/* If http_should_keep_alive() in the on_headers_complete or | |
| 274 | + * on_message_complete callback returns 0, then this should be | |
| 275 | + * the last message on the connection. | |
| 276 | + * If you are the server, respond with the "Connection: close" header. | |
| 277 | + * If you are the client, close the connection. | |
| 278 | + */ | |
| 279 | +int http_should_keep_alive(const http_parser *parser); | |
| 280 | + | |
| 281 | +/* Returns a string version of the HTTP method. */ | |
| 282 | +const char *http_method_str(enum http_method m); | |
| 283 | + | |
| 284 | +/* Return a string name of the given error */ | |
| 285 | +const char *http_errno_name(enum http_errno err); | |
| 286 | + | |
| 287 | +/* Return a string description of the given error */ | |
| 288 | +const char *http_errno_description(enum http_errno err); | |
| 289 | + | |
| 290 | +/* Parse a URL; return nonzero on failure */ | |
| 291 | +int http_parser_parse_url(const char *buf, size_t buflen, | |
| 292 | + int is_connect, | |
| 293 | + struct http_parser_url *u); | |
| 294 | + | |
| 295 | +/* Pause or un-pause the parser; a nonzero value pauses */ | |
| 296 | +void http_parser_pause(http_parser *parser, int paused); | |
| 297 | + | |
| 298 | +/* Checks if this is the final chunk of the body. */ | |
| 299 | +int http_body_is_final(const http_parser *parser); | |
| 300 | + | |
| 301 | +#ifdef __cplusplus | |
| 302 | +} | |
| 303 | +#endif | |
| 304 | +#endif | ... | ... |
3rdparty/http-parser-2.1/test.c
0 → 100644
| 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 | + | |
| 39 | +static http_parser *parser; | |
| 40 | + | |
| 41 | +struct 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 | + | |
| 73 | +static int currently_parsing_eof; | |
| 74 | + | |
| 75 | +static struct message messages[5]; | |
| 76 | +static int num_messages; | |
| 77 | +static http_parser_settings *current_pause_parser; | |
| 78 | + | |
| 79 | +/* * R E Q U E S T S * */ | |
| 80 | +const 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 | + ,.http_minor= 1 | |
| 651 | + ,.method= HTTP_GET | |
| 652 | + ,.query_string= "hail=all" | |
| 653 | + ,.fragment= "" | |
| 654 | + ,.request_path= "" | |
| 655 | + ,.request_url= "http://hypnotoad.org:1234?hail=all" | |
| 656 | + ,.host= "hypnotoad.org" | |
| 657 | + ,.port= 1234 | |
| 658 | + ,.num_headers= 0 | |
| 659 | + ,.headers= { } | |
| 660 | + ,.body= "" | |
| 661 | + } | |
| 662 | + | |
| 663 | +#define SPACE_TERMINATED_HOSTPORT 24 | |
| 664 | +, {.name= "host:port terminated by a space" | |
| 665 | + ,.type= HTTP_REQUEST | |
| 666 | + ,.raw= "GET http://hypnotoad.org:1234 HTTP/1.1\r\n" | |
| 667 | + "\r\n" | |
| 668 | + ,.should_keep_alive= TRUE | |
| 669 | + ,.message_complete_on_eof= FALSE | |
| 670 | + ,.http_major= 1 | |
| 671 | + ,.http_minor= 1 | |
| 672 | + ,.method= HTTP_GET | |
| 673 | + ,.query_string= "" | |
| 674 | + ,.fragment= "" | |
| 675 | + ,.request_path= "" | |
| 676 | + ,.request_url= "http://hypnotoad.org:1234" | |
| 677 | + ,.host= "hypnotoad.org" | |
| 678 | + ,.port= 1234 | |
| 679 | + ,.num_headers= 0 | |
| 680 | + ,.headers= { } | |
| 681 | + ,.body= "" | |
| 682 | + } | |
| 683 | + | |
| 684 | +#define PATCH_REQ 25 | |
| 685 | +, {.name = "PATCH request" | |
| 686 | + ,.type= HTTP_REQUEST | |
| 687 | + ,.raw= "PATCH /file.txt HTTP/1.1\r\n" | |
| 688 | + "Host: www.example.com\r\n" | |
| 689 | + "Content-Type: application/example\r\n" | |
| 690 | + "If-Match: \"e0023aa4e\"\r\n" | |
| 691 | + "Content-Length: 10\r\n" | |
| 692 | + "\r\n" | |
| 693 | + "cccccccccc" | |
| 694 | + ,.should_keep_alive= TRUE | |
| 695 | + ,.message_complete_on_eof= FALSE | |
| 696 | + ,.http_major= 1 | |
| 697 | + ,.http_minor= 1 | |
| 698 | + ,.method= HTTP_PATCH | |
| 699 | + ,.query_string= "" | |
| 700 | + ,.fragment= "" | |
| 701 | + ,.request_path= "/file.txt" | |
| 702 | + ,.request_url= "/file.txt" | |
| 703 | + ,.num_headers= 4 | |
| 704 | + ,.headers= { { "Host", "www.example.com" } | |
| 705 | + , { "Content-Type", "application/example" } | |
| 706 | + , { "If-Match", "\"e0023aa4e\"" } | |
| 707 | + , { "Content-Length", "10" } | |
| 708 | + } | |
| 709 | + ,.body= "cccccccccc" | |
| 710 | + } | |
| 711 | + | |
| 712 | +#define CONNECT_CAPS_REQUEST 26 | |
| 713 | +, {.name = "connect caps request" | |
| 714 | + ,.type= HTTP_REQUEST | |
| 715 | + ,.raw= "CONNECT HOME0.NETSCAPE.COM:443 HTTP/1.0\r\n" | |
| 716 | + "User-agent: Mozilla/1.1N\r\n" | |
| 717 | + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" | |
| 718 | + "\r\n" | |
| 719 | + ,.should_keep_alive= FALSE | |
| 720 | + ,.message_complete_on_eof= FALSE | |
| 721 | + ,.http_major= 1 | |
| 722 | + ,.http_minor= 0 | |
| 723 | + ,.method= HTTP_CONNECT | |
| 724 | + ,.query_string= "" | |
| 725 | + ,.fragment= "" | |
| 726 | + ,.request_path= "" | |
| 727 | + ,.request_url= "HOME0.NETSCAPE.COM:443" | |
| 728 | + ,.num_headers= 2 | |
| 729 | + ,.upgrade="" | |
| 730 | + ,.headers= { { "User-agent", "Mozilla/1.1N" } | |
| 731 | + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } | |
| 732 | + } | |
| 733 | + ,.body= "" | |
| 734 | + } | |
| 735 | + | |
| 736 | +#if !HTTP_PARSER_STRICT | |
| 737 | +#define UTF8_PATH_REQ 27 | |
| 738 | +, {.name= "utf-8 path request" | |
| 739 | + ,.type= HTTP_REQUEST | |
| 740 | + ,.raw= "GET /δ¶/δt/pope?q=1#narf HTTP/1.1\r\n" | |
| 741 | + "Host: github.com\r\n" | |
| 742 | + "\r\n" | |
| 743 | + ,.should_keep_alive= TRUE | |
| 744 | + ,.message_complete_on_eof= FALSE | |
| 745 | + ,.http_major= 1 | |
| 746 | + ,.http_minor= 1 | |
| 747 | + ,.method= HTTP_GET | |
| 748 | + ,.query_string= "q=1" | |
| 749 | + ,.fragment= "narf" | |
| 750 | + ,.request_path= "/δ¶/δt/pope" | |
| 751 | + ,.request_url= "/δ¶/δt/pope?q=1#narf" | |
| 752 | + ,.num_headers= 1 | |
| 753 | + ,.headers= { {"Host", "github.com" } | |
| 754 | + } | |
| 755 | + ,.body= "" | |
| 756 | + } | |
| 757 | + | |
| 758 | +#define HOSTNAME_UNDERSCORE 28 | |
| 759 | +, {.name = "hostname underscore" | |
| 760 | + ,.type= HTTP_REQUEST | |
| 761 | + ,.raw= "CONNECT home_0.netscape.com:443 HTTP/1.0\r\n" | |
| 762 | + "User-agent: Mozilla/1.1N\r\n" | |
| 763 | + "Proxy-authorization: basic aGVsbG86d29ybGQ=\r\n" | |
| 764 | + "\r\n" | |
| 765 | + ,.should_keep_alive= FALSE | |
| 766 | + ,.message_complete_on_eof= FALSE | |
| 767 | + ,.http_major= 1 | |
| 768 | + ,.http_minor= 0 | |
| 769 | + ,.method= HTTP_CONNECT | |
| 770 | + ,.query_string= "" | |
| 771 | + ,.fragment= "" | |
| 772 | + ,.request_path= "" | |
| 773 | + ,.request_url= "home_0.netscape.com:443" | |
| 774 | + ,.num_headers= 2 | |
| 775 | + ,.upgrade="" | |
| 776 | + ,.headers= { { "User-agent", "Mozilla/1.1N" } | |
| 777 | + , { "Proxy-authorization", "basic aGVsbG86d29ybGQ=" } | |
| 778 | + } | |
| 779 | + ,.body= "" | |
| 780 | + } | |
| 781 | +#endif /* !HTTP_PARSER_STRICT */ | |
| 782 | + | |
| 783 | +/* see https://github.com/ry/http-parser/issues/47 */ | |
| 784 | +#define EAT_TRAILING_CRLF_NO_CONNECTION_CLOSE 29 | |
| 785 | +, {.name = "eat CRLF between requests, no \"Connection: close\" header" | |
| 786 | + ,.raw= "POST / HTTP/1.1\r\n" | |
| 787 | + "Host: www.example.com\r\n" | |
| 788 | + "Content-Type: application/x-www-form-urlencoded\r\n" | |
| 789 | + "Content-Length: 4\r\n" | |
| 790 | + "\r\n" | |
| 791 | + "q=42\r\n" /* note the trailing CRLF */ | |
| 792 | + ,.should_keep_alive= TRUE | |
| 793 | + ,.message_complete_on_eof= FALSE | |
| 794 | + ,.http_major= 1 | |
| 795 | + ,.http_minor= 1 | |
| 796 | + ,.method= HTTP_POST | |
| 797 | + ,.query_string= "" | |
| 798 | + ,.fragment= "" | |
| 799 | + ,.request_path= "/" | |
| 800 | + ,.request_url= "/" | |
| 801 | + ,.num_headers= 3 | |
| 802 | + ,.upgrade= 0 | |
| 803 | + ,.headers= { { "Host", "www.example.com" } | |
| 804 | + , { "Content-Type", "application/x-www-form-urlencoded" } | |
| 805 | + , { "Content-Length", "4" } | |
| 806 | + } | |
| 807 | + ,.body= "q=42" | |
| 808 | + } | |
| 809 | + | |
| 810 | +/* see https://github.com/ry/http-parser/issues/47 */ | |
| 811 | +#define EAT_TRAILING_CRLF_WITH_CONNECTION_CLOSE 30 | |
| 812 | +, {.name = "eat CRLF between requests even if \"Connection: close\" is set" | |
| 813 | + ,.raw= "POST / HTTP/1.1\r\n" | |
| 814 | + "Host: www.example.com\r\n" | |
| 815 | + "Content-Type: application/x-www-form-urlencoded\r\n" | |
| 816 | + "Content-Length: 4\r\n" | |
| 817 | + "Connection: close\r\n" | |
| 818 | + "\r\n" | |
| 819 | + "q=42\r\n" /* note the trailing CRLF */ | |
| 820 | + ,.should_keep_alive= FALSE | |
| 821 | + ,.message_complete_on_eof= FALSE /* input buffer isn't empty when on_message_complete is called */ | |
| 822 | + ,.http_major= 1 | |
| 823 | + ,.http_minor= 1 | |
| 824 | + ,.method= HTTP_POST | |
| 825 | + ,.query_string= "" | |
| 826 | + ,.fragment= "" | |
| 827 | + ,.request_path= "/" | |
| 828 | + ,.request_url= "/" | |
| 829 | + ,.num_headers= 4 | |
| 830 | + ,.upgrade= 0 | |
| 831 | + ,.headers= { { "Host", "www.example.com" } | |
| 832 | + , { "Content-Type", "application/x-www-form-urlencoded" } | |
| 833 | + , { "Content-Length", "4" } | |
| 834 | + , { "Connection", "close" } | |
| 835 | + } | |
| 836 | + ,.body= "q=42" | |
| 837 | + } | |
| 838 | + | |
| 839 | +#define PURGE_REQ 31 | |
| 840 | +, {.name = "PURGE request" | |
| 841 | + ,.type= HTTP_REQUEST | |
| 842 | + ,.raw= "PURGE /file.txt HTTP/1.1\r\n" | |
| 843 | + "Host: www.example.com\r\n" | |
| 844 | + "\r\n" | |
| 845 | + ,.should_keep_alive= TRUE | |
| 846 | + ,.message_complete_on_eof= FALSE | |
| 847 | + ,.http_major= 1 | |
| 848 | + ,.http_minor= 1 | |
| 849 | + ,.method= HTTP_PURGE | |
| 850 | + ,.query_string= "" | |
| 851 | + ,.fragment= "" | |
| 852 | + ,.request_path= "/file.txt" | |
| 853 | + ,.request_url= "/file.txt" | |
| 854 | + ,.num_headers= 1 | |
| 855 | + ,.headers= { { "Host", "www.example.com" } } | |
| 856 | + ,.body= "" | |
| 857 | + } | |
| 858 | + | |
| 859 | +#define SEARCH_REQ 32 | |
| 860 | +, {.name = "SEARCH request" | |
| 861 | + ,.type= HTTP_REQUEST | |
| 862 | + ,.raw= "SEARCH / HTTP/1.1\r\n" | |
| 863 | + "Host: www.example.com\r\n" | |
| 864 | + "\r\n" | |
| 865 | + ,.should_keep_alive= TRUE | |
| 866 | + ,.message_complete_on_eof= FALSE | |
| 867 | + ,.http_major= 1 | |
| 868 | + ,.http_minor= 1 | |
| 869 | + ,.method= HTTP_SEARCH | |
| 870 | + ,.query_string= "" | |
| 871 | + ,.fragment= "" | |
| 872 | + ,.request_path= "/" | |
| 873 | + ,.request_url= "/" | |
| 874 | + ,.num_headers= 1 | |
| 875 | + ,.headers= { { "Host", "www.example.com" } } | |
| 876 | + ,.body= "" | |
| 877 | + } | |
| 878 | + | |
| 879 | +#define PROXY_WITH_BASIC_AUTH 33 | |
| 880 | +, {.name= "host:port and basic_auth" | |
| 881 | + ,.type= HTTP_REQUEST | |
| 882 | + ,.raw= "GET http://a%12:b!&*$@hypnotoad.org:1234/toto HTTP/1.1\r\n" | |
| 883 | + "\r\n" | |
| 884 | + ,.should_keep_alive= TRUE | |
| 885 | + ,.message_complete_on_eof= FALSE | |
| 886 | + ,.http_major= 1 | |
| 887 | + ,.http_minor= 1 | |
| 888 | + ,.method= HTTP_GET | |
| 889 | + ,.fragment= "" | |
| 890 | + ,.request_path= "/toto" | |
| 891 | + ,.request_url= "http://a%12:b!&*$@hypnotoad.org:1234/toto" | |
| 892 | + ,.host= "hypnotoad.org" | |
| 893 | + ,.userinfo= "a%12:b!&*$" | |
| 894 | + ,.port= 1234 | |
| 895 | + ,.num_headers= 0 | |
| 896 | + ,.headers= { } | |
| 897 | + ,.body= "" | |
| 898 | + } | |
| 899 | + | |
| 900 | + | |
| 901 | +, {.name= NULL } /* sentinel */ | |
| 902 | +}; | |
| 903 | + | |
| 904 | +/* * R E S P O N S E S * */ | |
| 905 | +const struct message responses[] = | |
| 906 | +#define GOOGLE_301 0 | |
| 907 | +{ {.name= "google 301" | |
| 908 | + ,.type= HTTP_RESPONSE | |
| 909 | + ,.raw= "HTTP/1.1 301 Moved Permanently\r\n" | |
| 910 | + "Location: http://www.google.com/\r\n" | |
| 911 | + "Content-Type: text/html; charset=UTF-8\r\n" | |
| 912 | + "Date: Sun, 26 Apr 2009 11:11:49 GMT\r\n" | |
| 913 | + "Expires: Tue, 26 May 2009 11:11:49 GMT\r\n" | |
| 914 | + "X-$PrototypeBI-Version: 1.6.0.3\r\n" /* $ char in header field */ | |
| 915 | + "Cache-Control: public, max-age=2592000\r\n" | |
| 916 | + "Server: gws\r\n" | |
| 917 | + "Content-Length: 219 \r\n" | |
| 918 | + "\r\n" | |
| 919 | + "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n" | |
| 920 | + "<TITLE>301 Moved</TITLE></HEAD><BODY>\n" | |
| 921 | + "<H1>301 Moved</H1>\n" | |
| 922 | + "The document has moved\n" | |
| 923 | + "<A HREF=\"http://www.google.com/\">here</A>.\r\n" | |
| 924 | + "</BODY></HTML>\r\n" | |
| 925 | + ,.should_keep_alive= TRUE | |
| 926 | + ,.message_complete_on_eof= FALSE | |
| 927 | + ,.http_major= 1 | |
| 928 | + ,.http_minor= 1 | |
| 929 | + ,.status_code= 301 | |
| 930 | + ,.num_headers= 8 | |
| 931 | + ,.headers= | |
| 932 | + { { "Location", "http://www.google.com/" } | |
| 933 | + , { "Content-Type", "text/html; charset=UTF-8" } | |
| 934 | + , { "Date", "Sun, 26 Apr 2009 11:11:49 GMT" } | |
| 935 | + , { "Expires", "Tue, 26 May 2009 11:11:49 GMT" } | |
| 936 | + , { "X-$PrototypeBI-Version", "1.6.0.3" } | |
| 937 | + , { "Cache-Control", "public, max-age=2592000" } | |
| 938 | + , { "Server", "gws" } | |
| 939 | + , { "Content-Length", "219 " } | |
| 940 | + } | |
| 941 | + ,.body= "<HTML><HEAD><meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">\n" | |
| 942 | + "<TITLE>301 Moved</TITLE></HEAD><BODY>\n" | |
| 943 | + "<H1>301 Moved</H1>\n" | |
| 944 | + "The document has moved\n" | |
| 945 | + "<A HREF=\"http://www.google.com/\">here</A>.\r\n" | |
| 946 | + "</BODY></HTML>\r\n" | |
| 947 | + } | |
| 948 | + | |
| 949 | +#define NO_CONTENT_LENGTH_RESPONSE 1 | |
| 950 | +/* The client should wait for the server's EOF. That is, when content-length | |
| 951 | + * is not specified, and "Connection: close", the end of body is specified | |
| 952 | + * by the EOF. | |
| 953 | + * Compare with APACHEBENCH_GET | |
| 954 | + */ | |
| 955 | +, {.name= "no content-length response" | |
| 956 | + ,.type= HTTP_RESPONSE | |
| 957 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 958 | + "Date: Tue, 04 Aug 2009 07:59:32 GMT\r\n" | |
| 959 | + "Server: Apache\r\n" | |
| 960 | + "X-Powered-By: Servlet/2.5 JSP/2.1\r\n" | |
| 961 | + "Content-Type: text/xml; charset=utf-8\r\n" | |
| 962 | + "Connection: close\r\n" | |
| 963 | + "\r\n" | |
| 964 | + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| 965 | + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" | |
| 966 | + " <SOAP-ENV:Body>\n" | |
| 967 | + " <SOAP-ENV:Fault>\n" | |
| 968 | + " <faultcode>SOAP-ENV:Client</faultcode>\n" | |
| 969 | + " <faultstring>Client Error</faultstring>\n" | |
| 970 | + " </SOAP-ENV:Fault>\n" | |
| 971 | + " </SOAP-ENV:Body>\n" | |
| 972 | + "</SOAP-ENV:Envelope>" | |
| 973 | + ,.should_keep_alive= FALSE | |
| 974 | + ,.message_complete_on_eof= TRUE | |
| 975 | + ,.http_major= 1 | |
| 976 | + ,.http_minor= 1 | |
| 977 | + ,.status_code= 200 | |
| 978 | + ,.num_headers= 5 | |
| 979 | + ,.headers= | |
| 980 | + { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } | |
| 981 | + , { "Server", "Apache" } | |
| 982 | + , { "X-Powered-By", "Servlet/2.5 JSP/2.1" } | |
| 983 | + , { "Content-Type", "text/xml; charset=utf-8" } | |
| 984 | + , { "Connection", "close" } | |
| 985 | + } | |
| 986 | + ,.body= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| 987 | + "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" | |
| 988 | + " <SOAP-ENV:Body>\n" | |
| 989 | + " <SOAP-ENV:Fault>\n" | |
| 990 | + " <faultcode>SOAP-ENV:Client</faultcode>\n" | |
| 991 | + " <faultstring>Client Error</faultstring>\n" | |
| 992 | + " </SOAP-ENV:Fault>\n" | |
| 993 | + " </SOAP-ENV:Body>\n" | |
| 994 | + "</SOAP-ENV:Envelope>" | |
| 995 | + } | |
| 996 | + | |
| 997 | +#define NO_HEADERS_NO_BODY_404 2 | |
| 998 | +, {.name= "404 no headers no body" | |
| 999 | + ,.type= HTTP_RESPONSE | |
| 1000 | + ,.raw= "HTTP/1.1 404 Not Found\r\n\r\n" | |
| 1001 | + ,.should_keep_alive= FALSE | |
| 1002 | + ,.message_complete_on_eof= TRUE | |
| 1003 | + ,.http_major= 1 | |
| 1004 | + ,.http_minor= 1 | |
| 1005 | + ,.status_code= 404 | |
| 1006 | + ,.num_headers= 0 | |
| 1007 | + ,.headers= {} | |
| 1008 | + ,.body_size= 0 | |
| 1009 | + ,.body= "" | |
| 1010 | + } | |
| 1011 | + | |
| 1012 | +#define NO_REASON_PHRASE 3 | |
| 1013 | +, {.name= "301 no response phrase" | |
| 1014 | + ,.type= HTTP_RESPONSE | |
| 1015 | + ,.raw= "HTTP/1.1 301\r\n\r\n" | |
| 1016 | + ,.should_keep_alive = FALSE | |
| 1017 | + ,.message_complete_on_eof= TRUE | |
| 1018 | + ,.http_major= 1 | |
| 1019 | + ,.http_minor= 1 | |
| 1020 | + ,.status_code= 301 | |
| 1021 | + ,.num_headers= 0 | |
| 1022 | + ,.headers= {} | |
| 1023 | + ,.body= "" | |
| 1024 | + } | |
| 1025 | + | |
| 1026 | +#define TRAILING_SPACE_ON_CHUNKED_BODY 4 | |
| 1027 | +, {.name="200 trailing space on chunked body" | |
| 1028 | + ,.type= HTTP_RESPONSE | |
| 1029 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1030 | + "Content-Type: text/plain\r\n" | |
| 1031 | + "Transfer-Encoding: chunked\r\n" | |
| 1032 | + "\r\n" | |
| 1033 | + "25 \r\n" | |
| 1034 | + "This is the data in the first chunk\r\n" | |
| 1035 | + "\r\n" | |
| 1036 | + "1C\r\n" | |
| 1037 | + "and this is the second one\r\n" | |
| 1038 | + "\r\n" | |
| 1039 | + "0 \r\n" | |
| 1040 | + "\r\n" | |
| 1041 | + ,.should_keep_alive= TRUE | |
| 1042 | + ,.message_complete_on_eof= FALSE | |
| 1043 | + ,.http_major= 1 | |
| 1044 | + ,.http_minor= 1 | |
| 1045 | + ,.status_code= 200 | |
| 1046 | + ,.num_headers= 2 | |
| 1047 | + ,.headers= | |
| 1048 | + { {"Content-Type", "text/plain" } | |
| 1049 | + , {"Transfer-Encoding", "chunked" } | |
| 1050 | + } | |
| 1051 | + ,.body_size = 37+28 | |
| 1052 | + ,.body = | |
| 1053 | + "This is the data in the first chunk\r\n" | |
| 1054 | + "and this is the second one\r\n" | |
| 1055 | + | |
| 1056 | + } | |
| 1057 | + | |
| 1058 | +#define NO_CARRIAGE_RET 5 | |
| 1059 | +, {.name="no carriage ret" | |
| 1060 | + ,.type= HTTP_RESPONSE | |
| 1061 | + ,.raw= "HTTP/1.1 200 OK\n" | |
| 1062 | + "Content-Type: text/html; charset=utf-8\n" | |
| 1063 | + "Connection: close\n" | |
| 1064 | + "\n" | |
| 1065 | + "these headers are from http://news.ycombinator.com/" | |
| 1066 | + ,.should_keep_alive= FALSE | |
| 1067 | + ,.message_complete_on_eof= TRUE | |
| 1068 | + ,.http_major= 1 | |
| 1069 | + ,.http_minor= 1 | |
| 1070 | + ,.status_code= 200 | |
| 1071 | + ,.num_headers= 2 | |
| 1072 | + ,.headers= | |
| 1073 | + { {"Content-Type", "text/html; charset=utf-8" } | |
| 1074 | + , {"Connection", "close" } | |
| 1075 | + } | |
| 1076 | + ,.body= "these headers are from http://news.ycombinator.com/" | |
| 1077 | + } | |
| 1078 | + | |
| 1079 | +#define PROXY_CONNECTION 6 | |
| 1080 | +, {.name="proxy connection" | |
| 1081 | + ,.type= HTTP_RESPONSE | |
| 1082 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1083 | + "Content-Type: text/html; charset=UTF-8\r\n" | |
| 1084 | + "Content-Length: 11\r\n" | |
| 1085 | + "Proxy-Connection: close\r\n" | |
| 1086 | + "Date: Thu, 31 Dec 2009 20:55:48 +0000\r\n" | |
| 1087 | + "\r\n" | |
| 1088 | + "hello world" | |
| 1089 | + ,.should_keep_alive= FALSE | |
| 1090 | + ,.message_complete_on_eof= FALSE | |
| 1091 | + ,.http_major= 1 | |
| 1092 | + ,.http_minor= 1 | |
| 1093 | + ,.status_code= 200 | |
| 1094 | + ,.num_headers= 4 | |
| 1095 | + ,.headers= | |
| 1096 | + { {"Content-Type", "text/html; charset=UTF-8" } | |
| 1097 | + , {"Content-Length", "11" } | |
| 1098 | + , {"Proxy-Connection", "close" } | |
| 1099 | + , {"Date", "Thu, 31 Dec 2009 20:55:48 +0000"} | |
| 1100 | + } | |
| 1101 | + ,.body= "hello world" | |
| 1102 | + } | |
| 1103 | + | |
| 1104 | +#define UNDERSTORE_HEADER_KEY 7 | |
| 1105 | + // shown by | |
| 1106 | + // curl -o /dev/null -v "http://ad.doubleclick.net/pfadx/DARTSHELLCONFIGXML;dcmt=text/xml;" | |
| 1107 | +, {.name="underscore header key" | |
| 1108 | + ,.type= HTTP_RESPONSE | |
| 1109 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1110 | + "Server: DCLK-AdSvr\r\n" | |
| 1111 | + "Content-Type: text/xml\r\n" | |
| 1112 | + "Content-Length: 0\r\n" | |
| 1113 | + "DCLK_imp: v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o\r\n\r\n" | |
| 1114 | + ,.should_keep_alive= TRUE | |
| 1115 | + ,.message_complete_on_eof= FALSE | |
| 1116 | + ,.http_major= 1 | |
| 1117 | + ,.http_minor= 1 | |
| 1118 | + ,.status_code= 200 | |
| 1119 | + ,.num_headers= 4 | |
| 1120 | + ,.headers= | |
| 1121 | + { {"Server", "DCLK-AdSvr" } | |
| 1122 | + , {"Content-Type", "text/xml" } | |
| 1123 | + , {"Content-Length", "0" } | |
| 1124 | + , {"DCLK_imp", "v7;x;114750856;0-0;0;17820020;0/0;21603567/21621457/1;;~okv=;dcmt=text/xml;;~cs=o" } | |
| 1125 | + } | |
| 1126 | + ,.body= "" | |
| 1127 | + } | |
| 1128 | + | |
| 1129 | +#define BONJOUR_MADAME_FR 8 | |
| 1130 | +/* The client should not merge two headers fields when the first one doesn't | |
| 1131 | + * have a value. | |
| 1132 | + */ | |
| 1133 | +, {.name= "bonjourmadame.fr" | |
| 1134 | + ,.type= HTTP_RESPONSE | |
| 1135 | + ,.raw= "HTTP/1.0 301 Moved Permanently\r\n" | |
| 1136 | + "Date: Thu, 03 Jun 2010 09:56:32 GMT\r\n" | |
| 1137 | + "Server: Apache/2.2.3 (Red Hat)\r\n" | |
| 1138 | + "Cache-Control: public\r\n" | |
| 1139 | + "Pragma: \r\n" | |
| 1140 | + "Location: http://www.bonjourmadame.fr/\r\n" | |
| 1141 | + "Vary: Accept-Encoding\r\n" | |
| 1142 | + "Content-Length: 0\r\n" | |
| 1143 | + "Content-Type: text/html; charset=UTF-8\r\n" | |
| 1144 | + "Connection: keep-alive\r\n" | |
| 1145 | + "\r\n" | |
| 1146 | + ,.should_keep_alive= TRUE | |
| 1147 | + ,.message_complete_on_eof= FALSE | |
| 1148 | + ,.http_major= 1 | |
| 1149 | + ,.http_minor= 0 | |
| 1150 | + ,.status_code= 301 | |
| 1151 | + ,.num_headers= 9 | |
| 1152 | + ,.headers= | |
| 1153 | + { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" } | |
| 1154 | + , { "Server", "Apache/2.2.3 (Red Hat)" } | |
| 1155 | + , { "Cache-Control", "public" } | |
| 1156 | + , { "Pragma", "" } | |
| 1157 | + , { "Location", "http://www.bonjourmadame.fr/" } | |
| 1158 | + , { "Vary", "Accept-Encoding" } | |
| 1159 | + , { "Content-Length", "0" } | |
| 1160 | + , { "Content-Type", "text/html; charset=UTF-8" } | |
| 1161 | + , { "Connection", "keep-alive" } | |
| 1162 | + } | |
| 1163 | + ,.body= "" | |
| 1164 | + } | |
| 1165 | + | |
| 1166 | +#define RES_FIELD_UNDERSCORE 9 | |
| 1167 | +/* Should handle spaces in header fields */ | |
| 1168 | +, {.name= "field underscore" | |
| 1169 | + ,.type= HTTP_RESPONSE | |
| 1170 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1171 | + "Date: Tue, 28 Sep 2010 01:14:13 GMT\r\n" | |
| 1172 | + "Server: Apache\r\n" | |
| 1173 | + "Cache-Control: no-cache, must-revalidate\r\n" | |
| 1174 | + "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n" | |
| 1175 | + ".et-Cookie: PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com\r\n" | |
| 1176 | + "Vary: Accept-Encoding\r\n" | |
| 1177 | + "_eep-Alive: timeout=45\r\n" /* semantic value ignored */ | |
| 1178 | + "_onnection: Keep-Alive\r\n" /* semantic value ignored */ | |
| 1179 | + "Transfer-Encoding: chunked\r\n" | |
| 1180 | + "Content-Type: text/html\r\n" | |
| 1181 | + "Connection: close\r\n" | |
| 1182 | + "\r\n" | |
| 1183 | + "0\r\n\r\n" | |
| 1184 | + ,.should_keep_alive= FALSE | |
| 1185 | + ,.message_complete_on_eof= FALSE | |
| 1186 | + ,.http_major= 1 | |
| 1187 | + ,.http_minor= 1 | |
| 1188 | + ,.status_code= 200 | |
| 1189 | + ,.num_headers= 11 | |
| 1190 | + ,.headers= | |
| 1191 | + { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" } | |
| 1192 | + , { "Server", "Apache" } | |
| 1193 | + , { "Cache-Control", "no-cache, must-revalidate" } | |
| 1194 | + , { "Expires", "Mon, 26 Jul 1997 05:00:00 GMT" } | |
| 1195 | + , { ".et-Cookie", "PlaxoCS=1274804622353690521; path=/; domain=.plaxo.com" } | |
| 1196 | + , { "Vary", "Accept-Encoding" } | |
| 1197 | + , { "_eep-Alive", "timeout=45" } | |
| 1198 | + , { "_onnection", "Keep-Alive" } | |
| 1199 | + , { "Transfer-Encoding", "chunked" } | |
| 1200 | + , { "Content-Type", "text/html" } | |
| 1201 | + , { "Connection", "close" } | |
| 1202 | + } | |
| 1203 | + ,.body= "" | |
| 1204 | + } | |
| 1205 | + | |
| 1206 | +#define NON_ASCII_IN_STATUS_LINE 10 | |
| 1207 | +/* Should handle non-ASCII in status line */ | |
| 1208 | +, {.name= "non-ASCII in status line" | |
| 1209 | + ,.type= HTTP_RESPONSE | |
| 1210 | + ,.raw= "HTTP/1.1 500 Oriëntatieprobleem\r\n" | |
| 1211 | + "Date: Fri, 5 Nov 2010 23:07:12 GMT+2\r\n" | |
| 1212 | + "Content-Length: 0\r\n" | |
| 1213 | + "Connection: close\r\n" | |
| 1214 | + "\r\n" | |
| 1215 | + ,.should_keep_alive= FALSE | |
| 1216 | + ,.message_complete_on_eof= FALSE | |
| 1217 | + ,.http_major= 1 | |
| 1218 | + ,.http_minor= 1 | |
| 1219 | + ,.status_code= 500 | |
| 1220 | + ,.num_headers= 3 | |
| 1221 | + ,.headers= | |
| 1222 | + { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" } | |
| 1223 | + , { "Content-Length", "0" } | |
| 1224 | + , { "Connection", "close" } | |
| 1225 | + } | |
| 1226 | + ,.body= "" | |
| 1227 | + } | |
| 1228 | + | |
| 1229 | +#define HTTP_VERSION_0_9 11 | |
| 1230 | +/* Should handle HTTP/0.9 */ | |
| 1231 | +, {.name= "http version 0.9" | |
| 1232 | + ,.type= HTTP_RESPONSE | |
| 1233 | + ,.raw= "HTTP/0.9 200 OK\r\n" | |
| 1234 | + "\r\n" | |
| 1235 | + ,.should_keep_alive= FALSE | |
| 1236 | + ,.message_complete_on_eof= TRUE | |
| 1237 | + ,.http_major= 0 | |
| 1238 | + ,.http_minor= 9 | |
| 1239 | + ,.status_code= 200 | |
| 1240 | + ,.num_headers= 0 | |
| 1241 | + ,.headers= | |
| 1242 | + {} | |
| 1243 | + ,.body= "" | |
| 1244 | + } | |
| 1245 | + | |
| 1246 | +#define NO_CONTENT_LENGTH_NO_TRANSFER_ENCODING_RESPONSE 12 | |
| 1247 | +/* The client should wait for the server's EOF. That is, when neither | |
| 1248 | + * content-length nor transfer-encoding is specified, the end of body | |
| 1249 | + * is specified by the EOF. | |
| 1250 | + */ | |
| 1251 | +, {.name= "neither content-length nor transfer-encoding response" | |
| 1252 | + ,.type= HTTP_RESPONSE | |
| 1253 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1254 | + "Content-Type: text/plain\r\n" | |
| 1255 | + "\r\n" | |
| 1256 | + "hello world" | |
| 1257 | + ,.should_keep_alive= FALSE | |
| 1258 | + ,.message_complete_on_eof= TRUE | |
| 1259 | + ,.http_major= 1 | |
| 1260 | + ,.http_minor= 1 | |
| 1261 | + ,.status_code= 200 | |
| 1262 | + ,.num_headers= 1 | |
| 1263 | + ,.headers= | |
| 1264 | + { { "Content-Type", "text/plain" } | |
| 1265 | + } | |
| 1266 | + ,.body= "hello world" | |
| 1267 | + } | |
| 1268 | + | |
| 1269 | +#define NO_BODY_HTTP10_KA_200 13 | |
| 1270 | +, {.name= "HTTP/1.0 with keep-alive and EOF-terminated 200 status" | |
| 1271 | + ,.type= HTTP_RESPONSE | |
| 1272 | + ,.raw= "HTTP/1.0 200 OK\r\n" | |
| 1273 | + "Connection: keep-alive\r\n" | |
| 1274 | + "\r\n" | |
| 1275 | + ,.should_keep_alive= FALSE | |
| 1276 | + ,.message_complete_on_eof= TRUE | |
| 1277 | + ,.http_major= 1 | |
| 1278 | + ,.http_minor= 0 | |
| 1279 | + ,.status_code= 200 | |
| 1280 | + ,.num_headers= 1 | |
| 1281 | + ,.headers= | |
| 1282 | + { { "Connection", "keep-alive" } | |
| 1283 | + } | |
| 1284 | + ,.body_size= 0 | |
| 1285 | + ,.body= "" | |
| 1286 | + } | |
| 1287 | + | |
| 1288 | +#define NO_BODY_HTTP10_KA_204 14 | |
| 1289 | +, {.name= "HTTP/1.0 with keep-alive and a 204 status" | |
| 1290 | + ,.type= HTTP_RESPONSE | |
| 1291 | + ,.raw= "HTTP/1.0 204 No content\r\n" | |
| 1292 | + "Connection: keep-alive\r\n" | |
| 1293 | + "\r\n" | |
| 1294 | + ,.should_keep_alive= TRUE | |
| 1295 | + ,.message_complete_on_eof= FALSE | |
| 1296 | + ,.http_major= 1 | |
| 1297 | + ,.http_minor= 0 | |
| 1298 | + ,.status_code= 204 | |
| 1299 | + ,.num_headers= 1 | |
| 1300 | + ,.headers= | |
| 1301 | + { { "Connection", "keep-alive" } | |
| 1302 | + } | |
| 1303 | + ,.body_size= 0 | |
| 1304 | + ,.body= "" | |
| 1305 | + } | |
| 1306 | + | |
| 1307 | +#define NO_BODY_HTTP11_KA_200 15 | |
| 1308 | +, {.name= "HTTP/1.1 with an EOF-terminated 200 status" | |
| 1309 | + ,.type= HTTP_RESPONSE | |
| 1310 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1311 | + "\r\n" | |
| 1312 | + ,.should_keep_alive= FALSE | |
| 1313 | + ,.message_complete_on_eof= TRUE | |
| 1314 | + ,.http_major= 1 | |
| 1315 | + ,.http_minor= 1 | |
| 1316 | + ,.status_code= 200 | |
| 1317 | + ,.num_headers= 0 | |
| 1318 | + ,.headers={} | |
| 1319 | + ,.body_size= 0 | |
| 1320 | + ,.body= "" | |
| 1321 | + } | |
| 1322 | + | |
| 1323 | +#define NO_BODY_HTTP11_KA_204 16 | |
| 1324 | +, {.name= "HTTP/1.1 with a 204 status" | |
| 1325 | + ,.type= HTTP_RESPONSE | |
| 1326 | + ,.raw= "HTTP/1.1 204 No content\r\n" | |
| 1327 | + "\r\n" | |
| 1328 | + ,.should_keep_alive= TRUE | |
| 1329 | + ,.message_complete_on_eof= FALSE | |
| 1330 | + ,.http_major= 1 | |
| 1331 | + ,.http_minor= 1 | |
| 1332 | + ,.status_code= 204 | |
| 1333 | + ,.num_headers= 0 | |
| 1334 | + ,.headers={} | |
| 1335 | + ,.body_size= 0 | |
| 1336 | + ,.body= "" | |
| 1337 | + } | |
| 1338 | + | |
| 1339 | +#define NO_BODY_HTTP11_NOKA_204 17 | |
| 1340 | +, {.name= "HTTP/1.1 with a 204 status and keep-alive disabled" | |
| 1341 | + ,.type= HTTP_RESPONSE | |
| 1342 | + ,.raw= "HTTP/1.1 204 No content\r\n" | |
| 1343 | + "Connection: close\r\n" | |
| 1344 | + "\r\n" | |
| 1345 | + ,.should_keep_alive= FALSE | |
| 1346 | + ,.message_complete_on_eof= FALSE | |
| 1347 | + ,.http_major= 1 | |
| 1348 | + ,.http_minor= 1 | |
| 1349 | + ,.status_code= 204 | |
| 1350 | + ,.num_headers= 1 | |
| 1351 | + ,.headers= | |
| 1352 | + { { "Connection", "close" } | |
| 1353 | + } | |
| 1354 | + ,.body_size= 0 | |
| 1355 | + ,.body= "" | |
| 1356 | + } | |
| 1357 | + | |
| 1358 | +#define NO_BODY_HTTP11_KA_CHUNKED_200 18 | |
| 1359 | +, {.name= "HTTP/1.1 with chunked endocing and a 200 response" | |
| 1360 | + ,.type= HTTP_RESPONSE | |
| 1361 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1362 | + "Transfer-Encoding: chunked\r\n" | |
| 1363 | + "\r\n" | |
| 1364 | + "0\r\n" | |
| 1365 | + "\r\n" | |
| 1366 | + ,.should_keep_alive= TRUE | |
| 1367 | + ,.message_complete_on_eof= FALSE | |
| 1368 | + ,.http_major= 1 | |
| 1369 | + ,.http_minor= 1 | |
| 1370 | + ,.status_code= 200 | |
| 1371 | + ,.num_headers= 1 | |
| 1372 | + ,.headers= | |
| 1373 | + { { "Transfer-Encoding", "chunked" } | |
| 1374 | + } | |
| 1375 | + ,.body_size= 0 | |
| 1376 | + ,.body= "" | |
| 1377 | + } | |
| 1378 | + | |
| 1379 | +#if !HTTP_PARSER_STRICT | |
| 1380 | +#define SPACE_IN_FIELD_RES 19 | |
| 1381 | +/* Should handle spaces in header fields */ | |
| 1382 | +, {.name= "field space" | |
| 1383 | + ,.type= HTTP_RESPONSE | |
| 1384 | + ,.raw= "HTTP/1.1 200 OK\r\n" | |
| 1385 | + "Server: Microsoft-IIS/6.0\r\n" | |
| 1386 | + "X-Powered-By: ASP.NET\r\n" | |
| 1387 | + "en-US Content-Type: text/xml\r\n" /* this is the problem */ | |
| 1388 | + "Content-Type: text/xml\r\n" | |
| 1389 | + "Content-Length: 16\r\n" | |
| 1390 | + "Date: Fri, 23 Jul 2010 18:45:38 GMT\r\n" | |
| 1391 | + "Connection: keep-alive\r\n" | |
| 1392 | + "\r\n" | |
| 1393 | + "<xml>hello</xml>" /* fake body */ | |
| 1394 | + ,.should_keep_alive= TRUE | |
| 1395 | + ,.message_complete_on_eof= FALSE | |
| 1396 | + ,.http_major= 1 | |
| 1397 | + ,.http_minor= 1 | |
| 1398 | + ,.status_code= 200 | |
| 1399 | + ,.num_headers= 7 | |
| 1400 | + ,.headers= | |
| 1401 | + { { "Server", "Microsoft-IIS/6.0" } | |
| 1402 | + , { "X-Powered-By", "ASP.NET" } | |
| 1403 | + , { "en-US Content-Type", "text/xml" } | |
| 1404 | + , { "Content-Type", "text/xml" } | |
| 1405 | + , { "Content-Length", "16" } | |
| 1406 | + , { "Date", "Fri, 23 Jul 2010 18:45:38 GMT" } | |
| 1407 | + , { "Connection", "keep-alive" } | |
| 1408 | + } | |
| 1409 | + ,.body= "<xml>hello</xml>" | |
| 1410 | + } | |
| 1411 | +#endif /* !HTTP_PARSER_STRICT */ | |
| 1412 | + | |
| 1413 | +, {.name= NULL } /* sentinel */ | |
| 1414 | +}; | |
| 1415 | + | |
| 1416 | +/* strnlen() is a POSIX.2008 addition. Can't rely on it being available so | |
| 1417 | + * define it ourselves. | |
| 1418 | + */ | |
| 1419 | +size_t | |
| 1420 | +strnlen(const char *s, size_t maxlen) | |
| 1421 | +{ | |
| 1422 | + const char *p; | |
| 1423 | + | |
| 1424 | + p = memchr(s, '\0', maxlen); | |
| 1425 | + if (p == NULL) | |
| 1426 | + return maxlen; | |
| 1427 | + | |
| 1428 | + return p - s; | |
| 1429 | +} | |
| 1430 | + | |
| 1431 | +size_t | |
| 1432 | +strlncat(char *dst, size_t len, const char *src, size_t n) | |
| 1433 | +{ | |
| 1434 | + size_t slen; | |
| 1435 | + size_t dlen; | |
| 1436 | + size_t rlen; | |
| 1437 | + size_t ncpy; | |
| 1438 | + | |
| 1439 | + slen = strnlen(src, n); | |
| 1440 | + dlen = strnlen(dst, len); | |
| 1441 | + | |
| 1442 | + if (dlen < len) { | |
| 1443 | + rlen = len - dlen; | |
| 1444 | + ncpy = slen < rlen ? slen : (rlen - 1); | |
| 1445 | + memcpy(dst + dlen, src, ncpy); | |
| 1446 | + dst[dlen + ncpy] = '\0'; | |
| 1447 | + } | |
| 1448 | + | |
| 1449 | + assert(len > slen + dlen); | |
| 1450 | + return slen + dlen; | |
| 1451 | +} | |
| 1452 | + | |
| 1453 | +size_t | |
| 1454 | +strlcat(char *dst, const char *src, size_t len) | |
| 1455 | +{ | |
| 1456 | + return strlncat(dst, len, src, (size_t) -1); | |
| 1457 | +} | |
| 1458 | + | |
| 1459 | +size_t | |
| 1460 | +strlncpy(char *dst, size_t len, const char *src, size_t n) | |
| 1461 | +{ | |
| 1462 | + size_t slen; | |
| 1463 | + size_t ncpy; | |
| 1464 | + | |
| 1465 | + slen = strnlen(src, n); | |
| 1466 | + | |
| 1467 | + if (len > 0) { | |
| 1468 | + ncpy = slen < len ? slen : (len - 1); | |
| 1469 | + memcpy(dst, src, ncpy); | |
| 1470 | + dst[ncpy] = '\0'; | |
| 1471 | + } | |
| 1472 | + | |
| 1473 | + assert(len > slen); | |
| 1474 | + return slen; | |
| 1475 | +} | |
| 1476 | + | |
| 1477 | +size_t | |
| 1478 | +strlcpy(char *dst, const char *src, size_t len) | |
| 1479 | +{ | |
| 1480 | + return strlncpy(dst, len, src, (size_t) -1); | |
| 1481 | +} | |
| 1482 | + | |
| 1483 | +int | |
| 1484 | +request_url_cb (http_parser *p, const char *buf, size_t len) | |
| 1485 | +{ | |
| 1486 | + assert(p == parser); | |
| 1487 | + strlncat(messages[num_messages].request_url, | |
| 1488 | + sizeof(messages[num_messages].request_url), | |
| 1489 | + buf, | |
| 1490 | + len); | |
| 1491 | + return 0; | |
| 1492 | +} | |
| 1493 | + | |
| 1494 | +int | |
| 1495 | +status_complete_cb (http_parser *p) { | |
| 1496 | + assert(p == parser); | |
| 1497 | + p->data++; | |
| 1498 | + return 0; | |
| 1499 | +} | |
| 1500 | + | |
| 1501 | +int | |
| 1502 | +header_field_cb (http_parser *p, const char *buf, size_t len) | |
| 1503 | +{ | |
| 1504 | + assert(p == parser); | |
| 1505 | + struct message *m = &messages[num_messages]; | |
| 1506 | + | |
| 1507 | + if (m->last_header_element != FIELD) | |
| 1508 | + m->num_headers++; | |
| 1509 | + | |
| 1510 | + strlncat(m->headers[m->num_headers-1][0], | |
| 1511 | + sizeof(m->headers[m->num_headers-1][0]), | |
| 1512 | + buf, | |
| 1513 | + len); | |
| 1514 | + | |
| 1515 | + m->last_header_element = FIELD; | |
| 1516 | + | |
| 1517 | + return 0; | |
| 1518 | +} | |
| 1519 | + | |
| 1520 | +int | |
| 1521 | +header_value_cb (http_parser *p, const char *buf, size_t len) | |
| 1522 | +{ | |
| 1523 | + assert(p == parser); | |
| 1524 | + struct message *m = &messages[num_messages]; | |
| 1525 | + | |
| 1526 | + strlncat(m->headers[m->num_headers-1][1], | |
| 1527 | + sizeof(m->headers[m->num_headers-1][1]), | |
| 1528 | + buf, | |
| 1529 | + len); | |
| 1530 | + | |
| 1531 | + m->last_header_element = VALUE; | |
| 1532 | + | |
| 1533 | + return 0; | |
| 1534 | +} | |
| 1535 | + | |
| 1536 | +void | |
| 1537 | +check_body_is_final (const http_parser *p) | |
| 1538 | +{ | |
| 1539 | + if (messages[num_messages].body_is_final) { | |
| 1540 | + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " | |
| 1541 | + "on last on_body callback call " | |
| 1542 | + "but it doesn't! ***\n\n"); | |
| 1543 | + assert(0); | |
| 1544 | + abort(); | |
| 1545 | + } | |
| 1546 | + messages[num_messages].body_is_final = http_body_is_final(p); | |
| 1547 | +} | |
| 1548 | + | |
| 1549 | +int | |
| 1550 | +body_cb (http_parser *p, const char *buf, size_t len) | |
| 1551 | +{ | |
| 1552 | + assert(p == parser); | |
| 1553 | + strlncat(messages[num_messages].body, | |
| 1554 | + sizeof(messages[num_messages].body), | |
| 1555 | + buf, | |
| 1556 | + len); | |
| 1557 | + messages[num_messages].body_size += len; | |
| 1558 | + check_body_is_final(p); | |
| 1559 | + // printf("body_cb: '%s'\n", requests[num_messages].body); | |
| 1560 | + return 0; | |
| 1561 | +} | |
| 1562 | + | |
| 1563 | +int | |
| 1564 | +count_body_cb (http_parser *p, const char *buf, size_t len) | |
| 1565 | +{ | |
| 1566 | + assert(p == parser); | |
| 1567 | + assert(buf); | |
| 1568 | + messages[num_messages].body_size += len; | |
| 1569 | + check_body_is_final(p); | |
| 1570 | + return 0; | |
| 1571 | +} | |
| 1572 | + | |
| 1573 | +int | |
| 1574 | +message_begin_cb (http_parser *p) | |
| 1575 | +{ | |
| 1576 | + assert(p == parser); | |
| 1577 | + messages[num_messages].message_begin_cb_called = TRUE; | |
| 1578 | + return 0; | |
| 1579 | +} | |
| 1580 | + | |
| 1581 | +int | |
| 1582 | +headers_complete_cb (http_parser *p) | |
| 1583 | +{ | |
| 1584 | + assert(p == parser); | |
| 1585 | + messages[num_messages].method = parser->method; | |
| 1586 | + messages[num_messages].status_code = parser->status_code; | |
| 1587 | + messages[num_messages].http_major = parser->http_major; | |
| 1588 | + messages[num_messages].http_minor = parser->http_minor; | |
| 1589 | + messages[num_messages].headers_complete_cb_called = TRUE; | |
| 1590 | + messages[num_messages].should_keep_alive = http_should_keep_alive(parser); | |
| 1591 | + return 0; | |
| 1592 | +} | |
| 1593 | + | |
| 1594 | +int | |
| 1595 | +message_complete_cb (http_parser *p) | |
| 1596 | +{ | |
| 1597 | + assert(p == parser); | |
| 1598 | + if (messages[num_messages].should_keep_alive != http_should_keep_alive(parser)) | |
| 1599 | + { | |
| 1600 | + fprintf(stderr, "\n\n *** Error http_should_keep_alive() should have same " | |
| 1601 | + "value in both on_message_complete and on_headers_complete " | |
| 1602 | + "but it doesn't! ***\n\n"); | |
| 1603 | + assert(0); | |
| 1604 | + abort(); | |
| 1605 | + } | |
| 1606 | + | |
| 1607 | + if (messages[num_messages].body_size && | |
| 1608 | + http_body_is_final(p) && | |
| 1609 | + !messages[num_messages].body_is_final) | |
| 1610 | + { | |
| 1611 | + fprintf(stderr, "\n\n *** Error http_body_is_final() should return 1 " | |
| 1612 | + "on last on_body callback call " | |
| 1613 | + "but it doesn't! ***\n\n"); | |
| 1614 | + assert(0); | |
| 1615 | + abort(); | |
| 1616 | + } | |
| 1617 | + | |
| 1618 | + messages[num_messages].message_complete_cb_called = TRUE; | |
| 1619 | + | |
| 1620 | + messages[num_messages].message_complete_on_eof = currently_parsing_eof; | |
| 1621 | + | |
| 1622 | + num_messages++; | |
| 1623 | + return 0; | |
| 1624 | +} | |
| 1625 | + | |
| 1626 | +/* These dontcall_* callbacks exist so that we can verify that when we're | |
| 1627 | + * paused, no additional callbacks are invoked */ | |
| 1628 | +int | |
| 1629 | +dontcall_message_begin_cb (http_parser *p) | |
| 1630 | +{ | |
| 1631 | + if (p) { } // gcc | |
| 1632 | + fprintf(stderr, "\n\n*** on_message_begin() called on paused parser ***\n\n"); | |
| 1633 | + abort(); | |
| 1634 | +} | |
| 1635 | + | |
| 1636 | +int | |
| 1637 | +dontcall_header_field_cb (http_parser *p, const char *buf, size_t len) | |
| 1638 | +{ | |
| 1639 | + if (p || buf || len) { } // gcc | |
| 1640 | + fprintf(stderr, "\n\n*** on_header_field() called on paused parser ***\n\n"); | |
| 1641 | + abort(); | |
| 1642 | +} | |
| 1643 | + | |
| 1644 | +int | |
| 1645 | +dontcall_header_value_cb (http_parser *p, const char *buf, size_t len) | |
| 1646 | +{ | |
| 1647 | + if (p || buf || len) { } // gcc | |
| 1648 | + fprintf(stderr, "\n\n*** on_header_value() called on paused parser ***\n\n"); | |
| 1649 | + abort(); | |
| 1650 | +} | |
| 1651 | + | |
| 1652 | +int | |
| 1653 | +dontcall_request_url_cb (http_parser *p, const char *buf, size_t len) | |
| 1654 | +{ | |
| 1655 | + if (p || buf || len) { } // gcc | |
| 1656 | + fprintf(stderr, "\n\n*** on_request_url() called on paused parser ***\n\n"); | |
| 1657 | + abort(); | |
| 1658 | +} | |
| 1659 | + | |
| 1660 | +int | |
| 1661 | +dontcall_body_cb (http_parser *p, const char *buf, size_t len) | |
| 1662 | +{ | |
| 1663 | + if (p || buf || len) { } // gcc | |
| 1664 | + fprintf(stderr, "\n\n*** on_body_cb() called on paused parser ***\n\n"); | |
| 1665 | + abort(); | |
| 1666 | +} | |
| 1667 | + | |
| 1668 | +int | |
| 1669 | +dontcall_headers_complete_cb (http_parser *p) | |
| 1670 | +{ | |
| 1671 | + if (p) { } // gcc | |
| 1672 | + fprintf(stderr, "\n\n*** on_headers_complete() called on paused " | |
| 1673 | + "parser ***\n\n"); | |
| 1674 | + abort(); | |
| 1675 | +} | |
| 1676 | + | |
| 1677 | +int | |
| 1678 | +dontcall_message_complete_cb (http_parser *p) | |
| 1679 | +{ | |
| 1680 | + if (p) { } // gcc | |
| 1681 | + fprintf(stderr, "\n\n*** on_message_complete() called on paused " | |
| 1682 | + "parser ***\n\n"); | |
| 1683 | + abort(); | |
| 1684 | +} | |
| 1685 | + | |
| 1686 | +static http_parser_settings settings_dontcall = | |
| 1687 | + {.on_message_begin = dontcall_message_begin_cb | |
| 1688 | + ,.on_header_field = dontcall_header_field_cb | |
| 1689 | + ,.on_header_value = dontcall_header_value_cb | |
| 1690 | + ,.on_url = dontcall_request_url_cb | |
| 1691 | + ,.on_body = dontcall_body_cb | |
| 1692 | + ,.on_headers_complete = dontcall_headers_complete_cb | |
| 1693 | + ,.on_message_complete = dontcall_message_complete_cb | |
| 1694 | + }; | |
| 1695 | + | |
| 1696 | +/* These pause_* callbacks always pause the parser and just invoke the regular | |
| 1697 | + * callback that tracks content. Before returning, we overwrite the parser | |
| 1698 | + * settings to point to the _dontcall variety so that we can verify that | |
| 1699 | + * the pause actually did, you know, pause. */ | |
| 1700 | +int | |
| 1701 | +pause_message_begin_cb (http_parser *p) | |
| 1702 | +{ | |
| 1703 | + http_parser_pause(p, 1); | |
| 1704 | + *current_pause_parser = settings_dontcall; | |
| 1705 | + return message_begin_cb(p); | |
| 1706 | +} | |
| 1707 | + | |
| 1708 | +int | |
| 1709 | +pause_header_field_cb (http_parser *p, const char *buf, size_t len) | |
| 1710 | +{ | |
| 1711 | + http_parser_pause(p, 1); | |
| 1712 | + *current_pause_parser = settings_dontcall; | |
| 1713 | + return header_field_cb(p, buf, len); | |
| 1714 | +} | |
| 1715 | + | |
| 1716 | +int | |
| 1717 | +pause_header_value_cb (http_parser *p, const char *buf, size_t len) | |
| 1718 | +{ | |
| 1719 | + http_parser_pause(p, 1); | |
| 1720 | + *current_pause_parser = settings_dontcall; | |
| 1721 | + return header_value_cb(p, buf, len); | |
| 1722 | +} | |
| 1723 | + | |
| 1724 | +int | |
| 1725 | +pause_request_url_cb (http_parser *p, const char *buf, size_t len) | |
| 1726 | +{ | |
| 1727 | + http_parser_pause(p, 1); | |
| 1728 | + *current_pause_parser = settings_dontcall; | |
| 1729 | + return request_url_cb(p, buf, len); | |
| 1730 | +} | |
| 1731 | + | |
| 1732 | +int | |
| 1733 | +pause_body_cb (http_parser *p, const char *buf, size_t len) | |
| 1734 | +{ | |
| 1735 | + http_parser_pause(p, 1); | |
| 1736 | + *current_pause_parser = settings_dontcall; | |
| 1737 | + return body_cb(p, buf, len); | |
| 1738 | +} | |
| 1739 | + | |
| 1740 | +int | |
| 1741 | +pause_headers_complete_cb (http_parser *p) | |
| 1742 | +{ | |
| 1743 | + http_parser_pause(p, 1); | |
| 1744 | + *current_pause_parser = settings_dontcall; | |
| 1745 | + return headers_complete_cb(p); | |
| 1746 | +} | |
| 1747 | + | |
| 1748 | +int | |
| 1749 | +pause_message_complete_cb (http_parser *p) | |
| 1750 | +{ | |
| 1751 | + http_parser_pause(p, 1); | |
| 1752 | + *current_pause_parser = settings_dontcall; | |
| 1753 | + return message_complete_cb(p); | |
| 1754 | +} | |
| 1755 | + | |
| 1756 | +static http_parser_settings settings_pause = | |
| 1757 | + {.on_message_begin = pause_message_begin_cb | |
| 1758 | + ,.on_header_field = pause_header_field_cb | |
| 1759 | + ,.on_header_value = pause_header_value_cb | |
| 1760 | + ,.on_url = pause_request_url_cb | |
| 1761 | + ,.on_body = pause_body_cb | |
| 1762 | + ,.on_headers_complete = pause_headers_complete_cb | |
| 1763 | + ,.on_message_complete = pause_message_complete_cb | |
| 1764 | + }; | |
| 1765 | + | |
| 1766 | +static http_parser_settings settings = | |
| 1767 | + {.on_message_begin = message_begin_cb | |
| 1768 | + ,.on_header_field = header_field_cb | |
| 1769 | + ,.on_header_value = header_value_cb | |
| 1770 | + ,.on_url = request_url_cb | |
| 1771 | + ,.on_body = body_cb | |
| 1772 | + ,.on_headers_complete = headers_complete_cb | |
| 1773 | + ,.on_message_complete = message_complete_cb | |
| 1774 | + }; | |
| 1775 | + | |
| 1776 | +static http_parser_settings settings_count_body = | |
| 1777 | + {.on_message_begin = message_begin_cb | |
| 1778 | + ,.on_header_field = header_field_cb | |
| 1779 | + ,.on_header_value = header_value_cb | |
| 1780 | + ,.on_url = request_url_cb | |
| 1781 | + ,.on_body = count_body_cb | |
| 1782 | + ,.on_headers_complete = headers_complete_cb | |
| 1783 | + ,.on_message_complete = message_complete_cb | |
| 1784 | + }; | |
| 1785 | + | |
| 1786 | +static http_parser_settings settings_null = | |
| 1787 | + {.on_message_begin = 0 | |
| 1788 | + ,.on_header_field = 0 | |
| 1789 | + ,.on_header_value = 0 | |
| 1790 | + ,.on_url = 0 | |
| 1791 | + ,.on_body = 0 | |
| 1792 | + ,.on_headers_complete = 0 | |
| 1793 | + ,.on_message_complete = 0 | |
| 1794 | + }; | |
| 1795 | + | |
| 1796 | +void | |
| 1797 | +parser_init (enum http_parser_type type) | |
| 1798 | +{ | |
| 1799 | + num_messages = 0; | |
| 1800 | + | |
| 1801 | + assert(parser == NULL); | |
| 1802 | + | |
| 1803 | + parser = malloc(sizeof(http_parser)); | |
| 1804 | + | |
| 1805 | + http_parser_init(parser, type); | |
| 1806 | + | |
| 1807 | + memset(&messages, 0, sizeof messages); | |
| 1808 | + | |
| 1809 | +} | |
| 1810 | + | |
| 1811 | +void | |
| 1812 | +parser_free () | |
| 1813 | +{ | |
| 1814 | + assert(parser); | |
| 1815 | + free(parser); | |
| 1816 | + parser = NULL; | |
| 1817 | +} | |
| 1818 | + | |
| 1819 | +size_t parse (const char *buf, size_t len) | |
| 1820 | +{ | |
| 1821 | + size_t nparsed; | |
| 1822 | + currently_parsing_eof = (len == 0); | |
| 1823 | + nparsed = http_parser_execute(parser, &settings, buf, len); | |
| 1824 | + return nparsed; | |
| 1825 | +} | |
| 1826 | + | |
| 1827 | +size_t parse_count_body (const char *buf, size_t len) | |
| 1828 | +{ | |
| 1829 | + size_t nparsed; | |
| 1830 | + currently_parsing_eof = (len == 0); | |
| 1831 | + nparsed = http_parser_execute(parser, &settings_count_body, buf, len); | |
| 1832 | + return nparsed; | |
| 1833 | +} | |
| 1834 | + | |
| 1835 | +size_t parse_pause (const char *buf, size_t len) | |
| 1836 | +{ | |
| 1837 | + size_t nparsed; | |
| 1838 | + http_parser_settings s = settings_pause; | |
| 1839 | + | |
| 1840 | + currently_parsing_eof = (len == 0); | |
| 1841 | + current_pause_parser = &s; | |
| 1842 | + nparsed = http_parser_execute(parser, current_pause_parser, buf, len); | |
| 1843 | + return nparsed; | |
| 1844 | +} | |
| 1845 | + | |
| 1846 | +static inline int | |
| 1847 | +check_str_eq (const struct message *m, | |
| 1848 | + const char *prop, | |
| 1849 | + const char *expected, | |
| 1850 | + const char *found) { | |
| 1851 | + if ((expected == NULL) != (found == NULL)) { | |
| 1852 | + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); | |
| 1853 | + printf("expected %s\n", (expected == NULL) ? "NULL" : expected); | |
| 1854 | + printf(" found %s\n", (found == NULL) ? "NULL" : found); | |
| 1855 | + return 0; | |
| 1856 | + } | |
| 1857 | + if (expected != NULL && 0 != strcmp(expected, found)) { | |
| 1858 | + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); | |
| 1859 | + printf("expected '%s'\n", expected); | |
| 1860 | + printf(" found '%s'\n", found); | |
| 1861 | + return 0; | |
| 1862 | + } | |
| 1863 | + return 1; | |
| 1864 | +} | |
| 1865 | + | |
| 1866 | +static inline int | |
| 1867 | +check_num_eq (const struct message *m, | |
| 1868 | + const char *prop, | |
| 1869 | + int expected, | |
| 1870 | + int found) { | |
| 1871 | + if (expected != found) { | |
| 1872 | + printf("\n*** Error: %s in '%s' ***\n\n", prop, m->name); | |
| 1873 | + printf("expected %d\n", expected); | |
| 1874 | + printf(" found %d\n", found); | |
| 1875 | + return 0; | |
| 1876 | + } | |
| 1877 | + return 1; | |
| 1878 | +} | |
| 1879 | + | |
| 1880 | +#define MESSAGE_CHECK_STR_EQ(expected, found, prop) \ | |
| 1881 | + if (!check_str_eq(expected, #prop, expected->prop, found->prop)) return 0 | |
| 1882 | + | |
| 1883 | +#define MESSAGE_CHECK_NUM_EQ(expected, found, prop) \ | |
| 1884 | + if (!check_num_eq(expected, #prop, expected->prop, found->prop)) return 0 | |
| 1885 | + | |
| 1886 | +#define MESSAGE_CHECK_URL_EQ(u, expected, found, prop, fn) \ | |
| 1887 | +do { \ | |
| 1888 | + char ubuf[256]; \ | |
| 1889 | + \ | |
| 1890 | + if ((u)->field_set & (1 << (fn))) { \ | |
| 1891 | + memcpy(ubuf, (found)->request_url + (u)->field_data[(fn)].off, \ | |
| 1892 | + (u)->field_data[(fn)].len); \ | |
| 1893 | + ubuf[(u)->field_data[(fn)].len] = '\0'; \ | |
| 1894 | + } else { \ | |
| 1895 | + ubuf[0] = '\0'; \ | |
| 1896 | + } \ | |
| 1897 | + \ | |
| 1898 | + check_str_eq(expected, #prop, expected->prop, ubuf); \ | |
| 1899 | +} while(0) | |
| 1900 | + | |
| 1901 | +int | |
| 1902 | +message_eq (int index, const struct message *expected) | |
| 1903 | +{ | |
| 1904 | + int i; | |
| 1905 | + struct message *m = &messages[index]; | |
| 1906 | + | |
| 1907 | + MESSAGE_CHECK_NUM_EQ(expected, m, http_major); | |
| 1908 | + MESSAGE_CHECK_NUM_EQ(expected, m, http_minor); | |
| 1909 | + | |
| 1910 | + if (expected->type == HTTP_REQUEST) { | |
| 1911 | + MESSAGE_CHECK_NUM_EQ(expected, m, method); | |
| 1912 | + } else { | |
| 1913 | + MESSAGE_CHECK_NUM_EQ(expected, m, status_code); | |
| 1914 | + } | |
| 1915 | + | |
| 1916 | + MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); | |
| 1917 | + MESSAGE_CHECK_NUM_EQ(expected, m, message_complete_on_eof); | |
| 1918 | + | |
| 1919 | + assert(m->message_begin_cb_called); | |
| 1920 | + assert(m->headers_complete_cb_called); | |
| 1921 | + assert(m->message_complete_cb_called); | |
| 1922 | + | |
| 1923 | + | |
| 1924 | + MESSAGE_CHECK_STR_EQ(expected, m, request_url); | |
| 1925 | + | |
| 1926 | + /* Check URL components; we can't do this w/ CONNECT since it doesn't | |
| 1927 | + * send us a well-formed URL. | |
| 1928 | + */ | |
| 1929 | + if (*m->request_url && m->method != HTTP_CONNECT) { | |
| 1930 | + struct http_parser_url u; | |
| 1931 | + | |
| 1932 | + if (http_parser_parse_url(m->request_url, strlen(m->request_url), 0, &u)) { | |
| 1933 | + fprintf(stderr, "\n\n*** failed to parse URL %s ***\n\n", | |
| 1934 | + m->request_url); | |
| 1935 | + abort(); | |
| 1936 | + } | |
| 1937 | + | |
| 1938 | + if (expected->host) { | |
| 1939 | + MESSAGE_CHECK_URL_EQ(&u, expected, m, host, UF_HOST); | |
| 1940 | + } | |
| 1941 | + | |
| 1942 | + if (expected->userinfo) { | |
| 1943 | + MESSAGE_CHECK_URL_EQ(&u, expected, m, userinfo, UF_USERINFO); | |
| 1944 | + } | |
| 1945 | + | |
| 1946 | + m->port = (u.field_set & (1 << UF_PORT)) ? | |
| 1947 | + u.port : 0; | |
| 1948 | + | |
| 1949 | + MESSAGE_CHECK_URL_EQ(&u, expected, m, query_string, UF_QUERY); | |
| 1950 | + MESSAGE_CHECK_URL_EQ(&u, expected, m, fragment, UF_FRAGMENT); | |
| 1951 | + MESSAGE_CHECK_URL_EQ(&u, expected, m, request_path, UF_PATH); | |
| 1952 | + MESSAGE_CHECK_NUM_EQ(expected, m, port); | |
| 1953 | + } | |
| 1954 | + | |
| 1955 | + if (expected->body_size) { | |
| 1956 | + MESSAGE_CHECK_NUM_EQ(expected, m, body_size); | |
| 1957 | + } else { | |
| 1958 | + MESSAGE_CHECK_STR_EQ(expected, m, body); | |
| 1959 | + } | |
| 1960 | + | |
| 1961 | + MESSAGE_CHECK_NUM_EQ(expected, m, num_headers); | |
| 1962 | + | |
| 1963 | + int r; | |
| 1964 | + for (i = 0; i < m->num_headers; i++) { | |
| 1965 | + r = check_str_eq(expected, "header field", expected->headers[i][0], m->headers[i][0]); | |
| 1966 | + if (!r) return 0; | |
| 1967 | + r = check_str_eq(expected, "header value", expected->headers[i][1], m->headers[i][1]); | |
| 1968 | + if (!r) return 0; | |
| 1969 | + } | |
| 1970 | + | |
| 1971 | + MESSAGE_CHECK_STR_EQ(expected, m, upgrade); | |
| 1972 | + | |
| 1973 | + return 1; | |
| 1974 | +} | |
| 1975 | + | |
| 1976 | +/* Given a sequence of varargs messages, return the number of them that the | |
| 1977 | + * parser should successfully parse, taking into account that upgraded | |
| 1978 | + * messages prevent all subsequent messages from being parsed. | |
| 1979 | + */ | |
| 1980 | +size_t | |
| 1981 | +count_parsed_messages(const size_t nmsgs, ...) { | |
| 1982 | + size_t i; | |
| 1983 | + va_list ap; | |
| 1984 | + | |
| 1985 | + va_start(ap, nmsgs); | |
| 1986 | + | |
| 1987 | + for (i = 0; i < nmsgs; i++) { | |
| 1988 | + struct message *m = va_arg(ap, struct message *); | |
| 1989 | + | |
| 1990 | + if (m->upgrade) { | |
| 1991 | + va_end(ap); | |
| 1992 | + return i + 1; | |
| 1993 | + } | |
| 1994 | + } | |
| 1995 | + | |
| 1996 | + va_end(ap); | |
| 1997 | + return nmsgs; | |
| 1998 | +} | |
| 1999 | + | |
| 2000 | +/* Given a sequence of bytes and the number of these that we were able to | |
| 2001 | + * parse, verify that upgrade bodies are correct. | |
| 2002 | + */ | |
| 2003 | +void | |
| 2004 | +upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, ...) { | |
| 2005 | + va_list ap; | |
| 2006 | + size_t i; | |
| 2007 | + size_t off = 0; | |
| 2008 | + | |
| 2009 | + va_start(ap, nmsgs); | |
| 2010 | + | |
| 2011 | + for (i = 0; i < nmsgs; i++) { | |
| 2012 | + struct message *m = va_arg(ap, struct message *); | |
| 2013 | + | |
| 2014 | + off += strlen(m->raw); | |
| 2015 | + | |
| 2016 | + if (m->upgrade) { | |
| 2017 | + off -= strlen(m->upgrade); | |
| 2018 | + | |
| 2019 | + /* Check the portion of the response after its specified upgrade */ | |
| 2020 | + if (!check_str_eq(m, "upgrade", body + off, body + nread)) { | |
| 2021 | + abort(); | |
| 2022 | + } | |
| 2023 | + | |
| 2024 | + /* Fix up the response so that message_eq() will verify the beginning | |
| 2025 | + * of the upgrade */ | |
| 2026 | + *(body + nread + strlen(m->upgrade)) = '\0'; | |
| 2027 | + messages[num_messages -1 ].upgrade = body + nread; | |
| 2028 | + | |
| 2029 | + va_end(ap); | |
| 2030 | + return; | |
| 2031 | + } | |
| 2032 | + } | |
| 2033 | + | |
| 2034 | + va_end(ap); | |
| 2035 | + printf("\n\n*** Error: expected a message with upgrade ***\n"); | |
| 2036 | + | |
| 2037 | + abort(); | |
| 2038 | +} | |
| 2039 | + | |
| 2040 | +static void | |
| 2041 | +print_error (const char *raw, size_t error_location) | |
| 2042 | +{ | |
| 2043 | + fprintf(stderr, "\n*** %s ***\n\n", | |
| 2044 | + http_errno_description(HTTP_PARSER_ERRNO(parser))); | |
| 2045 | + | |
| 2046 | + int this_line = 0, char_len = 0; | |
| 2047 | + size_t i, j, len = strlen(raw), error_location_line = 0; | |
| 2048 | + for (i = 0; i < len; i++) { | |
| 2049 | + if (i == error_location) this_line = 1; | |
| 2050 | + switch (raw[i]) { | |
| 2051 | + case '\r': | |
| 2052 | + char_len = 2; | |
| 2053 | + fprintf(stderr, "\\r"); | |
| 2054 | + break; | |
| 2055 | + | |
| 2056 | + case '\n': | |
| 2057 | + char_len = 2; | |
| 2058 | + fprintf(stderr, "\\n\n"); | |
| 2059 | + | |
| 2060 | + if (this_line) goto print; | |
| 2061 | + | |
| 2062 | + error_location_line = 0; | |
| 2063 | + continue; | |
| 2064 | + | |
| 2065 | + default: | |
| 2066 | + char_len = 1; | |
| 2067 | + fputc(raw[i], stderr); | |
| 2068 | + break; | |
| 2069 | + } | |
| 2070 | + if (!this_line) error_location_line += char_len; | |
| 2071 | + } | |
| 2072 | + | |
| 2073 | + fprintf(stderr, "[eof]\n"); | |
| 2074 | + | |
| 2075 | + print: | |
| 2076 | + for (j = 0; j < error_location_line; j++) { | |
| 2077 | + fputc(' ', stderr); | |
| 2078 | + } | |
| 2079 | + fprintf(stderr, "^\n\nerror location: %u\n", (unsigned int)error_location); | |
| 2080 | +} | |
| 2081 | + | |
| 2082 | +void | |
| 2083 | +test_preserve_data (void) | |
| 2084 | +{ | |
| 2085 | + char my_data[] = "application-specific data"; | |
| 2086 | + http_parser parser; | |
| 2087 | + parser.data = my_data; | |
| 2088 | + http_parser_init(&parser, HTTP_REQUEST); | |
| 2089 | + if (parser.data != my_data) { | |
| 2090 | + printf("\n*** parser.data not preserved accross http_parser_init ***\n\n"); | |
| 2091 | + abort(); | |
| 2092 | + } | |
| 2093 | +} | |
| 2094 | + | |
| 2095 | +struct url_test { | |
| 2096 | + const char *name; | |
| 2097 | + const char *url; | |
| 2098 | + int is_connect; | |
| 2099 | + struct http_parser_url u; | |
| 2100 | + int rv; | |
| 2101 | +}; | |
| 2102 | + | |
| 2103 | +const struct url_test url_tests[] = | |
| 2104 | +{ {.name="proxy request" | |
| 2105 | + ,.url="http://hostname/" | |
| 2106 | + ,.is_connect=0 | |
| 2107 | + ,.u= | |
| 2108 | + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) | |
| 2109 | + ,.port=0 | |
| 2110 | + ,.field_data= | |
| 2111 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2112 | + ,{ 7, 8 } /* UF_HOST */ | |
| 2113 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2114 | + ,{ 15, 1 } /* UF_PATH */ | |
| 2115 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2116 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2117 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2118 | + } | |
| 2119 | + } | |
| 2120 | + ,.rv=0 | |
| 2121 | + } | |
| 2122 | + | |
| 2123 | +, {.name="proxy request with port" | |
| 2124 | + ,.url="http://hostname:444/" | |
| 2125 | + ,.is_connect=0 | |
| 2126 | + ,.u= | |
| 2127 | + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) | |
| 2128 | + ,.port=444 | |
| 2129 | + ,.field_data= | |
| 2130 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2131 | + ,{ 7, 8 } /* UF_HOST */ | |
| 2132 | + ,{ 16, 3 } /* UF_PORT */ | |
| 2133 | + ,{ 19, 1 } /* UF_PATH */ | |
| 2134 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2135 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2136 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2137 | + } | |
| 2138 | + } | |
| 2139 | + ,.rv=0 | |
| 2140 | + } | |
| 2141 | + | |
| 2142 | +, {.name="CONNECT request" | |
| 2143 | + ,.url="hostname:443" | |
| 2144 | + ,.is_connect=1 | |
| 2145 | + ,.u= | |
| 2146 | + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) | |
| 2147 | + ,.port=443 | |
| 2148 | + ,.field_data= | |
| 2149 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2150 | + ,{ 0, 8 } /* UF_HOST */ | |
| 2151 | + ,{ 9, 3 } /* UF_PORT */ | |
| 2152 | + ,{ 0, 0 } /* UF_PATH */ | |
| 2153 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2154 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2155 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2156 | + } | |
| 2157 | + } | |
| 2158 | + ,.rv=0 | |
| 2159 | + } | |
| 2160 | + | |
| 2161 | +, {.name="CONNECT request but not connect" | |
| 2162 | + ,.url="hostname:443" | |
| 2163 | + ,.is_connect=0 | |
| 2164 | + ,.rv=1 | |
| 2165 | + } | |
| 2166 | + | |
| 2167 | +, {.name="proxy ipv6 request" | |
| 2168 | + ,.url="http://[1:2::3:4]/" | |
| 2169 | + ,.is_connect=0 | |
| 2170 | + ,.u= | |
| 2171 | + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) | |
| 2172 | + ,.port=0 | |
| 2173 | + ,.field_data= | |
| 2174 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2175 | + ,{ 8, 8 } /* UF_HOST */ | |
| 2176 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2177 | + ,{ 17, 1 } /* UF_PATH */ | |
| 2178 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2179 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2180 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2181 | + } | |
| 2182 | + } | |
| 2183 | + ,.rv=0 | |
| 2184 | + } | |
| 2185 | + | |
| 2186 | +, {.name="proxy ipv6 request with port" | |
| 2187 | + ,.url="http://[1:2::3:4]:67/" | |
| 2188 | + ,.is_connect=0 | |
| 2189 | + ,.u= | |
| 2190 | + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PORT) | (1 << UF_PATH) | |
| 2191 | + ,.port=67 | |
| 2192 | + ,.field_data= | |
| 2193 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2194 | + ,{ 8, 8 } /* UF_HOST */ | |
| 2195 | + ,{ 18, 2 } /* UF_PORT */ | |
| 2196 | + ,{ 20, 1 } /* UF_PATH */ | |
| 2197 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2198 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2199 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2200 | + } | |
| 2201 | + } | |
| 2202 | + ,.rv=0 | |
| 2203 | + } | |
| 2204 | + | |
| 2205 | +, {.name="CONNECT ipv6 address" | |
| 2206 | + ,.url="[1:2::3:4]:443" | |
| 2207 | + ,.is_connect=1 | |
| 2208 | + ,.u= | |
| 2209 | + {.field_set=(1 << UF_HOST) | (1 << UF_PORT) | |
| 2210 | + ,.port=443 | |
| 2211 | + ,.field_data= | |
| 2212 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2213 | + ,{ 1, 8 } /* UF_HOST */ | |
| 2214 | + ,{ 11, 3 } /* UF_PORT */ | |
| 2215 | + ,{ 0, 0 } /* UF_PATH */ | |
| 2216 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2217 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2218 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2219 | + } | |
| 2220 | + } | |
| 2221 | + ,.rv=0 | |
| 2222 | + } | |
| 2223 | + | |
| 2224 | +, {.name="ipv4 in ipv6 address" | |
| 2225 | + ,.url="http://[2001:0000:0000:0000:0000:0000:1.9.1.1]/" | |
| 2226 | + ,.is_connect=0 | |
| 2227 | + ,.u= | |
| 2228 | + {.field_set=(1 << UF_SCHEMA) | (1 << UF_HOST) | (1 << UF_PATH) | |
| 2229 | + ,.port=0 | |
| 2230 | + ,.field_data= | |
| 2231 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2232 | + ,{ 8, 37 } /* UF_HOST */ | |
| 2233 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2234 | + ,{ 46, 1 } /* UF_PATH */ | |
| 2235 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2236 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2237 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2238 | + } | |
| 2239 | + } | |
| 2240 | + ,.rv=0 | |
| 2241 | + } | |
| 2242 | + | |
| 2243 | +, {.name="extra ? in query string" | |
| 2244 | + ,.url="http://a.tbcdn.cn/p/fp/2010c/??fp-header-min.css,fp-base-min.css," | |
| 2245 | + "fp-channel-min.css,fp-product-min.css,fp-mall-min.css,fp-category-min.css," | |
| 2246 | + "fp-sub-min.css,fp-gdp4p-min.css,fp-css3-min.css,fp-misc-min.css?t=20101022.css" | |
| 2247 | + ,.is_connect=0 | |
| 2248 | + ,.u= | |
| 2249 | + {.field_set=(1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) | |
| 2250 | + ,.port=0 | |
| 2251 | + ,.field_data= | |
| 2252 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2253 | + ,{ 7, 10 } /* UF_HOST */ | |
| 2254 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2255 | + ,{ 17, 12 } /* UF_PATH */ | |
| 2256 | + ,{ 30,187 } /* UF_QUERY */ | |
| 2257 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2258 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2259 | + } | |
| 2260 | + } | |
| 2261 | + ,.rv=0 | |
| 2262 | + } | |
| 2263 | + | |
| 2264 | +, {.name="space URL encoded" | |
| 2265 | + ,.url="/toto.html?toto=a%20b" | |
| 2266 | + ,.is_connect=0 | |
| 2267 | + ,.u= | |
| 2268 | + {.field_set= (1<<UF_PATH) | (1<<UF_QUERY) | |
| 2269 | + ,.port=0 | |
| 2270 | + ,.field_data= | |
| 2271 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2272 | + ,{ 0, 0 } /* UF_HOST */ | |
| 2273 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2274 | + ,{ 0, 10 } /* UF_PATH */ | |
| 2275 | + ,{ 11, 10 } /* UF_QUERY */ | |
| 2276 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2277 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2278 | + } | |
| 2279 | + } | |
| 2280 | + ,.rv=0 | |
| 2281 | + } | |
| 2282 | + | |
| 2283 | + | |
| 2284 | +, {.name="URL fragment" | |
| 2285 | + ,.url="/toto.html#titi" | |
| 2286 | + ,.is_connect=0 | |
| 2287 | + ,.u= | |
| 2288 | + {.field_set= (1<<UF_PATH) | (1<<UF_FRAGMENT) | |
| 2289 | + ,.port=0 | |
| 2290 | + ,.field_data= | |
| 2291 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2292 | + ,{ 0, 0 } /* UF_HOST */ | |
| 2293 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2294 | + ,{ 0, 10 } /* UF_PATH */ | |
| 2295 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2296 | + ,{ 11, 4 } /* UF_FRAGMENT */ | |
| 2297 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2298 | + } | |
| 2299 | + } | |
| 2300 | + ,.rv=0 | |
| 2301 | + } | |
| 2302 | + | |
| 2303 | +, {.name="complex URL fragment" | |
| 2304 | + ,.url="http://www.webmasterworld.com/r.cgi?f=21&d=8405&url=" | |
| 2305 | + "http://www.example.com/index.html?foo=bar&hello=world#midpage" | |
| 2306 | + ,.is_connect=0 | |
| 2307 | + ,.u= | |
| 2308 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_QUERY) |\ | |
| 2309 | + (1<<UF_FRAGMENT) | |
| 2310 | + ,.port=0 | |
| 2311 | + ,.field_data= | |
| 2312 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2313 | + ,{ 7, 22 } /* UF_HOST */ | |
| 2314 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2315 | + ,{ 29, 6 } /* UF_PATH */ | |
| 2316 | + ,{ 36, 69 } /* UF_QUERY */ | |
| 2317 | + ,{106, 7 } /* UF_FRAGMENT */ | |
| 2318 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2319 | + } | |
| 2320 | + } | |
| 2321 | + ,.rv=0 | |
| 2322 | + } | |
| 2323 | + | |
| 2324 | +, {.name="complex URL from node js url parser doc" | |
| 2325 | + ,.url="http://host.com:8080/p/a/t/h?query=string#hash" | |
| 2326 | + ,.is_connect=0 | |
| 2327 | + ,.u= | |
| 2328 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\ | |
| 2329 | + (1<<UF_QUERY) | (1<<UF_FRAGMENT) | |
| 2330 | + ,.port=8080 | |
| 2331 | + ,.field_data= | |
| 2332 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2333 | + ,{ 7, 8 } /* UF_HOST */ | |
| 2334 | + ,{ 16, 4 } /* UF_PORT */ | |
| 2335 | + ,{ 20, 8 } /* UF_PATH */ | |
| 2336 | + ,{ 29, 12 } /* UF_QUERY */ | |
| 2337 | + ,{ 42, 4 } /* UF_FRAGMENT */ | |
| 2338 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2339 | + } | |
| 2340 | + } | |
| 2341 | + ,.rv=0 | |
| 2342 | + } | |
| 2343 | + | |
| 2344 | +, {.name="complex URL with basic auth from node js url parser doc" | |
| 2345 | + ,.url="http://a:b@host.com:8080/p/a/t/h?query=string#hash" | |
| 2346 | + ,.is_connect=0 | |
| 2347 | + ,.u= | |
| 2348 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PORT) | (1<<UF_PATH) |\ | |
| 2349 | + (1<<UF_QUERY) | (1<<UF_FRAGMENT) | (1<<UF_USERINFO) | |
| 2350 | + ,.port=8080 | |
| 2351 | + ,.field_data= | |
| 2352 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2353 | + ,{ 11, 8 } /* UF_HOST */ | |
| 2354 | + ,{ 20, 4 } /* UF_PORT */ | |
| 2355 | + ,{ 24, 8 } /* UF_PATH */ | |
| 2356 | + ,{ 33, 12 } /* UF_QUERY */ | |
| 2357 | + ,{ 46, 4 } /* UF_FRAGMENT */ | |
| 2358 | + ,{ 7, 3 } /* UF_USERINFO */ | |
| 2359 | + } | |
| 2360 | + } | |
| 2361 | + ,.rv=0 | |
| 2362 | + } | |
| 2363 | + | |
| 2364 | +, {.name="double @" | |
| 2365 | + ,.url="http://a:b@@hostname:443/" | |
| 2366 | + ,.is_connect=0 | |
| 2367 | + ,.rv=1 | |
| 2368 | + } | |
| 2369 | + | |
| 2370 | +, {.name="proxy empty host" | |
| 2371 | + ,.url="http://:443/" | |
| 2372 | + ,.is_connect=0 | |
| 2373 | + ,.rv=1 | |
| 2374 | + } | |
| 2375 | + | |
| 2376 | +, {.name="proxy empty port" | |
| 2377 | + ,.url="http://hostname:/" | |
| 2378 | + ,.is_connect=0 | |
| 2379 | + ,.rv=1 | |
| 2380 | + } | |
| 2381 | + | |
| 2382 | +, {.name="CONNECT with basic auth" | |
| 2383 | + ,.url="a:b@hostname:443" | |
| 2384 | + ,.is_connect=1 | |
| 2385 | + ,.rv=1 | |
| 2386 | + } | |
| 2387 | + | |
| 2388 | +, {.name="CONNECT empty host" | |
| 2389 | + ,.url=":443" | |
| 2390 | + ,.is_connect=1 | |
| 2391 | + ,.rv=1 | |
| 2392 | + } | |
| 2393 | + | |
| 2394 | +, {.name="CONNECT empty port" | |
| 2395 | + ,.url="hostname:" | |
| 2396 | + ,.is_connect=1 | |
| 2397 | + ,.rv=1 | |
| 2398 | + } | |
| 2399 | + | |
| 2400 | +, {.name="CONNECT with extra bits" | |
| 2401 | + ,.url="hostname:443/" | |
| 2402 | + ,.is_connect=1 | |
| 2403 | + ,.rv=1 | |
| 2404 | + } | |
| 2405 | + | |
| 2406 | +, {.name="space in URL" | |
| 2407 | + ,.url="/foo bar/" | |
| 2408 | + ,.rv=1 /* s_dead */ | |
| 2409 | + } | |
| 2410 | + | |
| 2411 | +, {.name="proxy basic auth with space url encoded" | |
| 2412 | + ,.url="http://a%20:b@host.com/" | |
| 2413 | + ,.is_connect=0 | |
| 2414 | + ,.u= | |
| 2415 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO) | |
| 2416 | + ,.port=0 | |
| 2417 | + ,.field_data= | |
| 2418 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2419 | + ,{ 14, 8 } /* UF_HOST */ | |
| 2420 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2421 | + ,{ 22, 1 } /* UF_PATH */ | |
| 2422 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2423 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2424 | + ,{ 7, 6 } /* UF_USERINFO */ | |
| 2425 | + } | |
| 2426 | + } | |
| 2427 | + ,.rv=0 | |
| 2428 | + } | |
| 2429 | + | |
| 2430 | +, {.name="carriage return in URL" | |
| 2431 | + ,.url="/foo\rbar/" | |
| 2432 | + ,.rv=1 /* s_dead */ | |
| 2433 | + } | |
| 2434 | + | |
| 2435 | +, {.name="proxy double : in URL" | |
| 2436 | + ,.url="http://hostname::443/" | |
| 2437 | + ,.rv=1 /* s_dead */ | |
| 2438 | + } | |
| 2439 | + | |
| 2440 | +, {.name="proxy basic auth with double :" | |
| 2441 | + ,.url="http://a::b@host.com/" | |
| 2442 | + ,.is_connect=0 | |
| 2443 | + ,.u= | |
| 2444 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO) | |
| 2445 | + ,.port=0 | |
| 2446 | + ,.field_data= | |
| 2447 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2448 | + ,{ 12, 8 } /* UF_HOST */ | |
| 2449 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2450 | + ,{ 20, 1 } /* UF_PATH */ | |
| 2451 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2452 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2453 | + ,{ 7, 4 } /* UF_USERINFO */ | |
| 2454 | + } | |
| 2455 | + } | |
| 2456 | + ,.rv=0 | |
| 2457 | + } | |
| 2458 | + | |
| 2459 | +, {.name="line feed in URL" | |
| 2460 | + ,.url="/foo\nbar/" | |
| 2461 | + ,.rv=1 /* s_dead */ | |
| 2462 | + } | |
| 2463 | + | |
| 2464 | +, {.name="proxy empty basic auth" | |
| 2465 | + ,.url="http://@hostname/fo" | |
| 2466 | + ,.u= | |
| 2467 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | |
| 2468 | + ,.port=0 | |
| 2469 | + ,.field_data= | |
| 2470 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2471 | + ,{ 8, 8 } /* UF_HOST */ | |
| 2472 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2473 | + ,{ 16, 3 } /* UF_PATH */ | |
| 2474 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2475 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2476 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2477 | + } | |
| 2478 | + } | |
| 2479 | + ,.rv=0 | |
| 2480 | + } | |
| 2481 | +, {.name="proxy line feed in hostname" | |
| 2482 | + ,.url="http://host\name/fo" | |
| 2483 | + ,.rv=1 /* s_dead */ | |
| 2484 | + } | |
| 2485 | + | |
| 2486 | +, {.name="proxy % in hostname" | |
| 2487 | + ,.url="http://host%name/fo" | |
| 2488 | + ,.rv=1 /* s_dead */ | |
| 2489 | + } | |
| 2490 | + | |
| 2491 | +, {.name="proxy ; in hostname" | |
| 2492 | + ,.url="http://host;ame/fo" | |
| 2493 | + ,.rv=1 /* s_dead */ | |
| 2494 | + } | |
| 2495 | + | |
| 2496 | +, {.name="proxy basic auth with unreservedchars" | |
| 2497 | + ,.url="http://a!;-_!=+$@host.com/" | |
| 2498 | + ,.is_connect=0 | |
| 2499 | + ,.u= | |
| 2500 | + {.field_set= (1<<UF_SCHEMA) | (1<<UF_HOST) | (1<<UF_PATH) | (1<<UF_USERINFO) | |
| 2501 | + ,.port=0 | |
| 2502 | + ,.field_data= | |
| 2503 | + {{ 0, 4 } /* UF_SCHEMA */ | |
| 2504 | + ,{ 17, 8 } /* UF_HOST */ | |
| 2505 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2506 | + ,{ 25, 1 } /* UF_PATH */ | |
| 2507 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2508 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2509 | + ,{ 7, 9 } /* UF_USERINFO */ | |
| 2510 | + } | |
| 2511 | + } | |
| 2512 | + ,.rv=0 | |
| 2513 | + } | |
| 2514 | + | |
| 2515 | +, {.name="proxy only empty basic auth" | |
| 2516 | + ,.url="http://@/fo" | |
| 2517 | + ,.rv=1 /* s_dead */ | |
| 2518 | + } | |
| 2519 | + | |
| 2520 | +, {.name="proxy only basic auth" | |
| 2521 | + ,.url="http://toto@/fo" | |
| 2522 | + ,.rv=1 /* s_dead */ | |
| 2523 | + } | |
| 2524 | + | |
| 2525 | +, {.name="proxy emtpy hostname" | |
| 2526 | + ,.url="http:///fo" | |
| 2527 | + ,.rv=1 /* s_dead */ | |
| 2528 | + } | |
| 2529 | + | |
| 2530 | +, {.name="proxy = in URL" | |
| 2531 | + ,.url="http://host=ame/fo" | |
| 2532 | + ,.rv=1 /* s_dead */ | |
| 2533 | + } | |
| 2534 | + | |
| 2535 | +#if HTTP_PARSER_STRICT | |
| 2536 | + | |
| 2537 | +, {.name="tab in URL" | |
| 2538 | + ,.url="/foo\tbar/" | |
| 2539 | + ,.rv=1 /* s_dead */ | |
| 2540 | + } | |
| 2541 | + | |
| 2542 | +, {.name="form feed in URL" | |
| 2543 | + ,.url="/foo\fbar/" | |
| 2544 | + ,.rv=1 /* s_dead */ | |
| 2545 | + } | |
| 2546 | + | |
| 2547 | +#else /* !HTTP_PARSER_STRICT */ | |
| 2548 | + | |
| 2549 | +, {.name="tab in URL" | |
| 2550 | + ,.url="/foo\tbar/" | |
| 2551 | + ,.u= | |
| 2552 | + {.field_set=(1 << UF_PATH) | |
| 2553 | + ,.field_data= | |
| 2554 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2555 | + ,{ 0, 0 } /* UF_HOST */ | |
| 2556 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2557 | + ,{ 0, 9 } /* UF_PATH */ | |
| 2558 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2559 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2560 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2561 | + } | |
| 2562 | + } | |
| 2563 | + ,.rv=0 | |
| 2564 | + } | |
| 2565 | + | |
| 2566 | +, {.name="form feed in URL" | |
| 2567 | + ,.url="/foo\fbar/" | |
| 2568 | + ,.u= | |
| 2569 | + {.field_set=(1 << UF_PATH) | |
| 2570 | + ,.field_data= | |
| 2571 | + {{ 0, 0 } /* UF_SCHEMA */ | |
| 2572 | + ,{ 0, 0 } /* UF_HOST */ | |
| 2573 | + ,{ 0, 0 } /* UF_PORT */ | |
| 2574 | + ,{ 0, 9 } /* UF_PATH */ | |
| 2575 | + ,{ 0, 0 } /* UF_QUERY */ | |
| 2576 | + ,{ 0, 0 } /* UF_FRAGMENT */ | |
| 2577 | + ,{ 0, 0 } /* UF_USERINFO */ | |
| 2578 | + } | |
| 2579 | + } | |
| 2580 | + ,.rv=0 | |
| 2581 | + } | |
| 2582 | +#endif | |
| 2583 | +}; | |
| 2584 | + | |
| 2585 | +void | |
| 2586 | +dump_url (const char *url, const struct http_parser_url *u) | |
| 2587 | +{ | |
| 2588 | + unsigned int i; | |
| 2589 | + | |
| 2590 | + printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port); | |
| 2591 | + for (i = 0; i < UF_MAX; i++) { | |
| 2592 | + if ((u->field_set & (1 << i)) == 0) { | |
| 2593 | + printf("\tfield_data[%u]: unset\n", i); | |
| 2594 | + continue; | |
| 2595 | + } | |
| 2596 | + | |
| 2597 | + printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n\"", | |
| 2598 | + i, | |
| 2599 | + u->field_data[i].off, | |
| 2600 | + u->field_data[i].len, | |
| 2601 | + u->field_data[i].len, | |
| 2602 | + url + u->field_data[i].off); | |
| 2603 | + } | |
| 2604 | +} | |
| 2605 | + | |
| 2606 | +void | |
| 2607 | +test_parse_url (void) | |
| 2608 | +{ | |
| 2609 | + struct http_parser_url u; | |
| 2610 | + const struct url_test *test; | |
| 2611 | + unsigned int i; | |
| 2612 | + int rv; | |
| 2613 | + | |
| 2614 | + for (i = 0; i < (sizeof(url_tests) / sizeof(url_tests[0])); i++) { | |
| 2615 | + test = &url_tests[i]; | |
| 2616 | + memset(&u, 0, sizeof(u)); | |
| 2617 | + | |
| 2618 | + rv = http_parser_parse_url(test->url, | |
| 2619 | + strlen(test->url), | |
| 2620 | + test->is_connect, | |
| 2621 | + &u); | |
| 2622 | + | |
| 2623 | + if (test->rv == 0) { | |
| 2624 | + if (rv != 0) { | |
| 2625 | + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " | |
| 2626 | + "unexpected rv %d ***\n\n", test->url, test->name, rv); | |
| 2627 | + abort(); | |
| 2628 | + } | |
| 2629 | + | |
| 2630 | + if (memcmp(&u, &test->u, sizeof(u)) != 0) { | |
| 2631 | + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" failed ***\n", | |
| 2632 | + test->url, test->name); | |
| 2633 | + | |
| 2634 | + printf("target http_parser_url:\n"); | |
| 2635 | + dump_url(test->url, &test->u); | |
| 2636 | + printf("result http_parser_url:\n"); | |
| 2637 | + dump_url(test->url, &u); | |
| 2638 | + | |
| 2639 | + abort(); | |
| 2640 | + } | |
| 2641 | + } else { | |
| 2642 | + /* test->rv != 0 */ | |
| 2643 | + if (rv == 0) { | |
| 2644 | + printf("\n*** http_parser_parse_url(\"%s\") \"%s\" test failed, " | |
| 2645 | + "unexpected rv %d ***\n\n", test->url, test->name, rv); | |
| 2646 | + abort(); | |
| 2647 | + } | |
| 2648 | + } | |
| 2649 | + } | |
| 2650 | +} | |
| 2651 | + | |
| 2652 | +void | |
| 2653 | +test_method_str (void) | |
| 2654 | +{ | |
| 2655 | + assert(0 == strcmp("GET", http_method_str(HTTP_GET))); | |
| 2656 | + assert(0 == strcmp("<unknown>", http_method_str(1337))); | |
| 2657 | +} | |
| 2658 | + | |
| 2659 | +void | |
| 2660 | +test_message (const struct message *message) | |
| 2661 | +{ | |
| 2662 | + size_t raw_len = strlen(message->raw); | |
| 2663 | + size_t msg1len; | |
| 2664 | + for (msg1len = 0; msg1len < raw_len; msg1len++) { | |
| 2665 | + parser_init(message->type); | |
| 2666 | + | |
| 2667 | + size_t read; | |
| 2668 | + const char *msg1 = message->raw; | |
| 2669 | + const char *msg2 = msg1 + msg1len; | |
| 2670 | + size_t msg2len = raw_len - msg1len; | |
| 2671 | + | |
| 2672 | + if (msg1len) { | |
| 2673 | + read = parse(msg1, msg1len); | |
| 2674 | + | |
| 2675 | + if (message->upgrade && parser->upgrade) { | |
| 2676 | + messages[num_messages - 1].upgrade = msg1 + read; | |
| 2677 | + goto test; | |
| 2678 | + } | |
| 2679 | + | |
| 2680 | + if (read != msg1len) { | |
| 2681 | + print_error(msg1, read); | |
| 2682 | + abort(); | |
| 2683 | + } | |
| 2684 | + } | |
| 2685 | + | |
| 2686 | + | |
| 2687 | + read = parse(msg2, msg2len); | |
| 2688 | + | |
| 2689 | + if (message->upgrade && parser->upgrade) { | |
| 2690 | + messages[num_messages - 1].upgrade = msg2 + read; | |
| 2691 | + goto test; | |
| 2692 | + } | |
| 2693 | + | |
| 2694 | + if (read != msg2len) { | |
| 2695 | + print_error(msg2, read); | |
| 2696 | + abort(); | |
| 2697 | + } | |
| 2698 | + | |
| 2699 | + read = parse(NULL, 0); | |
| 2700 | + | |
| 2701 | + if (read != 0) { | |
| 2702 | + print_error(message->raw, read); | |
| 2703 | + abort(); | |
| 2704 | + } | |
| 2705 | + | |
| 2706 | + test: | |
| 2707 | + | |
| 2708 | + if (num_messages != 1) { | |
| 2709 | + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); | |
| 2710 | + abort(); | |
| 2711 | + } | |
| 2712 | + | |
| 2713 | + if(!message_eq(0, message)) abort(); | |
| 2714 | + | |
| 2715 | + parser_free(); | |
| 2716 | + } | |
| 2717 | +} | |
| 2718 | + | |
| 2719 | +void | |
| 2720 | +test_message_count_body (const struct message *message) | |
| 2721 | +{ | |
| 2722 | + parser_init(message->type); | |
| 2723 | + | |
| 2724 | + size_t read; | |
| 2725 | + size_t l = strlen(message->raw); | |
| 2726 | + size_t i, toread; | |
| 2727 | + size_t chunk = 4024; | |
| 2728 | + | |
| 2729 | + for (i = 0; i < l; i+= chunk) { | |
| 2730 | + toread = MIN(l-i, chunk); | |
| 2731 | + read = parse_count_body(message->raw + i, toread); | |
| 2732 | + if (read != toread) { | |
| 2733 | + print_error(message->raw, read); | |
| 2734 | + abort(); | |
| 2735 | + } | |
| 2736 | + } | |
| 2737 | + | |
| 2738 | + | |
| 2739 | + read = parse_count_body(NULL, 0); | |
| 2740 | + if (read != 0) { | |
| 2741 | + print_error(message->raw, read); | |
| 2742 | + abort(); | |
| 2743 | + } | |
| 2744 | + | |
| 2745 | + if (num_messages != 1) { | |
| 2746 | + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", message->name); | |
| 2747 | + abort(); | |
| 2748 | + } | |
| 2749 | + | |
| 2750 | + if(!message_eq(0, message)) abort(); | |
| 2751 | + | |
| 2752 | + parser_free(); | |
| 2753 | +} | |
| 2754 | + | |
| 2755 | +void | |
| 2756 | +test_simple (const char *buf, enum http_errno err_expected) | |
| 2757 | +{ | |
| 2758 | + parser_init(HTTP_REQUEST); | |
| 2759 | + | |
| 2760 | + size_t parsed; | |
| 2761 | + int pass; | |
| 2762 | + enum http_errno err; | |
| 2763 | + | |
| 2764 | + parsed = parse(buf, strlen(buf)); | |
| 2765 | + pass = (parsed == strlen(buf)); | |
| 2766 | + err = HTTP_PARSER_ERRNO(parser); | |
| 2767 | + parsed = parse(NULL, 0); | |
| 2768 | + pass &= (parsed == 0); | |
| 2769 | + | |
| 2770 | + parser_free(); | |
| 2771 | + | |
| 2772 | + /* In strict mode, allow us to pass with an unexpected HPE_STRICT as | |
| 2773 | + * long as the caller isn't expecting success. | |
| 2774 | + */ | |
| 2775 | +#if HTTP_PARSER_STRICT | |
| 2776 | + if (err_expected != err && err_expected != HPE_OK && err != HPE_STRICT) { | |
| 2777 | +#else | |
| 2778 | + if (err_expected != err) { | |
| 2779 | +#endif | |
| 2780 | + fprintf(stderr, "\n*** test_simple expected %s, but saw %s ***\n\n%s\n", | |
| 2781 | + http_errno_name(err_expected), http_errno_name(err), buf); | |
| 2782 | + abort(); | |
| 2783 | + } | |
| 2784 | +} | |
| 2785 | + | |
| 2786 | +void | |
| 2787 | +test_header_overflow_error (int req) | |
| 2788 | +{ | |
| 2789 | + http_parser parser; | |
| 2790 | + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); | |
| 2791 | + size_t parsed; | |
| 2792 | + const char *buf; | |
| 2793 | + buf = req ? "GET / HTTP/1.1\r\n" : "HTTP/1.0 200 OK\r\n"; | |
| 2794 | + parsed = http_parser_execute(&parser, &settings_null, buf, strlen(buf)); | |
| 2795 | + assert(parsed == strlen(buf)); | |
| 2796 | + | |
| 2797 | + buf = "header-key: header-value\r\n"; | |
| 2798 | + size_t buflen = strlen(buf); | |
| 2799 | + | |
| 2800 | + int i; | |
| 2801 | + for (i = 0; i < 10000; i++) { | |
| 2802 | + parsed = http_parser_execute(&parser, &settings_null, buf, buflen); | |
| 2803 | + if (parsed != buflen) { | |
| 2804 | + //fprintf(stderr, "error found on iter %d\n", i); | |
| 2805 | + assert(HTTP_PARSER_ERRNO(&parser) == HPE_HEADER_OVERFLOW); | |
| 2806 | + return; | |
| 2807 | + } | |
| 2808 | + } | |
| 2809 | + | |
| 2810 | + fprintf(stderr, "\n*** Error expected but none in header overflow test ***\n"); | |
| 2811 | + abort(); | |
| 2812 | +} | |
| 2813 | + | |
| 2814 | +static void | |
| 2815 | +test_content_length_overflow (const char *buf, size_t buflen, int expect_ok) | |
| 2816 | +{ | |
| 2817 | + http_parser parser; | |
| 2818 | + http_parser_init(&parser, HTTP_RESPONSE); | |
| 2819 | + http_parser_execute(&parser, &settings_null, buf, buflen); | |
| 2820 | + | |
| 2821 | + if (expect_ok) | |
| 2822 | + assert(HTTP_PARSER_ERRNO(&parser) == HPE_OK); | |
| 2823 | + else | |
| 2824 | + assert(HTTP_PARSER_ERRNO(&parser) == HPE_INVALID_CONTENT_LENGTH); | |
| 2825 | +} | |
| 2826 | + | |
| 2827 | +void | |
| 2828 | +test_header_content_length_overflow_error (void) | |
| 2829 | +{ | |
| 2830 | +#define X(size) \ | |
| 2831 | + "HTTP/1.1 200 OK\r\n" \ | |
| 2832 | + "Content-Length: " #size "\r\n" \ | |
| 2833 | + "\r\n" | |
| 2834 | + const char a[] = X(18446744073709551614); /* 2^64-2 */ | |
| 2835 | + const char b[] = X(18446744073709551615); /* 2^64-1 */ | |
| 2836 | + const char c[] = X(18446744073709551616); /* 2^64 */ | |
| 2837 | +#undef X | |
| 2838 | + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ | |
| 2839 | + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ | |
| 2840 | + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ | |
| 2841 | +} | |
| 2842 | + | |
| 2843 | +void | |
| 2844 | +test_chunk_content_length_overflow_error (void) | |
| 2845 | +{ | |
| 2846 | +#define X(size) \ | |
| 2847 | + "HTTP/1.1 200 OK\r\n" \ | |
| 2848 | + "Transfer-Encoding: chunked\r\n" \ | |
| 2849 | + "\r\n" \ | |
| 2850 | + #size "\r\n" \ | |
| 2851 | + "..." | |
| 2852 | + const char a[] = X(FFFFFFFFFFFFFFFE); /* 2^64-2 */ | |
| 2853 | + const char b[] = X(FFFFFFFFFFFFFFFF); /* 2^64-1 */ | |
| 2854 | + const char c[] = X(10000000000000000); /* 2^64 */ | |
| 2855 | +#undef X | |
| 2856 | + test_content_length_overflow(a, sizeof(a) - 1, 1); /* expect ok */ | |
| 2857 | + test_content_length_overflow(b, sizeof(b) - 1, 0); /* expect failure */ | |
| 2858 | + test_content_length_overflow(c, sizeof(c) - 1, 0); /* expect failure */ | |
| 2859 | +} | |
| 2860 | + | |
| 2861 | +void | |
| 2862 | +test_no_overflow_long_body (int req, size_t length) | |
| 2863 | +{ | |
| 2864 | + http_parser parser; | |
| 2865 | + http_parser_init(&parser, req ? HTTP_REQUEST : HTTP_RESPONSE); | |
| 2866 | + size_t parsed; | |
| 2867 | + size_t i; | |
| 2868 | + char buf1[3000]; | |
| 2869 | + size_t buf1len = sprintf(buf1, "%s\r\nConnection: Keep-Alive\r\nContent-Length: %lu\r\n\r\n", | |
| 2870 | + req ? "POST / HTTP/1.0" : "HTTP/1.0 200 OK", (unsigned long)length); | |
| 2871 | + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); | |
| 2872 | + if (parsed != buf1len) | |
| 2873 | + goto err; | |
| 2874 | + | |
| 2875 | + for (i = 0; i < length; i++) { | |
| 2876 | + char foo = 'a'; | |
| 2877 | + parsed = http_parser_execute(&parser, &settings_null, &foo, 1); | |
| 2878 | + if (parsed != 1) | |
| 2879 | + goto err; | |
| 2880 | + } | |
| 2881 | + | |
| 2882 | + parsed = http_parser_execute(&parser, &settings_null, buf1, buf1len); | |
| 2883 | + if (parsed != buf1len) goto err; | |
| 2884 | + return; | |
| 2885 | + | |
| 2886 | + err: | |
| 2887 | + fprintf(stderr, | |
| 2888 | + "\n*** error in test_no_overflow_long_body %s of length %lu ***\n", | |
| 2889 | + req ? "REQUEST" : "RESPONSE", | |
| 2890 | + (unsigned long)length); | |
| 2891 | + abort(); | |
| 2892 | +} | |
| 2893 | + | |
| 2894 | +void | |
| 2895 | +test_multiple3 (const struct message *r1, const struct message *r2, const struct message *r3) | |
| 2896 | +{ | |
| 2897 | + int message_count = count_parsed_messages(3, r1, r2, r3); | |
| 2898 | + | |
| 2899 | + char total[ strlen(r1->raw) | |
| 2900 | + + strlen(r2->raw) | |
| 2901 | + + strlen(r3->raw) | |
| 2902 | + + 1 | |
| 2903 | + ]; | |
| 2904 | + total[0] = '\0'; | |
| 2905 | + | |
| 2906 | + strcat(total, r1->raw); | |
| 2907 | + strcat(total, r2->raw); | |
| 2908 | + strcat(total, r3->raw); | |
| 2909 | + | |
| 2910 | + parser_init(r1->type); | |
| 2911 | + | |
| 2912 | + size_t read; | |
| 2913 | + | |
| 2914 | + read = parse(total, strlen(total)); | |
| 2915 | + | |
| 2916 | + if (parser->upgrade) { | |
| 2917 | + upgrade_message_fix(total, read, 3, r1, r2, r3); | |
| 2918 | + goto test; | |
| 2919 | + } | |
| 2920 | + | |
| 2921 | + if (read != strlen(total)) { | |
| 2922 | + print_error(total, read); | |
| 2923 | + abort(); | |
| 2924 | + } | |
| 2925 | + | |
| 2926 | + read = parse(NULL, 0); | |
| 2927 | + | |
| 2928 | + if (read != 0) { | |
| 2929 | + print_error(total, read); | |
| 2930 | + abort(); | |
| 2931 | + } | |
| 2932 | + | |
| 2933 | +test: | |
| 2934 | + | |
| 2935 | + if (message_count != num_messages) { | |
| 2936 | + fprintf(stderr, "\n\n*** Parser didn't see 3 messages only %d *** \n", num_messages); | |
| 2937 | + abort(); | |
| 2938 | + } | |
| 2939 | + | |
| 2940 | + if (!message_eq(0, r1)) abort(); | |
| 2941 | + if (message_count > 1 && !message_eq(1, r2)) abort(); | |
| 2942 | + if (message_count > 2 && !message_eq(2, r3)) abort(); | |
| 2943 | + | |
| 2944 | + parser_free(); | |
| 2945 | +} | |
| 2946 | + | |
| 2947 | +/* SCAN through every possible breaking to make sure the | |
| 2948 | + * parser can handle getting the content in any chunks that | |
| 2949 | + * might come from the socket | |
| 2950 | + */ | |
| 2951 | +void | |
| 2952 | +test_scan (const struct message *r1, const struct message *r2, const struct message *r3) | |
| 2953 | +{ | |
| 2954 | + char total[80*1024] = "\0"; | |
| 2955 | + char buf1[80*1024] = "\0"; | |
| 2956 | + char buf2[80*1024] = "\0"; | |
| 2957 | + char buf3[80*1024] = "\0"; | |
| 2958 | + | |
| 2959 | + strcat(total, r1->raw); | |
| 2960 | + strcat(total, r2->raw); | |
| 2961 | + strcat(total, r3->raw); | |
| 2962 | + | |
| 2963 | + size_t read; | |
| 2964 | + | |
| 2965 | + int total_len = strlen(total); | |
| 2966 | + | |
| 2967 | + int total_ops = 2 * (total_len - 1) * (total_len - 2) / 2; | |
| 2968 | + int ops = 0 ; | |
| 2969 | + | |
| 2970 | + size_t buf1_len, buf2_len, buf3_len; | |
| 2971 | + int message_count = count_parsed_messages(3, r1, r2, r3); | |
| 2972 | + | |
| 2973 | + int i,j,type_both; | |
| 2974 | + for (type_both = 0; type_both < 2; type_both ++ ) { | |
| 2975 | + for (j = 2; j < total_len; j ++ ) { | |
| 2976 | + for (i = 1; i < j; i ++ ) { | |
| 2977 | + | |
| 2978 | + if (ops % 1000 == 0) { | |
| 2979 | + printf("\b\b\b\b%3.0f%%", 100 * (float)ops /(float)total_ops); | |
| 2980 | + fflush(stdout); | |
| 2981 | + } | |
| 2982 | + ops += 1; | |
| 2983 | + | |
| 2984 | + parser_init(type_both ? HTTP_BOTH : r1->type); | |
| 2985 | + | |
| 2986 | + buf1_len = i; | |
| 2987 | + strlncpy(buf1, sizeof(buf1), total, buf1_len); | |
| 2988 | + buf1[buf1_len] = 0; | |
| 2989 | + | |
| 2990 | + buf2_len = j - i; | |
| 2991 | + strlncpy(buf2, sizeof(buf1), total+i, buf2_len); | |
| 2992 | + buf2[buf2_len] = 0; | |
| 2993 | + | |
| 2994 | + buf3_len = total_len - j; | |
| 2995 | + strlncpy(buf3, sizeof(buf1), total+j, buf3_len); | |
| 2996 | + buf3[buf3_len] = 0; | |
| 2997 | + | |
| 2998 | + read = parse(buf1, buf1_len); | |
| 2999 | + | |
| 3000 | + if (parser->upgrade) goto test; | |
| 3001 | + | |
| 3002 | + if (read != buf1_len) { | |
| 3003 | + print_error(buf1, read); | |
| 3004 | + goto error; | |
| 3005 | + } | |
| 3006 | + | |
| 3007 | + read += parse(buf2, buf2_len); | |
| 3008 | + | |
| 3009 | + if (parser->upgrade) goto test; | |
| 3010 | + | |
| 3011 | + if (read != buf1_len + buf2_len) { | |
| 3012 | + print_error(buf2, read); | |
| 3013 | + goto error; | |
| 3014 | + } | |
| 3015 | + | |
| 3016 | + read += parse(buf3, buf3_len); | |
| 3017 | + | |
| 3018 | + if (parser->upgrade) goto test; | |
| 3019 | + | |
| 3020 | + if (read != buf1_len + buf2_len + buf3_len) { | |
| 3021 | + print_error(buf3, read); | |
| 3022 | + goto error; | |
| 3023 | + } | |
| 3024 | + | |
| 3025 | + parse(NULL, 0); | |
| 3026 | + | |
| 3027 | +test: | |
| 3028 | + if (parser->upgrade) { | |
| 3029 | + upgrade_message_fix(total, read, 3, r1, r2, r3); | |
| 3030 | + } | |
| 3031 | + | |
| 3032 | + if (message_count != num_messages) { | |
| 3033 | + fprintf(stderr, "\n\nParser didn't see %d messages only %d\n", | |
| 3034 | + message_count, num_messages); | |
| 3035 | + goto error; | |
| 3036 | + } | |
| 3037 | + | |
| 3038 | + if (!message_eq(0, r1)) { | |
| 3039 | + fprintf(stderr, "\n\nError matching messages[0] in test_scan.\n"); | |
| 3040 | + goto error; | |
| 3041 | + } | |
| 3042 | + | |
| 3043 | + if (message_count > 1 && !message_eq(1, r2)) { | |
| 3044 | + fprintf(stderr, "\n\nError matching messages[1] in test_scan.\n"); | |
| 3045 | + goto error; | |
| 3046 | + } | |
| 3047 | + | |
| 3048 | + if (message_count > 2 && !message_eq(2, r3)) { | |
| 3049 | + fprintf(stderr, "\n\nError matching messages[2] in test_scan.\n"); | |
| 3050 | + goto error; | |
| 3051 | + } | |
| 3052 | + | |
| 3053 | + parser_free(); | |
| 3054 | + } | |
| 3055 | + } | |
| 3056 | + } | |
| 3057 | + puts("\b\b\b\b100%"); | |
| 3058 | + return; | |
| 3059 | + | |
| 3060 | + error: | |
| 3061 | + fprintf(stderr, "i=%d j=%d\n", i, j); | |
| 3062 | + fprintf(stderr, "buf1 (%u) %s\n\n", (unsigned int)buf1_len, buf1); | |
| 3063 | + fprintf(stderr, "buf2 (%u) %s\n\n", (unsigned int)buf2_len , buf2); | |
| 3064 | + fprintf(stderr, "buf3 (%u) %s\n", (unsigned int)buf3_len, buf3); | |
| 3065 | + abort(); | |
| 3066 | +} | |
| 3067 | + | |
| 3068 | +// user required to free the result | |
| 3069 | +// string terminated by \0 | |
| 3070 | +char * | |
| 3071 | +create_large_chunked_message (int body_size_in_kb, const char* headers) | |
| 3072 | +{ | |
| 3073 | + int i; | |
| 3074 | + size_t wrote = 0; | |
| 3075 | + size_t headers_len = strlen(headers); | |
| 3076 | + size_t bufsize = headers_len + (5+1024+2)*body_size_in_kb + 6; | |
| 3077 | + char * buf = malloc(bufsize); | |
| 3078 | + | |
| 3079 | + memcpy(buf, headers, headers_len); | |
| 3080 | + wrote += headers_len; | |
| 3081 | + | |
| 3082 | + for (i = 0; i < body_size_in_kb; i++) { | |
| 3083 | + // write 1kb chunk into the body. | |
| 3084 | + memcpy(buf + wrote, "400\r\n", 5); | |
| 3085 | + wrote += 5; | |
| 3086 | + memset(buf + wrote, 'C', 1024); | |
| 3087 | + wrote += 1024; | |
| 3088 | + strcpy(buf + wrote, "\r\n"); | |
| 3089 | + wrote += 2; | |
| 3090 | + } | |
| 3091 | + | |
| 3092 | + memcpy(buf + wrote, "0\r\n\r\n", 6); | |
| 3093 | + wrote += 6; | |
| 3094 | + assert(wrote == bufsize); | |
| 3095 | + | |
| 3096 | + return buf; | |
| 3097 | +} | |
| 3098 | + | |
| 3099 | +void | |
| 3100 | +test_status_complete (void) | |
| 3101 | +{ | |
| 3102 | + parser_init(HTTP_RESPONSE); | |
| 3103 | + parser->data = 0; | |
| 3104 | + http_parser_settings settings = settings_null; | |
| 3105 | + settings.on_status_complete = status_complete_cb; | |
| 3106 | + | |
| 3107 | + char *response = "don't mind me, just a simple response"; | |
| 3108 | + http_parser_execute(parser, &settings, response, strlen(response)); | |
| 3109 | + assert(parser->data == (void*)0); // the status_complete callback was never called | |
| 3110 | + assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line | |
| 3111 | +} | |
| 3112 | + | |
| 3113 | +/* Verify that we can pause parsing at any of the bytes in the | |
| 3114 | + * message and still get the result that we're expecting. */ | |
| 3115 | +void | |
| 3116 | +test_message_pause (const struct message *msg) | |
| 3117 | +{ | |
| 3118 | + char *buf = (char*) msg->raw; | |
| 3119 | + size_t buflen = strlen(msg->raw); | |
| 3120 | + size_t nread; | |
| 3121 | + | |
| 3122 | + parser_init(msg->type); | |
| 3123 | + | |
| 3124 | + do { | |
| 3125 | + nread = parse_pause(buf, buflen); | |
| 3126 | + | |
| 3127 | + // We can only set the upgrade buffer once we've gotten our message | |
| 3128 | + // completion callback. | |
| 3129 | + if (messages[0].message_complete_cb_called && | |
| 3130 | + msg->upgrade && | |
| 3131 | + parser->upgrade) { | |
| 3132 | + messages[0].upgrade = buf + nread; | |
| 3133 | + goto test; | |
| 3134 | + } | |
| 3135 | + | |
| 3136 | + if (nread < buflen) { | |
| 3137 | + | |
| 3138 | + // Not much do to if we failed a strict-mode check | |
| 3139 | + if (HTTP_PARSER_ERRNO(parser) == HPE_STRICT) { | |
| 3140 | + parser_free(); | |
| 3141 | + return; | |
| 3142 | + } | |
| 3143 | + | |
| 3144 | + assert (HTTP_PARSER_ERRNO(parser) == HPE_PAUSED); | |
| 3145 | + } | |
| 3146 | + | |
| 3147 | + buf += nread; | |
| 3148 | + buflen -= nread; | |
| 3149 | + http_parser_pause(parser, 0); | |
| 3150 | + } while (buflen > 0); | |
| 3151 | + | |
| 3152 | + nread = parse_pause(NULL, 0); | |
| 3153 | + assert (nread == 0); | |
| 3154 | + | |
| 3155 | +test: | |
| 3156 | + if (num_messages != 1) { | |
| 3157 | + printf("\n*** num_messages != 1 after testing '%s' ***\n\n", msg->name); | |
| 3158 | + abort(); | |
| 3159 | + } | |
| 3160 | + | |
| 3161 | + if(!message_eq(0, msg)) abort(); | |
| 3162 | + | |
| 3163 | + parser_free(); | |
| 3164 | +} | |
| 3165 | + | |
| 3166 | +int | |
| 3167 | +main (void) | |
| 3168 | +{ | |
| 3169 | + parser = NULL; | |
| 3170 | + int i, j, k; | |
| 3171 | + int request_count; | |
| 3172 | + int response_count; | |
| 3173 | + | |
| 3174 | + printf("sizeof(http_parser) = %u\n", (unsigned int)sizeof(http_parser)); | |
| 3175 | + | |
| 3176 | + for (request_count = 0; requests[request_count].name; request_count++); | |
| 3177 | + for (response_count = 0; responses[response_count].name; response_count++); | |
| 3178 | + | |
| 3179 | + //// API | |
| 3180 | + test_preserve_data(); | |
| 3181 | + test_parse_url(); | |
| 3182 | + test_method_str(); | |
| 3183 | + | |
| 3184 | + //// OVERFLOW CONDITIONS | |
| 3185 | + | |
| 3186 | + test_header_overflow_error(HTTP_REQUEST); | |
| 3187 | + test_no_overflow_long_body(HTTP_REQUEST, 1000); | |
| 3188 | + test_no_overflow_long_body(HTTP_REQUEST, 100000); | |
| 3189 | + | |
| 3190 | + test_header_overflow_error(HTTP_RESPONSE); | |
| 3191 | + test_no_overflow_long_body(HTTP_RESPONSE, 1000); | |
| 3192 | + test_no_overflow_long_body(HTTP_RESPONSE, 100000); | |
| 3193 | + | |
| 3194 | + test_header_content_length_overflow_error(); | |
| 3195 | + test_chunk_content_length_overflow_error(); | |
| 3196 | + | |
| 3197 | + //// RESPONSES | |
| 3198 | + | |
| 3199 | + for (i = 0; i < response_count; i++) { | |
| 3200 | + test_message(&responses[i]); | |
| 3201 | + } | |
| 3202 | + | |
| 3203 | + for (i = 0; i < response_count; i++) { | |
| 3204 | + test_message_pause(&responses[i]); | |
| 3205 | + } | |
| 3206 | + | |
| 3207 | + for (i = 0; i < response_count; i++) { | |
| 3208 | + if (!responses[i].should_keep_alive) continue; | |
| 3209 | + for (j = 0; j < response_count; j++) { | |
| 3210 | + if (!responses[j].should_keep_alive) continue; | |
| 3211 | + for (k = 0; k < response_count; k++) { | |
| 3212 | + test_multiple3(&responses[i], &responses[j], &responses[k]); | |
| 3213 | + } | |
| 3214 | + } | |
| 3215 | + } | |
| 3216 | + | |
| 3217 | + test_message_count_body(&responses[NO_HEADERS_NO_BODY_404]); | |
| 3218 | + test_message_count_body(&responses[TRAILING_SPACE_ON_CHUNKED_BODY]); | |
| 3219 | + | |
| 3220 | + // test very large chunked response | |
| 3221 | + { | |
| 3222 | + char * msg = create_large_chunked_message(31337, | |
| 3223 | + "HTTP/1.0 200 OK\r\n" | |
| 3224 | + "Transfer-Encoding: chunked\r\n" | |
| 3225 | + "Content-Type: text/plain\r\n" | |
| 3226 | + "\r\n"); | |
| 3227 | + struct message large_chunked = | |
| 3228 | + {.name= "large chunked" | |
| 3229 | + ,.type= HTTP_RESPONSE | |
| 3230 | + ,.raw= msg | |
| 3231 | + ,.should_keep_alive= FALSE | |
| 3232 | + ,.message_complete_on_eof= FALSE | |
| 3233 | + ,.http_major= 1 | |
| 3234 | + ,.http_minor= 0 | |
| 3235 | + ,.status_code= 200 | |
| 3236 | + ,.num_headers= 2 | |
| 3237 | + ,.headers= | |
| 3238 | + { { "Transfer-Encoding", "chunked" } | |
| 3239 | + , { "Content-Type", "text/plain" } | |
| 3240 | + } | |
| 3241 | + ,.body_size= 31337*1024 | |
| 3242 | + }; | |
| 3243 | + test_message_count_body(&large_chunked); | |
| 3244 | + free(msg); | |
| 3245 | + } | |
| 3246 | + | |
| 3247 | + | |
| 3248 | + | |
| 3249 | + printf("response scan 1/2 "); | |
| 3250 | + test_scan( &responses[TRAILING_SPACE_ON_CHUNKED_BODY] | |
| 3251 | + , &responses[NO_BODY_HTTP10_KA_204] | |
| 3252 | + , &responses[NO_REASON_PHRASE] | |
| 3253 | + ); | |
| 3254 | + | |
| 3255 | + printf("response scan 2/2 "); | |
| 3256 | + test_scan( &responses[BONJOUR_MADAME_FR] | |
| 3257 | + , &responses[UNDERSTORE_HEADER_KEY] | |
| 3258 | + , &responses[NO_CARRIAGE_RET] | |
| 3259 | + ); | |
| 3260 | + | |
| 3261 | + puts("responses okay"); | |
| 3262 | + | |
| 3263 | + | |
| 3264 | + /// REQUESTS | |
| 3265 | + | |
| 3266 | + test_simple("hello world", HPE_INVALID_METHOD); | |
| 3267 | + test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION); | |
| 3268 | + | |
| 3269 | + | |
| 3270 | + test_simple("ASDF / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD); | |
| 3271 | + test_simple("PROPPATCHA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD); | |
| 3272 | + test_simple("GETA / HTTP/1.1\r\n\r\n", HPE_INVALID_METHOD); | |
| 3273 | + | |
| 3274 | + // Well-formed but incomplete | |
| 3275 | + test_simple("GET / HTTP/1.1\r\n" | |
| 3276 | + "Content-Type: text/plain\r\n" | |
| 3277 | + "Content-Length: 6\r\n" | |
| 3278 | + "\r\n" | |
| 3279 | + "fooba", | |
| 3280 | + HPE_OK); | |
| 3281 | + | |
| 3282 | + static const char *all_methods[] = { | |
| 3283 | + "DELETE", | |
| 3284 | + "GET", | |
| 3285 | + "HEAD", | |
| 3286 | + "POST", | |
| 3287 | + "PUT", | |
| 3288 | + //"CONNECT", //CONNECT can't be tested like other methods, it's a tunnel | |
| 3289 | + "OPTIONS", | |
| 3290 | + "TRACE", | |
| 3291 | + "COPY", | |
| 3292 | + "LOCK", | |
| 3293 | + "MKCOL", | |
| 3294 | + "MOVE", | |
| 3295 | + "PROPFIND", | |
| 3296 | + "PROPPATCH", | |
| 3297 | + "UNLOCK", | |
| 3298 | + "REPORT", | |
| 3299 | + "MKACTIVITY", | |
| 3300 | + "CHECKOUT", | |
| 3301 | + "MERGE", | |
| 3302 | + "M-SEARCH", | |
| 3303 | + "NOTIFY", | |
| 3304 | + "SUBSCRIBE", | |
| 3305 | + "UNSUBSCRIBE", | |
| 3306 | + "PATCH", | |
| 3307 | + 0 }; | |
| 3308 | + const char **this_method; | |
| 3309 | + for (this_method = all_methods; *this_method; this_method++) { | |
| 3310 | + char buf[200]; | |
| 3311 | + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); | |
| 3312 | + test_simple(buf, HPE_OK); | |
| 3313 | + } | |
| 3314 | + | |
| 3315 | + static const char *bad_methods[] = { | |
| 3316 | + "C******", | |
| 3317 | + "M****", | |
| 3318 | + 0 }; | |
| 3319 | + for (this_method = bad_methods; *this_method; this_method++) { | |
| 3320 | + char buf[200]; | |
| 3321 | + sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); | |
| 3322 | + test_simple(buf, HPE_UNKNOWN); | |
| 3323 | + } | |
| 3324 | + | |
| 3325 | + const char *dumbfuck2 = | |
| 3326 | + "GET / HTTP/1.1\r\n" | |
| 3327 | + "X-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n" | |
| 3328 | + "\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n" | |
| 3329 | + "\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n" | |
| 3330 | + "\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n" | |
| 3331 | + "\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n" | |
| 3332 | + "\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n" | |
| 3333 | + "\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n" | |
| 3334 | + "\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n" | |
| 3335 | + "\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n" | |
| 3336 | + "\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n" | |
| 3337 | + "\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n" | |
| 3338 | + "\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n" | |
| 3339 | + "\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n" | |
| 3340 | + "\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgHTTPAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n" | |
| 3341 | + "\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n" | |
| 3342 | + "\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n" | |
| 3343 | + "\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n" | |
| 3344 | + "\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n" | |
| 3345 | + "\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n" | |
| 3346 | + "\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n" | |
| 3347 | + "\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n" | |
| 3348 | + "\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n" | |
| 3349 | + "\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n" | |
| 3350 | + "\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n" | |
| 3351 | + "\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n" | |
| 3352 | + "\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n" | |
| 3353 | + "\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n" | |
| 3354 | + "\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n" | |
| 3355 | + "\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n" | |
| 3356 | + "\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n" | |
| 3357 | + "\tRA==\r\n" | |
| 3358 | + "\t-----END CERTIFICATE-----\r\n" | |
| 3359 | + "\r\n"; | |
| 3360 | + test_simple(dumbfuck2, HPE_OK); | |
| 3361 | + | |
| 3362 | +#if 0 | |
| 3363 | + // NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body | |
| 3364 | + // until EOF. | |
| 3365 | + // | |
| 3366 | + // no content-length | |
| 3367 | + // error if there is a body without content length | |
| 3368 | + const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n" | |
| 3369 | + "Accept: */*\r\n" | |
| 3370 | + "\r\n" | |
| 3371 | + "HELLO"; | |
| 3372 | + test_simple(bad_get_no_headers_no_body, 0); | |
| 3373 | +#endif | |
| 3374 | + /* TODO sending junk and large headers gets rejected */ | |
| 3375 | + | |
| 3376 | + | |
| 3377 | + /* check to make sure our predefined requests are okay */ | |
| 3378 | + for (i = 0; requests[i].name; i++) { | |
| 3379 | + test_message(&requests[i]); | |
| 3380 | + } | |
| 3381 | + | |
| 3382 | + for (i = 0; i < request_count; i++) { | |
| 3383 | + test_message_pause(&requests[i]); | |
| 3384 | + } | |
| 3385 | + | |
| 3386 | + for (i = 0; i < request_count; i++) { | |
| 3387 | + if (!requests[i].should_keep_alive) continue; | |
| 3388 | + for (j = 0; j < request_count; j++) { | |
| 3389 | + if (!requests[j].should_keep_alive) continue; | |
| 3390 | + for (k = 0; k < request_count; k++) { | |
| 3391 | + test_multiple3(&requests[i], &requests[j], &requests[k]); | |
| 3392 | + } | |
| 3393 | + } | |
| 3394 | + } | |
| 3395 | + | |
| 3396 | + printf("request scan 1/4 "); | |
| 3397 | + test_scan( &requests[GET_NO_HEADERS_NO_BODY] | |
| 3398 | + , &requests[GET_ONE_HEADER_NO_BODY] | |
| 3399 | + , &requests[GET_NO_HEADERS_NO_BODY] | |
| 3400 | + ); | |
| 3401 | + | |
| 3402 | + printf("request scan 2/4 "); | |
| 3403 | + test_scan( &requests[POST_CHUNKED_ALL_YOUR_BASE] | |
| 3404 | + , &requests[POST_IDENTITY_BODY_WORLD] | |
| 3405 | + , &requests[GET_FUNKY_CONTENT_LENGTH] | |
| 3406 | + ); | |
| 3407 | + | |
| 3408 | + printf("request scan 3/4 "); | |
| 3409 | + test_scan( &requests[TWO_CHUNKS_MULT_ZERO_END] | |
| 3410 | + , &requests[CHUNKED_W_TRAILING_HEADERS] | |
| 3411 | + , &requests[CHUNKED_W_BULLSHIT_AFTER_LENGTH] | |
| 3412 | + ); | |
| 3413 | + | |
| 3414 | + printf("request scan 4/4 "); | |
| 3415 | + test_scan( &requests[QUERY_URL_WITH_QUESTION_MARK_GET] | |
| 3416 | + , &requests[PREFIX_NEWLINE_GET ] | |
| 3417 | + , &requests[CONNECT_REQUEST] | |
| 3418 | + ); | |
| 3419 | + | |
| 3420 | + test_status_complete(); | |
| 3421 | + | |
| 3422 | + puts("requests okay"); | |
| 3423 | + | |
| 3424 | + return 0; | |
| 3425 | +} | ... | ... |
openbr/plugins/qtnetwork.cmake
| 1 | 1 | set(BR_WITH_QTNETWORK ON CACHE BOOL "Build with QtNetwork") |
| 2 | 2 | if(${BR_WITH_QTNETWORK}) |
| 3 | 3 | find_package(Qt5Network) |
| 4 | + find_package(HttpParser) | |
| 4 | 5 | set(QT_DEPENDENCIES ${QT_DEPENDENCIES} Network) |
| 5 | - set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/qtnetwork.cpp) | |
| 6 | + set(BR_THIRDPARTY_SRC ${BR_THIRDPARTY_SRC} plugins/qtnetwork.cpp ${HTTPPARSER_SRC}) | |
| 7 | + install(FILES ${HTTPPARSER_LICENSE} RENAME http-parser.license DESTINATION .) | |
| 6 | 8 | endif() | ... | ... |
openbr/plugins/qtnetwork.cpp
| ... | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | #include <QTcpServer> |
| 5 | 5 | #include <QTcpSocket> |
| 6 | 6 | #include <opencv2/highgui/highgui.hpp> |
| 7 | +#include <http_parser.h> | |
| 7 | 8 | #include <openbr/openbr_plugin.h> |
| 8 | 9 | |
| 9 | 10 | using namespace cv; |
| ... | ... | @@ -137,21 +138,50 @@ class postFormat : public Format |
| 137 | 138 | Template read() const |
| 138 | 139 | { |
| 139 | 140 | Template t(file); |
| 140 | - qDebug() << t.file.flat(); | |
| 141 | + | |
| 142 | + // Read from socket | |
| 141 | 143 | QTcpSocket *socket = new QTcpSocket(); |
| 142 | 144 | socket->setSocketDescriptor(file.get<qintptr>("socketDescriptor")); |
| 143 | - | |
| 144 | 145 | socket->write("HTTP/1.1 200 OK\r\n" |
| 145 | 146 | "Content-Type: text/html; charset=UTF-8\r\n\r\n" |
| 146 | 147 | "Hello World!\r\n"); |
| 147 | 148 | socket->waitForBytesWritten(); |
| 148 | - | |
| 149 | 149 | socket->waitForReadyRead(); |
| 150 | 150 | QByteArray data = socket->readAll(); |
| 151 | - t.append(imdecode(Mat(1, data.size(), CV_8UC1, data.data()), 1)); | |
| 152 | - | |
| 153 | 151 | socket->close(); |
| 154 | 152 | delete socket; |
| 153 | + | |
| 154 | + // Parse data | |
| 155 | + http_parser_settings settings; | |
| 156 | + settings.on_body = bodyCallback; | |
| 157 | + settings.on_headers_complete = NULL; | |
| 158 | + settings.on_header_field = NULL; | |
| 159 | + settings.on_header_value = NULL; | |
| 160 | + settings.on_message_begin = NULL; | |
| 161 | + settings.on_message_complete = NULL; | |
| 162 | + settings.on_status_complete = NULL; | |
| 163 | + settings.on_url = NULL; | |
| 164 | + | |
| 165 | + { | |
| 166 | + QByteArray body; | |
| 167 | + http_parser parser; | |
| 168 | + http_parser_init(&parser, HTTP_REQUEST); | |
| 169 | + parser.data = &body; | |
| 170 | + http_parser_execute(&parser, &settings, data.data(), data.size()); | |
| 171 | + data = body; | |
| 172 | + } | |
| 173 | + | |
| 174 | + data.prepend("HTTP/1.1 200 OK"); | |
| 175 | + QByteArray body; | |
| 176 | + { // Image data is two layers deep | |
| 177 | + http_parser parser; | |
| 178 | + http_parser_init(&parser, HTTP_BOTH); | |
| 179 | + parser.data = &body; | |
| 180 | + http_parser_execute(&parser, &settings, data.data(), data.size()); | |
| 181 | + } | |
| 182 | + | |
| 183 | + t.append(imdecode(Mat(1, body.size(), CV_8UC1, body.data()), 1)); | |
| 184 | + qDebug() << data.size(); | |
| 155 | 185 | return t; |
| 156 | 186 | } |
| 157 | 187 | |
| ... | ... | @@ -160,6 +190,13 @@ class postFormat : public Format |
| 160 | 190 | (void) t; |
| 161 | 191 | qFatal("Not supported!"); |
| 162 | 192 | } |
| 193 | + | |
| 194 | + static int bodyCallback(http_parser *parser, const char *at, size_t length) | |
| 195 | + { | |
| 196 | + QByteArray *byteArray = (QByteArray*)parser->data; | |
| 197 | + *byteArray = QByteArray(at, length); | |
| 198 | + return 0; | |
| 199 | + } | |
| 163 | 200 | }; |
| 164 | 201 | |
| 165 | 202 | BR_REGISTER(Format, postFormat) | ... | ... |
share/openbr/cmake/FindHttpParser.cmake
0 → 100644