Commit 6f90f78bc0f6f503d2ac749dc5be7c01162efb1d
1 parent
2d6ccbf1
removed http-parser
Showing
14 changed files
with
0 additions
and
6572 deletions
3rdparty/http-parser-2.1/.gitignore deleted
3rdparty/http-parser-2.1/.mailmap deleted
| 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 deleted
3rdparty/http-parser-2.1/AUTHORS deleted
| 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 deleted
3rdparty/http-parser-2.1/LICENSE-MIT deleted
| 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 deleted
| 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 deleted
| 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 deleted
| 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 deleted
| 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 | -} | |
| 45 | 0 | \ No newline at end of file |
3rdparty/http-parser-2.1/http_parser.c deleted
| 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 = (uint16_t) (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 = (uint16_t) (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 = (uint16_t) (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 = (uint16_t) (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 = (uint16_t) (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 deleted
| 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 deleted
| 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 deleted
| 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 | -} |