Commit e52c11d19dd2029ffe27072b82f7d1345e92a349

Authored by Josh Klontz
1 parent b7c43193

added http-parser

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