mirror of https://github.com/lukechilds/node.git
Browse Source
No longer based on Ragel, but hand-written. Had to add HTTPConnection.resetParser() because the parser is stricter and will error out when you try to give it a message after the previous had "Connection: close". The HTTP client was doing that. Thus we reset the parser manually after each new connection.v0.7.4-release
Ryan Dahl
15 years ago
12 changed files with 2135 additions and 6904 deletions
@ -1,79 +0,0 @@ |
|||
Copyright 2009, Ryan Lienhart Dahl. All rights reserved. |
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to |
|||
deal in the Software without restriction, including without limitation the |
|||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
sell copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
IN THE SOFTWARE. |
|||
|
|||
|
|||
|
|||
|
|||
http_parser is based on Zed Shaw's Mongrel. Mongrel's license is as follows. |
|||
|
|||
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT -- |
|||
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw |
|||
<zedshaw at zedshaw dot com> and contributors. You can redistribute it |
|||
and/or modify it under either the terms of the GPL2 or the conditions below: |
|||
|
|||
1. You may make and give away verbatim copies of the source form of the |
|||
software without restriction, provided that you duplicate all of the |
|||
original copyright notices and associated disclaimers. |
|||
|
|||
2. You may modify your copy of the software in any way, provided that |
|||
you do at least ONE of the following: |
|||
|
|||
a) place your modifications in the Public Domain or otherwise make them |
|||
Freely Available, such as by posting said modifications to Usenet or an |
|||
equivalent medium, or by allowing the author to include your |
|||
modifications in the software. |
|||
|
|||
b) use the modified software only within your corporation or |
|||
organization. |
|||
|
|||
c) rename any non-standard executables so the names do not conflict with |
|||
standard executables, which must also be provided. |
|||
|
|||
d) make other distribution arrangements with the author. |
|||
|
|||
3. You may distribute the software in object code or executable |
|||
form, provided that you do at least ONE of the following: |
|||
|
|||
a) distribute the executables and library files of the software, |
|||
together with instructions (in the manual page or equivalent) on where |
|||
to get the original distribution. |
|||
|
|||
b) accompany the distribution with the machine-readable source of the |
|||
software. |
|||
|
|||
c) give non-standard executables non-standard names, with |
|||
instructions on where to get the original software distribution. |
|||
|
|||
d) make other distribution arrangements with the author. |
|||
|
|||
4. You may modify and include the part of the software into any other |
|||
software (possibly commercial). But some files in the distribution |
|||
are not written by the author, so that they are not under this terms. |
|||
|
|||
5. The scripts and library files supplied as input to or produced as |
|||
output from the software do not automatically fall under the |
|||
copyright of the software, but belong to whomever generated them, |
|||
and may be sold commercially, and may be aggregated with this |
|||
software. |
|||
|
|||
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR |
|||
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|||
PURPOSE. |
|||
-- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT ---- CUT -- |
@ -0,0 +1,19 @@ |
|||
Copyright 2009 Ryan Dahl <ry@tinyclouds.org> |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to |
|||
deal in the Software without restriction, including without limitation the |
|||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
sell copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
IN THE SOFTWARE. |
@ -1,27 +1,31 @@ |
|||
#OPT=-O0 -g -Wall -Wextra -Werror
|
|||
OPT=-O2 |
|||
OPT_DEBUG=-O0 -g -Wall -Wextra -Werror |
|||
OPT_FAST=-O3 -DHTTP_PARSER_STRICT=0 |
|||
|
|||
|
|||
http_parser_g.o: http_parser.c http_parser.h Makefile |
|||
gcc $(OPT_DEBUG) -c http_parser.c |
|||
|
|||
test_g: http_parser_g.o test.c |
|||
gcc $(OPT_DEBUG) http_parser.o test.c -o $@ |
|||
|
|||
test-run: test_g |
|||
./test_g |
|||
|
|||
test: http_parser.o test.c |
|||
gcc $(OPT) http_parser.o test.c -o $@ |
|||
|
|||
http_parser.o: http_parser.c http_parser.h Makefile |
|||
gcc $(OPT) -c http_parser.c |
|||
gcc $(OPT_FAST) -c http_parser.c |
|||
|
|||
http_parser.c: http_parser.rl Makefile |
|||
ragel -s -G2 http_parser.rl -o $@ |
|||
test: http_parser.o test.c |
|||
gcc $(OPT_FAST) http_parser.o test.c -o $@ |
|||
|
|||
tags: http_parser.rl http_parser.h test.c |
|||
test-run-timed: test |
|||
while(true) do time ./test > /dev/null; done |
|||
|
|||
|
|||
tags: http_parser.c http_parser.h test.c |
|||
ctags $^ |
|||
|
|||
clean: |
|||
rm -f *.o http_parser.c test http_parser.tar |
|||
|
|||
package: http_parser.c |
|||
@rm -rf /tmp/http_parser && mkdir /tmp/http_parser && \
|
|||
cp LICENSE README.md Makefile http_parser.c http_parser.rl \
|
|||
http_parser.h test.c /tmp/http_parser && \
|
|||
cd /tmp && \
|
|||
tar -cf http_parser.tar http_parser/ |
|||
@echo /tmp/http_parser.tar |
|||
rm -f *.o test test_g http_parser.tar |
|||
|
|||
.PHONY: clean package |
|||
.PHONY: clean package test-run test-run-timed |
|||
|
File diff suppressed because it is too large
@ -1,502 +0,0 @@ |
|||
/* Copyright (c) 2008, 2009 Ryan Dahl (ry@tinyclouds.org) |
|||
* Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw |
|||
* |
|||
* All rights reserved. |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining |
|||
* a copy of this software and associated documentation files (the |
|||
* "Software"), to deal in the Software without restriction, including |
|||
* without limitation the rights to use, copy, modify, merge, publish, |
|||
* distribute, sublicense, and/or sell copies of the Software, and to |
|||
* permit persons to whom the Software is furnished to do so, subject to |
|||
* the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be |
|||
* included in all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
|||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
|||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
|||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
|||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
*/ |
|||
#include "http_parser.h" |
|||
#include <limits.h> |
|||
#include <assert.h> |
|||
|
|||
static int unhex[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 |
|||
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|||
}; |
|||
#define TRUE 1 |
|||
#define FALSE 0 |
|||
#define MIN(a,b) (a < b ? a : b) |
|||
#define NULL (void*)(0) |
|||
|
|||
#define MAX_FIELD_SIZE 80*1024 |
|||
|
|||
#define REMAINING (unsigned long)(pe - p) |
|||
#define CALLBACK(FOR) \ |
|||
do { \ |
|||
if (parser->FOR##_mark) { \ |
|||
parser->FOR##_size += p - parser->FOR##_mark; \ |
|||
if (parser->FOR##_size > MAX_FIELD_SIZE) { \ |
|||
parser->error = TRUE; \ |
|||
return 0; \ |
|||
} \ |
|||
if (parser->on_##FOR) { \ |
|||
callback_return_value = parser->on_##FOR(parser, \ |
|||
parser->FOR##_mark, \ |
|||
p - parser->FOR##_mark); \ |
|||
} \ |
|||
} \ |
|||
} while(0) |
|||
|
|||
#define RESET_PARSER(parser) \ |
|||
parser->chunk_size = 0; \ |
|||
parser->eating = 0; \ |
|||
parser->header_field_mark = NULL; \ |
|||
parser->header_value_mark = NULL; \ |
|||
parser->query_string_mark = NULL; \ |
|||
parser->path_mark = NULL; \ |
|||
parser->uri_mark = NULL; \ |
|||
parser->fragment_mark = NULL; \ |
|||
parser->status_code = 0; \ |
|||
parser->method = 0; \ |
|||
parser->transfer_encoding = HTTP_IDENTITY; \ |
|||
parser->version_major = 0; \ |
|||
parser->version_minor = 0; \ |
|||
parser->keep_alive = -1; \ |
|||
parser->content_length = 0; \ |
|||
parser->body_read = 0; |
|||
|
|||
#define END_REQUEST \ |
|||
do { \ |
|||
if (parser->on_message_complete) { \ |
|||
callback_return_value = \ |
|||
parser->on_message_complete(parser); \ |
|||
} \ |
|||
RESET_PARSER(parser); \ |
|||
} while (0) |
|||
|
|||
#define SKIP_BODY(nskip) \ |
|||
do { \ |
|||
tmp = (nskip); \ |
|||
if (parser->on_body && tmp > 0) { \ |
|||
callback_return_value = parser->on_body(parser, p, tmp); \ |
|||
} \ |
|||
if (callback_return_value == 0) { \ |
|||
p += tmp; \ |
|||
parser->body_read += tmp; \ |
|||
parser->chunk_size -= tmp; \ |
|||
if (0 == parser->chunk_size) { \ |
|||
parser->eating = FALSE; \ |
|||
if (parser->transfer_encoding == HTTP_IDENTITY) { \ |
|||
END_REQUEST; \ |
|||
} \ |
|||
} else { \ |
|||
parser->eating = TRUE; \ |
|||
} \ |
|||
} \ |
|||
} while (0) |
|||
|
|||
%%{ |
|||
machine http_parser; |
|||
|
|||
action mark_header_field { |
|||
parser->header_field_mark = p; |
|||
parser->header_field_size = 0; |
|||
} |
|||
|
|||
action mark_header_value { |
|||
parser->header_value_mark = p; |
|||
parser->header_value_size = 0; |
|||
} |
|||
|
|||
action mark_fragment { |
|||
parser->fragment_mark = p; |
|||
parser->fragment_size = 0; |
|||
} |
|||
|
|||
action mark_query_string { |
|||
parser->query_string_mark = p; |
|||
parser->query_string_size = 0; |
|||
} |
|||
|
|||
action mark_request_path { |
|||
parser->path_mark = p; |
|||
parser->path_size = 0; |
|||
} |
|||
|
|||
action mark_request_uri { |
|||
parser->uri_mark = p; |
|||
parser->uri_size = 0; |
|||
} |
|||
|
|||
action header_field { |
|||
CALLBACK(header_field); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->header_field_mark = NULL; |
|||
parser->header_field_size = 0; |
|||
} |
|||
|
|||
action header_value { |
|||
CALLBACK(header_value); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->header_value_mark = NULL; |
|||
parser->header_value_size = 0; |
|||
} |
|||
|
|||
action request_uri { |
|||
CALLBACK(uri); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->uri_mark = NULL; |
|||
parser->uri_size = 0; |
|||
} |
|||
|
|||
action fragment { |
|||
CALLBACK(fragment); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->fragment_mark = NULL; |
|||
parser->fragment_size = 0; |
|||
} |
|||
|
|||
action query_string { |
|||
CALLBACK(query_string); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->query_string_mark = NULL; |
|||
parser->query_string_size = 0; |
|||
} |
|||
|
|||
action request_path { |
|||
CALLBACK(path); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->path_mark = NULL; |
|||
parser->path_size = 0; |
|||
} |
|||
|
|||
action headers_complete { |
|||
if(parser->on_headers_complete) { |
|||
callback_return_value = parser->on_headers_complete(parser); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
action begin_message { |
|||
if(parser->on_message_begin) { |
|||
callback_return_value = parser->on_message_begin(parser); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
action content_length { |
|||
if (parser->content_length > INT_MAX) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
parser->content_length *= 10; |
|||
parser->content_length += *p - '0'; |
|||
} |
|||
|
|||
action status_code { |
|||
parser->status_code *= 10; |
|||
parser->status_code += *p - '0'; |
|||
} |
|||
|
|||
action use_identity_encoding { parser->transfer_encoding = HTTP_IDENTITY; } |
|||
action use_chunked_encoding { parser->transfer_encoding = HTTP_CHUNKED; } |
|||
|
|||
action set_keep_alive { parser->keep_alive = TRUE; } |
|||
action set_not_keep_alive { parser->keep_alive = FALSE; } |
|||
|
|||
action version_major { |
|||
parser->version_major *= 10; |
|||
parser->version_major += *p - '0'; |
|||
} |
|||
|
|||
action version_minor { |
|||
parser->version_minor *= 10; |
|||
parser->version_minor += *p - '0'; |
|||
} |
|||
|
|||
action add_to_chunk_size { |
|||
parser->chunk_size *= 16; |
|||
parser->chunk_size += unhex[(int)*p]; |
|||
} |
|||
|
|||
action skip_chunk_data { |
|||
SKIP_BODY(MIN(parser->chunk_size, REMAINING)); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
|
|||
fhold; |
|||
if (parser->chunk_size > REMAINING) { |
|||
fbreak; |
|||
} else { |
|||
fgoto chunk_end; |
|||
} |
|||
} |
|||
|
|||
action end_chunked_body { |
|||
END_REQUEST; |
|||
if (parser->type == HTTP_REQUEST) { |
|||
fnext Requests; |
|||
} else { |
|||
fnext Responses; |
|||
} |
|||
} |
|||
|
|||
action body_logic { |
|||
if (parser->transfer_encoding == HTTP_CHUNKED) { |
|||
fnext ChunkedBody; |
|||
} else { |
|||
/* this is pretty stupid. i'd prefer to combine this with skip_chunk_data */ |
|||
parser->chunk_size = parser->content_length; |
|||
p += 1; |
|||
|
|||
SKIP_BODY(MIN(REMAINING, parser->content_length)); |
|||
|
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
|
|||
fhold; |
|||
if(parser->chunk_size > REMAINING) { |
|||
fbreak; |
|||
} |
|||
} |
|||
} |
|||
|
|||
CRLF = "\r\n"; |
|||
|
|||
# character types |
|||
CTL = (cntrl | 127); |
|||
safe = ("$" | "-" | "_" | "."); |
|||
extra = ("!" | "*" | "'" | "(" | ")" | ","); |
|||
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); |
|||
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); |
|||
national = any -- (alpha | digit | reserved | extra | safe | unsafe); |
|||
unreserved = (alpha | digit | safe | extra | national); |
|||
escape = ("%" xdigit xdigit); |
|||
uchar = (unreserved | escape | "\""); |
|||
pchar = (uchar | ":" | "@" | "&" | "=" | "+"); |
|||
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" |
|||
| "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); |
|||
|
|||
# elements |
|||
token = (ascii -- (CTL | tspecials)); |
|||
quote = "\""; |
|||
# qdtext = token -- "\""; |
|||
# quoted_pair = "\" ascii; |
|||
# quoted_string = "\"" (qdtext | quoted_pair )* "\""; |
|||
|
|||
# headers |
|||
|
|||
Method = ( "COPY" %{ parser->method = HTTP_COPY; } |
|||
| "DELETE" %{ parser->method = HTTP_DELETE; } |
|||
| "GET" %{ parser->method = HTTP_GET; } |
|||
| "HEAD" %{ parser->method = HTTP_HEAD; } |
|||
| "LOCK" %{ parser->method = HTTP_LOCK; } |
|||
| "MKCOL" %{ parser->method = HTTP_MKCOL; } |
|||
| "MOVE" %{ parser->method = HTTP_MOVE; } |
|||
| "OPTIONS" %{ parser->method = HTTP_OPTIONS; } |
|||
| "POST" %{ parser->method = HTTP_POST; } |
|||
| "PROPFIND" %{ parser->method = HTTP_PROPFIND; } |
|||
| "PROPPATCH" %{ parser->method = HTTP_PROPPATCH; } |
|||
| "PUT" %{ parser->method = HTTP_PUT; } |
|||
| "TRACE" %{ parser->method = HTTP_TRACE; } |
|||
| "UNLOCK" %{ parser->method = HTTP_UNLOCK; } |
|||
); # Not allowing extension methods |
|||
|
|||
HTTP_Version = "HTTP/" digit $version_major "." digit $version_minor; |
|||
|
|||
scheme = ( alpha | digit | "+" | "-" | "." )* ; |
|||
absolute_uri = (scheme ":" (uchar | reserved )*); |
|||
path = ( pchar+ ( "/" pchar* )* ) ; |
|||
query = ( uchar | reserved )* >mark_query_string %query_string ; |
|||
param = ( pchar | "/" )* ; |
|||
params = ( param ( ";" param )* ) ; |
|||
rel_path = ( path? (";" params)? ) ; |
|||
absolute_path = ( "/"+ rel_path ) >mark_request_path %request_path ("?" query)?; |
|||
Request_URI = ( "*" | absolute_uri | absolute_path ) >mark_request_uri %request_uri; |
|||
Fragment = ( uchar | reserved )* >mark_fragment %fragment; |
|||
|
|||
field_name = ( token -- ":" )+; |
|||
Field_Name = field_name >mark_header_field %header_field; |
|||
|
|||
field_value = ((any - " ") any*)?; |
|||
Field_Value = field_value >mark_header_value %header_value; |
|||
|
|||
hsep = ":" " "*; |
|||
header = (field_name hsep field_value) :> CRLF; |
|||
Header = ( ("Content-Length"i hsep digit+ $content_length) |
|||
| ("Connection"i hsep |
|||
( "Keep-Alive"i %set_keep_alive |
|||
| "close"i %set_not_keep_alive |
|||
) |
|||
) |
|||
| ("Transfer-Encoding"i %use_chunked_encoding hsep "identity" %use_identity_encoding) |
|||
| (Field_Name hsep Field_Value) |
|||
) :> CRLF; |
|||
|
|||
Headers = (Header)* :> CRLF @headers_complete; |
|||
|
|||
Request_Line = ( Method " " Request_URI ("#" Fragment)? " " HTTP_Version CRLF ) ; |
|||
|
|||
StatusCode = (digit digit digit) $status_code; |
|||
ReasonPhrase = ascii* -- ("\r" | "\n"); |
|||
StatusLine = HTTP_Version " " StatusCode (" " ReasonPhrase)? CRLF; |
|||
|
|||
# chunked message |
|||
trailing_headers = header*; |
|||
#chunk_ext_val = token | quoted_string; |
|||
chunk_ext_val = token*; |
|||
chunk_ext_name = token*; |
|||
chunk_extension = ( ";" " "* chunk_ext_name ("=" chunk_ext_val)? )*; |
|||
last_chunk = "0"+ chunk_extension CRLF; |
|||
chunk_size = (xdigit* [1-9a-fA-F] xdigit*) $add_to_chunk_size; |
|||
chunk_end = CRLF; |
|||
chunk_body = any >skip_chunk_data; |
|||
chunk_begin = chunk_size chunk_extension CRLF; |
|||
chunk = chunk_begin chunk_body chunk_end; |
|||
ChunkedBody := chunk* last_chunk trailing_headers CRLF @end_chunked_body; |
|||
|
|||
Request = (Request_Line Headers) >begin_message @body_logic; |
|||
Response = (StatusLine Headers) >begin_message @body_logic; |
|||
|
|||
Requests := Request*; |
|||
Responses := Response*; |
|||
|
|||
main := any >{ |
|||
fhold; |
|||
if (parser->type == HTTP_REQUEST) { |
|||
fgoto Requests; |
|||
} else { |
|||
fgoto Responses; |
|||
} |
|||
}; |
|||
|
|||
}%% |
|||
|
|||
%% write data; |
|||
|
|||
void |
|||
http_parser_init (http_parser *parser, enum http_parser_type type) |
|||
{ |
|||
int cs = 0; |
|||
%% write init; |
|||
parser->cs = cs; |
|||
parser->type = type; |
|||
parser->error = 0; |
|||
|
|||
parser->on_message_begin = NULL; |
|||
parser->on_path = NULL; |
|||
parser->on_query_string = NULL; |
|||
parser->on_uri = NULL; |
|||
parser->on_fragment = NULL; |
|||
parser->on_header_field = NULL; |
|||
parser->on_header_value = NULL; |
|||
parser->on_headers_complete = NULL; |
|||
parser->on_body = NULL; |
|||
parser->on_message_complete = NULL; |
|||
|
|||
RESET_PARSER(parser); |
|||
} |
|||
|
|||
/** exec **/ |
|||
size_t |
|||
http_parser_execute (http_parser *parser, const char *buffer, size_t len) |
|||
{ |
|||
size_t tmp; // REMOVE ME this is extremely hacky |
|||
int callback_return_value = 0; |
|||
const char *p, *pe; |
|||
int cs = parser->cs; |
|||
|
|||
p = buffer; |
|||
pe = buffer+len; |
|||
|
|||
if (0 < parser->chunk_size && parser->eating) { |
|||
/* eat body */ |
|||
SKIP_BODY(MIN(len, parser->chunk_size)); |
|||
if (callback_return_value != 0) { |
|||
parser->error = TRUE; |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
if (parser->header_field_mark) parser->header_field_mark = buffer; |
|||
if (parser->header_value_mark) parser->header_value_mark = buffer; |
|||
if (parser->fragment_mark) parser->fragment_mark = buffer; |
|||
if (parser->query_string_mark) parser->query_string_mark = buffer; |
|||
if (parser->path_mark) parser->path_mark = buffer; |
|||
if (parser->uri_mark) parser->uri_mark = buffer; |
|||
|
|||
%% write exec; |
|||
|
|||
parser->cs = cs; |
|||
|
|||
CALLBACK(header_field); |
|||
CALLBACK(header_value); |
|||
CALLBACK(fragment); |
|||
CALLBACK(query_string); |
|||
CALLBACK(path); |
|||
CALLBACK(uri); |
|||
|
|||
assert(p <= pe && "buffer overflow after parsing execute"); |
|||
return(p - buffer); |
|||
} |
|||
|
|||
int |
|||
http_parser_has_error (http_parser *parser) |
|||
{ |
|||
if (parser->error) return TRUE; |
|||
return parser->cs == http_parser_error; |
|||
} |
|||
|
|||
int |
|||
http_parser_should_keep_alive (http_parser *parser) |
|||
{ |
|||
if (parser->keep_alive == -1) |
|||
if (parser->version_major == 1) |
|||
return (parser->version_minor != 0); |
|||
else if (parser->version_major == 0) |
|||
return FALSE; |
|||
else |
|||
return TRUE; |
|||
else |
|||
return parser->keep_alive; |
|||
} |
File diff suppressed because it is too large
Loading…
Reference in new issue