New http-parser
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.
This commit is contained in:
Родитель
1eba0cadc1
Коммит
7719ce33db
4
LICENSE
4
LICENSE
|
@ -26,9 +26,7 @@ are:
|
|||
Michael Tokarev <mjt@corpit.ru>. Released under the GNU Lesser General
|
||||
Public License version 2.1.
|
||||
|
||||
Additionally deps/http_parser is based on Zed Shaw's Mongrel. Mongrel is
|
||||
copyrighted by Zed Shaw and distributed under GPL2 or a permissive open
|
||||
licence. See deps/http_parser/LICENCE for more information.
|
||||
Other external libraries are my own and all use the same license as Node.
|
||||
|
||||
Node's license follows:
|
||||
|
||||
|
|
|
@ -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
|
||||
rm -f *.o test test_g 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
|
||||
|
||||
.PHONY: clean package
|
||||
.PHONY: clean package test-run test-run-timed
|
||||
|
|
|
@ -5,13 +5,13 @@ This is a parser for HTTP messages written in C. It parses both requests
|
|||
and responses. The parser is designed to be used in performance HTTP
|
||||
applications. It does not make any allocations, it does not buffer data, and
|
||||
it can be interrupted at anytime. It only requires about 128 bytes of data
|
||||
per message stream (in a web server that is per connection).
|
||||
per message stream (in a web server that is per connection).
|
||||
|
||||
Features:
|
||||
|
||||
* No dependencies
|
||||
* No dependencies
|
||||
* Parses both requests and responses.
|
||||
* Handles keep-alive streams.
|
||||
* Handles perstent streams.
|
||||
* Decodes chunked encoding.
|
||||
* Extracts the following data from a message
|
||||
* header fields and values
|
||||
|
@ -32,32 +32,47 @@ using `http_parser_init()` and set the callbacks. That might look something
|
|||
like this:
|
||||
|
||||
http_parser *parser = malloc(sizeof(http_parser));
|
||||
http_parser_init(parser, HTTP_REQUEST);
|
||||
http_parser_init(parser);
|
||||
parser->on_path = my_path_callback;
|
||||
parser->on_header_field = my_header_field_callback;
|
||||
/* ... */
|
||||
parser->data = my_socket;
|
||||
|
||||
When data is received on the socket execute the parser and check for errors.
|
||||
|
||||
size_t len = 80*1024;
|
||||
size_t len = 80*1024, nparsed;
|
||||
char buf[len];
|
||||
ssize_t recved;
|
||||
|
||||
recved = read(fd, buf, len);
|
||||
if (recved != 0) // handle error
|
||||
recved = recv(fd, buf, len, 0);
|
||||
|
||||
http_parser_execute(parser, buf, recved);
|
||||
|
||||
if (http_parser_has_error(parser)) {
|
||||
// handle error. usually just close the connection
|
||||
if (recved < 0) {
|
||||
/* Handle error. */
|
||||
}
|
||||
|
||||
/* Start up / continue the parser.
|
||||
* Note we pass the recved==0 to http_parse_requests to signal
|
||||
* that EOF has been recieved.
|
||||
*/
|
||||
nparsed = http_parse_requests(parser, buf, recved);
|
||||
|
||||
if (nparsed != recved) {
|
||||
/* Handle error. Usually just close the connection. */
|
||||
}
|
||||
|
||||
HTTP needs to know where the end of the stream is. For example, sometimes
|
||||
servers send responses without Content-Length and expect the client to
|
||||
consume input (for the body) until EOF. To tell http_parser about EOF, give
|
||||
`0` as the third parameter to `http_parse_requests()`. Callbacks and errors
|
||||
can still be encountered during an EOF, so one must still be prepared
|
||||
to receive them.
|
||||
|
||||
Scalar valued message information such as `status_code`, `method`, and the
|
||||
HTTP version are stored in the parser structure. This data is only
|
||||
temporarlly stored in `http_parser` and gets reset on each new message. If
|
||||
this information is needed later, copy it out of the structure during the
|
||||
`headers_complete` callback.
|
||||
|
||||
|
||||
The parser decodes the transfer-encoding for both requests and responses
|
||||
transparently. That is, a chunked encoding is decoded before being sent to
|
||||
the on_body callback.
|
||||
|
@ -70,7 +85,7 @@ parser, for example, would not want such a feature.
|
|||
Callbacks
|
||||
---------
|
||||
|
||||
During the `http_parser_execute()` call, the callbacks set in `http_parser`
|
||||
During the `http_parse_requests()` call, the callbacks set in `http_parser`
|
||||
will be executed. The parser maintains state and never looks behind, so
|
||||
buffering the data is not necessary. If you need to save certain data for
|
||||
later usage, you can do that from the callbacks.
|
||||
|
@ -93,7 +108,7 @@ Reading headers may be a tricky task if you read/parse headers partially.
|
|||
Basically, you need to remember whether last header callback was field or value
|
||||
and apply following logic:
|
||||
|
||||
/* on_header_field and on_header_value shortened to on_h_*
|
||||
(on_header_field and on_header_value shortened to on_h_*)
|
||||
------------------------ ------------ --------------------------------------------
|
||||
| State (prev. callback) | Callback | Description/action |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
|
@ -113,19 +128,9 @@ and apply following logic:
|
|||
| value | on_h_value | Value continues. Reallocate value buffer |
|
||||
| | | and append callback data to it |
|
||||
------------------------ ------------ --------------------------------------------
|
||||
*/
|
||||
|
||||
See examples of reading in headers:
|
||||
|
||||
* [partial example](http://gist.github.com/155877) in C
|
||||
* [from http-parser tests](http://github.com/ry/http-parser/blob/37a0ff8928fb0d83cec0d0d8909c5a4abcd221af/test.c#L403) in C
|
||||
* [from Node library](http://github.com/ry/node/blob/842eaf446d2fdcb33b296c67c911c32a0dabc747/src/http.js#L284) in Javascript
|
||||
|
||||
Releases
|
||||
--------
|
||||
|
||||
* [0.2](http://s3.amazonaws.com/four.livejournal/20090807/http_parser-0.2.tar.gz)
|
||||
|
||||
* [0.1](http://s3.amazonaws.com/four.livejournal/20090427/http_parser-0.1.tar.gz)
|
||||
|
||||
The source repo is at [github](http://github.com/ry/http-parser).
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,40 +1,48 @@
|
|||
/* Copyright (c) 2008, 2009 Ryan Dahl (ry@tinyclouds.org)
|
||||
* Based on Zed Shaw's Mongrel, copyright (c) Zed A. Shaw
|
||||
*
|
||||
* All rights reserved.
|
||||
/* 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:
|
||||
* 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 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.
|
||||
* 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.
|
||||
*/
|
||||
#ifndef http_parser_h
|
||||
#define http_parser_h
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifdef _MSC_VER
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
|
||||
* faster
|
||||
*/
|
||||
#ifndef HTTP_PARSER_STRICT
|
||||
# define HTTP_PARSER_STRICT 1
|
||||
#else
|
||||
# define HTTP_PARSER_STRICT 0
|
||||
#endif
|
||||
|
||||
typedef struct http_parser http_parser;
|
||||
|
||||
/* Callbacks should return non-zero to indicate an error. The parse will
|
||||
* then halt execution.
|
||||
*
|
||||
* then halt execution.
|
||||
*
|
||||
* http_data_cb does not return data chunks. It will be call arbitrarally
|
||||
* many times for each string. E.G. you might get 10 callbacks for "on_path"
|
||||
* each providing just a few characters more data.
|
||||
|
@ -43,75 +51,43 @@ typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
|
|||
typedef int (*http_cb) (http_parser*);
|
||||
|
||||
/* Request Methods */
|
||||
#define HTTP_COPY 0x0001
|
||||
#define HTTP_DELETE 0x0002
|
||||
#define HTTP_GET 0x0004
|
||||
#define HTTP_HEAD 0x0008
|
||||
#define HTTP_LOCK 0x0010
|
||||
#define HTTP_MKCOL 0x0020
|
||||
#define HTTP_MOVE 0x0040
|
||||
#define HTTP_OPTIONS 0x0080
|
||||
#define HTTP_POST 0x0100
|
||||
#define HTTP_PROPFIND 0x0200
|
||||
#define HTTP_PROPPATCH 0x0400
|
||||
#define HTTP_PUT 0x0800
|
||||
#define HTTP_TRACE 0x1000
|
||||
#define HTTP_UNLOCK 0x2000
|
||||
|
||||
/* Transfer Encodings */
|
||||
#define HTTP_IDENTITY 0x01
|
||||
#define HTTP_CHUNKED 0x02
|
||||
|
||||
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE };
|
||||
enum http_method
|
||||
{ HTTP_DELETE = 0x0002
|
||||
, HTTP_GET = 0x0004
|
||||
, HTTP_HEAD = 0x0008
|
||||
, HTTP_POST = 0x0100
|
||||
, HTTP_PUT = 0x0800
|
||||
};
|
||||
|
||||
struct http_parser {
|
||||
/** PRIVATE **/
|
||||
int cs;
|
||||
enum http_parser_type type;
|
||||
unsigned short state;
|
||||
unsigned short header_state;
|
||||
size_t header_index;
|
||||
|
||||
size_t chunk_size;
|
||||
char flags;
|
||||
|
||||
/**
|
||||
XXX
|
||||
do this so no other code has to change, but make the field only 1 byte wide
|
||||
instead of 2 (on x86/x86_64).
|
||||
ssize_t body_read;
|
||||
ssize_t content_length;
|
||||
|
||||
doing this not only shrinks the sizeof this struct by a byte but it ALSO
|
||||
makes wrapping this in FFI way easier.
|
||||
*/
|
||||
union {
|
||||
struct {
|
||||
unsigned eating:1;
|
||||
unsigned error:1;
|
||||
};
|
||||
struct {
|
||||
unsigned char _flags;
|
||||
};
|
||||
};
|
||||
|
||||
size_t body_read;
|
||||
|
||||
const char *header_field_mark;
|
||||
size_t header_field_size;
|
||||
const char *header_value_mark;
|
||||
size_t header_value_size;
|
||||
const char *query_string_mark;
|
||||
size_t query_string_size;
|
||||
const char *path_mark;
|
||||
size_t path_size;
|
||||
const char *uri_mark;
|
||||
size_t uri_size;
|
||||
const char *fragment_mark;
|
||||
size_t fragment_size;
|
||||
const char *header_field_mark;
|
||||
size_t header_field_size;
|
||||
const char *header_value_mark;
|
||||
size_t header_value_size;
|
||||
const char *query_string_mark;
|
||||
size_t query_string_size;
|
||||
const char *path_mark;
|
||||
size_t path_size;
|
||||
const char *url_mark;
|
||||
size_t url_size;
|
||||
const char *fragment_mark;
|
||||
size_t fragment_size;
|
||||
|
||||
/** READ-ONLY **/
|
||||
unsigned short status_code; /* responses only */
|
||||
unsigned short method; /* requests only */
|
||||
short transfer_encoding;
|
||||
unsigned short version_major;
|
||||
unsigned short version_minor;
|
||||
short keep_alive;
|
||||
size_t content_length;
|
||||
enum http_method method; /* requests only */
|
||||
unsigned short http_major;
|
||||
unsigned short http_minor;
|
||||
|
||||
/** PUBLIC **/
|
||||
void *data; /* A pointer to get hook to the "connection" or "socket" object */
|
||||
|
@ -123,7 +99,7 @@ struct http_parser {
|
|||
/* requests only */
|
||||
http_data_cb on_path;
|
||||
http_data_cb on_query_string;
|
||||
http_data_cb on_uri;
|
||||
http_data_cb on_url;
|
||||
http_data_cb on_fragment;
|
||||
|
||||
http_data_cb on_header_field;
|
||||
|
@ -133,18 +109,17 @@ struct http_parser {
|
|||
http_cb on_message_complete;
|
||||
};
|
||||
|
||||
/* Initializes an http_parser structure. The second argument specifies if
|
||||
* it will be parsing requests or responses.
|
||||
void http_parser_init(http_parser *parser);
|
||||
size_t http_parse_requests(http_parser *parser, const char *data, size_t len);
|
||||
size_t http_parse_responses(http_parser *parser, const char *data, size_t len);
|
||||
/* Call this in the on_headers_complete or on_message_complete callback to
|
||||
* determine if this will be the last message on the connection.
|
||||
* If you are the server, respond with the "Connection: close" header
|
||||
* if you are the client, close the connection.
|
||||
*/
|
||||
void http_parser_init (http_parser *parser, enum http_parser_type);
|
||||
|
||||
size_t http_parser_execute (http_parser *parser, const char *data, size_t len);
|
||||
|
||||
int http_parser_has_error (http_parser *parser);
|
||||
|
||||
int http_parser_should_keep_alive (http_parser *parser);
|
||||
int http_should_keep_alive(http_parser *parser);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -363,7 +363,7 @@ function createIncomingMessageStream (connection, incoming_listener) {
|
|||
});
|
||||
|
||||
// Only servers will get URI events.
|
||||
connection.addListener("uri", function (data) {
|
||||
connection.addListener("url", function (data) {
|
||||
incoming.uri.full += data;
|
||||
});
|
||||
|
||||
|
@ -441,7 +441,7 @@ function flushMessageQueue (connection, queue) {
|
|||
|
||||
while (message.output.length > 0) {
|
||||
if (connection.readyState !== "open" && connection.readyState !== "writeOnly") {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
var data = message.output.shift();
|
||||
|
@ -474,6 +474,8 @@ function connectionListener (connection) {
|
|||
// we need to keep track of the order they were sent.
|
||||
var responses = [];
|
||||
|
||||
connection.resetParser();
|
||||
|
||||
// is this really needed?
|
||||
connection.addListener("eof", function () {
|
||||
if (responses.length == 0) {
|
||||
|
@ -520,6 +522,7 @@ exports.createClient = function (port, host) {
|
|||
};
|
||||
|
||||
client.addListener("connect", function () {
|
||||
client.resetParser();
|
||||
requests[0].flush();
|
||||
});
|
||||
|
||||
|
|
|
@ -25,12 +25,14 @@ HTTPConnection::Initialize (Handle<Object> target)
|
|||
client_constructor_template->Inherit(Connection::constructor_template);
|
||||
client_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
client_constructor_template->SetClassName(String::NewSymbol("Client"));
|
||||
NODE_SET_PROTOTYPE_METHOD(client_constructor_template, "resetParser", ResetParser);
|
||||
target->Set(String::NewSymbol("Client"), client_constructor_template->GetFunction());
|
||||
|
||||
t = FunctionTemplate::New(NewServer);
|
||||
server_constructor_template = Persistent<FunctionTemplate>::New(t);
|
||||
server_constructor_template->Inherit(Connection::constructor_template);
|
||||
server_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
|
||||
NODE_SET_PROTOTYPE_METHOD(server_constructor_template, "resetParser", ResetParser);
|
||||
server_constructor_template->SetClassName(String::NewSymbol("ServerSideConnection"));
|
||||
}
|
||||
|
||||
|
@ -56,12 +58,46 @@ HTTPConnection::NewServer (const Arguments& args)
|
|||
return args.This();
|
||||
}
|
||||
|
||||
|
||||
Handle<Value> HTTPConnection::ResetParser(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
HTTPConnection *connection = ObjectWrap::Unwrap<HTTPConnection>(args.Holder());
|
||||
connection->ResetParser();
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HTTPConnection::OnReceive (const void *buf, size_t len)
|
||||
{
|
||||
HandleScope scope;
|
||||
|
||||
assert(attached_);
|
||||
http_parser_execute(&parser_, static_cast<const char*>(buf), len);
|
||||
if (http_parser_has_error(&parser_)) ForceClose();
|
||||
size_t nparsed;
|
||||
|
||||
if (type_ == HTTP_REQUEST) {
|
||||
nparsed = http_parse_requests(&parser_, static_cast<const char*>(buf), len);
|
||||
} else {
|
||||
nparsed = http_parse_responses(&parser_, static_cast<const char*>(buf), len);
|
||||
}
|
||||
|
||||
if (nparsed != len) {
|
||||
ForceClose();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTTPConnection::OnEOF ()
|
||||
{
|
||||
HandleScope scope;
|
||||
assert(attached_);
|
||||
printf("(node) HTTP EOF!\n");
|
||||
if (type_ == HTTP_REQUEST) {
|
||||
http_parse_requests(&parser_, NULL, 0);
|
||||
} else {
|
||||
http_parse_responses(&parser_, NULL, 0);
|
||||
}
|
||||
Emit("eof", 0, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -83,13 +119,13 @@ HTTPConnection::on_message_complete (http_parser *parser)
|
|||
}
|
||||
|
||||
int
|
||||
HTTPConnection::on_uri (http_parser *parser, const char *buf, size_t len)
|
||||
HTTPConnection::on_url (http_parser *parser, const char *buf, size_t len)
|
||||
{
|
||||
HandleScope scope;
|
||||
HTTPConnection *connection = static_cast<HTTPConnection*>(parser->data);
|
||||
assert(connection->attached_);
|
||||
Local<Value> argv[1] = { String::New(buf, len) };
|
||||
connection->Emit("uri", 1, argv);
|
||||
connection->Emit("url", 1, argv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -170,20 +206,11 @@ GetMethod (int method)
|
|||
{
|
||||
const char *s;
|
||||
switch (method) {
|
||||
case HTTP_COPY: s = "COPY"; break;
|
||||
case HTTP_DELETE: s = "DELETE"; break;
|
||||
case HTTP_GET: s = "GET"; break;
|
||||
case HTTP_HEAD: s = "HEAD"; break;
|
||||
case HTTP_LOCK: s = "LOCK"; break;
|
||||
case HTTP_MKCOL: s = "MKCOL"; break;
|
||||
case HTTP_MOVE: s = "MOVE"; break;
|
||||
case HTTP_OPTIONS: s = "OPTIONS"; break;
|
||||
case HTTP_POST: s = "POST"; break;
|
||||
case HTTP_PROPFIND: s = "PROPFIND"; break;
|
||||
case HTTP_PROPPATCH: s = "PROPPATCH"; break;
|
||||
case HTTP_PUT: s = "PUT"; break;
|
||||
case HTTP_TRACE: s = "TRACE"; break;
|
||||
case HTTP_UNLOCK: s = "UNLOCK"; break;
|
||||
}
|
||||
HandleScope scope;
|
||||
Local<String> method = String::NewSymbol(s);
|
||||
|
@ -200,26 +227,28 @@ HTTPConnection::on_headers_complete (http_parser *parser)
|
|||
Local<Object> message_info = Object::New();
|
||||
|
||||
// METHOD
|
||||
if (connection->parser_.type == HTTP_REQUEST)
|
||||
if (connection->type_ == HTTP_REQUEST) {
|
||||
message_info->Set(METHOD_SYMBOL, GetMethod(connection->parser_.method));
|
||||
}
|
||||
|
||||
// STATUS
|
||||
if (connection->parser_.type == HTTP_RESPONSE)
|
||||
if (connection->type_ == HTTP_RESPONSE) {
|
||||
message_info->Set(STATUS_CODE_SYMBOL,
|
||||
Integer::New(connection->parser_.status_code));
|
||||
}
|
||||
|
||||
// VERSION
|
||||
char version[10];
|
||||
snprintf( version
|
||||
, 10
|
||||
, "%d.%d"
|
||||
, connection->parser_.version_major
|
||||
, connection->parser_.version_minor
|
||||
, connection->parser_.http_major
|
||||
, connection->parser_.http_minor
|
||||
);
|
||||
message_info->Set(HTTP_VERSION_SYMBOL, String::New(version));
|
||||
|
||||
message_info->Set(SHOULD_KEEP_ALIVE_SYMBOL,
|
||||
http_parser_should_keep_alive(&connection->parser_) ? True() : False());
|
||||
http_should_keep_alive(&connection->parser_) ? True() : False());
|
||||
|
||||
Local<Value> argv[1] = { message_info };
|
||||
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
namespace node {
|
||||
|
||||
enum http_connection_type { HTTP_RESPONSE, HTTP_REQUEST };
|
||||
|
||||
class HTTPConnection : public Connection {
|
||||
public:
|
||||
static void Initialize (v8::Handle<v8::Object> target);
|
||||
|
@ -17,13 +19,19 @@ public:
|
|||
protected:
|
||||
static v8::Handle<v8::Value> NewClient (const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> NewServer (const v8::Arguments& args);
|
||||
static v8::Handle<v8::Value> ResetParser(const v8::Arguments& args);
|
||||
|
||||
HTTPConnection (enum http_parser_type type)
|
||||
HTTPConnection (enum http_connection_type t)
|
||||
: Connection()
|
||||
{
|
||||
http_parser_init (&parser_, type);
|
||||
type_ = t;
|
||||
ResetParser();
|
||||
}
|
||||
|
||||
void ResetParser() {
|
||||
http_parser_init (&parser_);
|
||||
parser_.on_message_begin = on_message_begin;
|
||||
parser_.on_uri = on_uri;
|
||||
parser_.on_url = on_url;
|
||||
parser_.on_path = on_path;
|
||||
parser_.on_fragment = on_fragment;
|
||||
parser_.on_query_string = on_query_string;
|
||||
|
@ -36,9 +44,10 @@ protected:
|
|||
}
|
||||
|
||||
void OnReceive (const void *buf, size_t len);
|
||||
void OnEOF ();
|
||||
|
||||
static int on_message_begin (http_parser *parser);
|
||||
static int on_uri (http_parser *parser, const char *at, size_t length);
|
||||
static int on_url (http_parser *parser, const char *at, size_t length);
|
||||
static int on_query_string (http_parser *parser, const char *at, size_t length);
|
||||
static int on_path (http_parser *parser, const char *at, size_t length);
|
||||
static int on_fragment (http_parser *parser, const char *at, size_t length);
|
||||
|
@ -49,7 +58,9 @@ protected:
|
|||
static int on_message_complete (http_parser *parser);
|
||||
|
||||
http_parser parser_;
|
||||
|
||||
enum http_connection_type type_; // should probably use subclass
|
||||
// but going to refactor this all soon
|
||||
// so won't worry about it.
|
||||
friend class HTTPServer;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче