Update http-parser to 4e382f96e6d3321538a78f2c7f9506d4e79b08d6
This commit is contained in:
parent
1cfdf386ff
commit
c5f3eee3be
|
@ -65,3 +65,4 @@ Romain Giraud <giraud.romain@gmail.com>
|
||||||
Jay Satiro <raysatiro@yahoo.com>
|
Jay Satiro <raysatiro@yahoo.com>
|
||||||
Arne Steen <Arne.Steen@gmx.de>
|
Arne Steen <Arne.Steen@gmx.de>
|
||||||
Kjell Schubert <kjell.schubert@gmail.com>
|
Kjell Schubert <kjell.schubert@gmail.com>
|
||||||
|
Olivier Mengué <dolmen@cpan.org>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
HTTP Parser
|
HTTP Parser
|
||||||
===========
|
===========
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/joyent/http-parser.png?branch=master)](https://travis-ci.org/joyent/http-parser)
|
[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
|
||||||
|
|
||||||
This is a parser for HTTP messages written in C. It parses both requests and
|
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
|
responses. The parser is designed to be used in performance HTTP
|
||||||
|
@ -137,6 +137,69 @@ There are two types of callbacks:
|
||||||
Callbacks must return 0 on success. Returning a non-zero value indicates
|
Callbacks must return 0 on success. Returning a non-zero value indicates
|
||||||
error to the parser, making it exit immediately.
|
error to the parser, making it exit immediately.
|
||||||
|
|
||||||
|
For cases where it is necessary to pass local information to/from a callback,
|
||||||
|
the `http_parser` object's `data` field can be used.
|
||||||
|
An example of such a case is when using threads to handle a socket connection,
|
||||||
|
parse a request, and then give a response over that socket. By instantiation
|
||||||
|
of a thread-local struct containing relevant data (e.g. accepted socket,
|
||||||
|
allocated memory for callbacks to write into, etc), a parser's callbacks are
|
||||||
|
able to communicate data between the scope of the thread and the scope of the
|
||||||
|
callback in a threadsafe manner. This allows http-parser to be used in
|
||||||
|
multi-threaded contexts.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
typedef struct {
|
||||||
|
socket_t sock;
|
||||||
|
void* buffer;
|
||||||
|
int buf_len;
|
||||||
|
} custom_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
int my_url_callback(http_parser* parser, const char *at, size_t length) {
|
||||||
|
/* access to thread local custom_data_t struct.
|
||||||
|
Use this access save parsed data for later use into thread local
|
||||||
|
buffer, or communicate over socket
|
||||||
|
*/
|
||||||
|
parser->data;
|
||||||
|
...
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
void http_parser_thread(socket_t sock) {
|
||||||
|
int nparsed = 0;
|
||||||
|
/* allocate memory for user data */
|
||||||
|
custom_data_t *my_data = malloc(sizeof(custom_data_t));
|
||||||
|
|
||||||
|
/* some information for use by callbacks.
|
||||||
|
* achieves thread -> callback information flow */
|
||||||
|
my_data->sock = sock;
|
||||||
|
|
||||||
|
/* instantiate a thread-local parser */
|
||||||
|
http_parser *parser = malloc(sizeof(http_parser));
|
||||||
|
http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
|
||||||
|
/* this custom data reference is accessible through the reference to the
|
||||||
|
parser supplied to callback functions */
|
||||||
|
parser->data = my_data;
|
||||||
|
|
||||||
|
http_parser_settings settings; / * set up callbacks */
|
||||||
|
settings.on_url = my_url_callback;
|
||||||
|
|
||||||
|
/* execute parser */
|
||||||
|
nparsed = http_parser_execute(parser, &settings, buf, recved);
|
||||||
|
|
||||||
|
...
|
||||||
|
/* parsed information copied from callback.
|
||||||
|
can now perform action on data copied into thread-local memory from callbacks.
|
||||||
|
achieves callback -> thread information flow */
|
||||||
|
my_data->buffer;
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
In case you parse HTTP message in chunks (i.e. `read()` request line
|
In case you parse HTTP message in chunks (i.e. `read()` request line
|
||||||
from socket, parse, read half headers, parse, etc) your data callbacks
|
from socket, parse, read half headers, parse, etc) your data callbacks
|
||||||
may be called more than once. Http-parser guarantees that data pointer is only
|
may be called more than once. Http-parser guarantees that data pointer is only
|
||||||
|
|
|
@ -35,6 +35,7 @@ int main(int argc, char ** argv) {
|
||||||
connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||||
printf("Parsing %s, connect %d\n", argv[2], connect);
|
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||||
|
|
||||||
|
http_parser_url_init(&u);
|
||||||
result = http_parser_parse_url(argv[2], len, connect, &u);
|
result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
printf("Parse error : %d\n", result);
|
printf("Parse error : %d\n", result);
|
||||||
|
@ -43,4 +44,4 @@ int main(int argc, char ** argv) {
|
||||||
printf("Parse ok, result : \n");
|
printf("Parse ok, result : \n");
|
||||||
dump_url(argv[2], &u);
|
dump_url(argv[2], &u);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -965,7 +965,7 @@ reexecute:
|
||||||
case 'D': parser->method = HTTP_DELETE; break;
|
case 'D': parser->method = HTTP_DELETE; break;
|
||||||
case 'G': parser->method = HTTP_GET; break;
|
case 'G': parser->method = HTTP_GET; break;
|
||||||
case 'H': parser->method = HTTP_HEAD; break;
|
case 'H': parser->method = HTTP_HEAD; break;
|
||||||
case 'L': parser->method = HTTP_LOCK; break;
|
case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
|
||||||
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
|
case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
|
||||||
case 'N': parser->method = HTTP_NOTIFY; break;
|
case 'N': parser->method = HTTP_NOTIFY; break;
|
||||||
case 'O': parser->method = HTTP_OPTIONS; break;
|
case 'O': parser->method = HTTP_OPTIONS; break;
|
||||||
|
@ -975,7 +975,7 @@ reexecute:
|
||||||
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
|
||||||
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
|
case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
|
||||||
case 'T': parser->method = HTTP_TRACE; break;
|
case 'T': parser->method = HTTP_TRACE; break;
|
||||||
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND */ break;
|
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
|
||||||
default:
|
default:
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -1038,16 +1038,25 @@ reexecute:
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
} else if (parser->index == 1 && parser->method == HTTP_POST) {
|
} else if (parser->index == 1) {
|
||||||
if (ch == 'R') {
|
if (parser->method == HTTP_POST) {
|
||||||
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
if (ch == 'R') {
|
||||||
} else if (ch == 'U') {
|
parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
|
||||||
parser->method = HTTP_PUT; /* or HTTP_PURGE */
|
} else if (ch == 'U') {
|
||||||
} else if (ch == 'A') {
|
parser->method = HTTP_PUT; /* or HTTP_PURGE */
|
||||||
parser->method = HTTP_PATCH;
|
} else if (ch == 'A') {
|
||||||
} else {
|
parser->method = HTTP_PATCH;
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
} else {
|
||||||
goto error;
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} else if (parser->method == HTTP_LOCK) {
|
||||||
|
if (ch == 'I') {
|
||||||
|
parser->method = HTTP_LINK;
|
||||||
|
} else {
|
||||||
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (parser->index == 2) {
|
} else if (parser->index == 2) {
|
||||||
if (parser->method == HTTP_PUT) {
|
if (parser->method == HTTP_PUT) {
|
||||||
|
@ -1072,6 +1081,8 @@ reexecute:
|
||||||
}
|
}
|
||||||
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
|
||||||
parser->method = HTTP_PROPPATCH;
|
parser->method = HTTP_PROPPATCH;
|
||||||
|
} else if (parser->index == 3 && parser->method == HTTP_UNLOCK && ch == 'I') {
|
||||||
|
parser->method = HTTP_UNLINK;
|
||||||
} else {
|
} else {
|
||||||
SET_ERRNO(HPE_INVALID_METHOD);
|
SET_ERRNO(HPE_INVALID_METHOD);
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -2339,6 +2350,11 @@ http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_parser_url_init(struct http_parser_url *u) {
|
||||||
|
memset(u, 0, sizeof(*u));
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
|
||||||
struct http_parser_url *u)
|
struct http_parser_url *u)
|
||||||
|
|
|
@ -26,7 +26,7 @@ extern "C" {
|
||||||
|
|
||||||
/* Also update SONAME in the Makefile whenever you change these. */
|
/* Also update SONAME in the Makefile whenever you change these. */
|
||||||
#define HTTP_PARSER_VERSION_MAJOR 2
|
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||||
#define HTTP_PARSER_VERSION_MINOR 5
|
#define HTTP_PARSER_VERSION_MINOR 6
|
||||||
#define HTTP_PARSER_VERSION_PATCH 0
|
#define HTTP_PARSER_VERSION_PATCH 0
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
@ -124,6 +124,9 @@ typedef int (*http_cb) (http_parser*);
|
||||||
XX(29, PURGE, PURGE) \
|
XX(29, PURGE, PURGE) \
|
||||||
/* CalDAV */ \
|
/* CalDAV */ \
|
||||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||||
|
/* RFC-2068, section 19.6.1.2 */ \
|
||||||
|
XX(31, LINK, LINK) \
|
||||||
|
XX(32, UNLINK, UNLINK) \
|
||||||
|
|
||||||
enum http_method
|
enum http_method
|
||||||
{
|
{
|
||||||
|
@ -149,7 +152,7 @@ enum flags
|
||||||
|
|
||||||
|
|
||||||
/* Map for errno-related constants
|
/* Map for errno-related constants
|
||||||
*
|
*
|
||||||
* The provided argument should be a macro that takes 2 arguments.
|
* The provided argument should be a macro that takes 2 arguments.
|
||||||
*/
|
*/
|
||||||
#define HTTP_ERRNO_MAP(XX) \
|
#define HTTP_ERRNO_MAP(XX) \
|
||||||
|
@ -330,6 +333,9 @@ const char *http_errno_name(enum http_errno err);
|
||||||
/* Return a string description of the given error */
|
/* Return a string description of the given error */
|
||||||
const char *http_errno_description(enum http_errno err);
|
const char *http_errno_description(enum http_errno err);
|
||||||
|
|
||||||
|
/* Initialize all http_parser_url members to 0 */
|
||||||
|
void http_parser_url_init(struct http_parser_url *u);
|
||||||
|
|
||||||
/* Parse a URL; return nonzero on failure */
|
/* Parse a URL; return nonzero on failure */
|
||||||
int http_parser_parse_url(const char *buf, size_t buflen,
|
int http_parser_parse_url(const char *buf, size_t buflen,
|
||||||
int is_connect,
|
int is_connect,
|
||||||
|
|
|
@ -1101,6 +1101,58 @@ const struct message requests[] =
|
||||||
,.body= ""
|
,.body= ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Examples from the Internet draft for LINK/UNLINK methods:
|
||||||
|
* https://tools.ietf.org/id/draft-snell-link-method-01.html#rfc.section.5
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LINK_REQUEST 40
|
||||||
|
, {.name = "link request"
|
||||||
|
,.type= HTTP_REQUEST
|
||||||
|
,.raw= "LINK /images/my_dog.jpg HTTP/1.1\r\n"
|
||||||
|
"Host: example.com\r\n"
|
||||||
|
"Link: <http://example.com/profiles/joe>; rel=\"tag\"\r\n"
|
||||||
|
"Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.method= HTTP_LINK
|
||||||
|
,.request_path= "/images/my_dog.jpg"
|
||||||
|
,.request_url= "/images/my_dog.jpg"
|
||||||
|
,.query_string= ""
|
||||||
|
,.fragment= ""
|
||||||
|
,.num_headers= 3
|
||||||
|
,.headers= { { "Host", "example.com" }
|
||||||
|
, { "Link", "<http://example.com/profiles/joe>; rel=\"tag\"" }
|
||||||
|
, { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
|
||||||
|
}
|
||||||
|
,.body= ""
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UNLINK_REQUEST 41
|
||||||
|
, {.name = "link request"
|
||||||
|
,.type= HTTP_REQUEST
|
||||||
|
,.raw= "UNLINK /images/my_dog.jpg HTTP/1.1\r\n"
|
||||||
|
"Host: example.com\r\n"
|
||||||
|
"Link: <http://example.com/profiles/sally>; rel=\"tag\"\r\n"
|
||||||
|
"\r\n"
|
||||||
|
,.should_keep_alive= TRUE
|
||||||
|
,.message_complete_on_eof= FALSE
|
||||||
|
,.http_major= 1
|
||||||
|
,.http_minor= 1
|
||||||
|
,.method= HTTP_UNLINK
|
||||||
|
,.request_path= "/images/my_dog.jpg"
|
||||||
|
,.request_url= "/images/my_dog.jpg"
|
||||||
|
,.query_string= ""
|
||||||
|
,.fragment= ""
|
||||||
|
,.num_headers= 2
|
||||||
|
,.headers= { { "Host", "example.com" }
|
||||||
|
, { "Link", "<http://example.com/profiles/sally>; rel=\"tag\"" }
|
||||||
|
}
|
||||||
|
,.body= ""
|
||||||
|
}
|
||||||
|
|
||||||
, {.name= NULL } /* sentinel */
|
, {.name= NULL } /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3760,6 +3812,8 @@ main (void)
|
||||||
"PATCH",
|
"PATCH",
|
||||||
"PURGE",
|
"PURGE",
|
||||||
"MKCALENDAR",
|
"MKCALENDAR",
|
||||||
|
"LINK",
|
||||||
|
"UNLINK",
|
||||||
0 };
|
0 };
|
||||||
const char **this_method;
|
const char **this_method;
|
||||||
for (this_method = all_methods; *this_method; this_method++) {
|
for (this_method = all_methods; *this_method; this_method++) {
|
||||||
|
|
Loading…
Reference in New Issue