Browse Source

Upgrade http-parser; supports webdav extension methods now

v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
c3e0a4bc7c
  1. 2
      deps/http_parser/README.md
  2. 358
      deps/http_parser/http_parser.c
  3. 21
      deps/http_parser/http_parser.h
  4. 10
      src/node_http.cc

2
deps/http_parser/README.md

@ -4,7 +4,7 @@ HTTP Parser
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
it can be interrupted at anytime. It only requires about 136 bytes of data
per message stream (in a web server that is per connection).
Features:

358
deps/http_parser/http_parser.c

@ -24,6 +24,7 @@
#include <http_parser.h>
#include <stdint.h>
#include <assert.h>
#include <string.h> /* strncmp */
#ifndef NULL
# define NULL ((void*)0)
@ -145,6 +146,8 @@ static const uint32_t usual[] = {
0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
};
#define USUAL(c) (usual[c >> 5] & (1 << (c & 0x1f)))
enum state
{ s_dead = 1 /* important that this is > 0 */
@ -163,20 +166,8 @@ enum state
, s_res_line_almost_done
, s_start_req
, s_req_method_G
, s_req_method_GE
, s_req_method_P
, s_req_method_PU
, s_req_method_PO
, s_req_method_POS
, s_req_method_H
, s_req_method_HE
, s_req_method_HEA
, s_req_method_D
, s_req_method_DE
, s_req_method_DEL
, s_req_method_DELE
, s_req_method_DELET
, s_req_method
, s_req_spaces_before_url
, s_req_schema
, s_req_schema_slash
@ -251,13 +242,12 @@ enum flags
, F_TRAILING = 0x0010
};
#define ERROR (p - data)
#define CR '\r'
#define LF '\n'
#define LOWER(c) (unsigned char)(c | 0x20)
#if HTTP_PARSER_STRICT
# define STRICT_CHECK(cond) if (cond) return ERROR
# define STRICT_CHECK(cond) if (cond) goto error
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
#else
# define STRICT_CHECK(cond)
@ -273,7 +263,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
enum state state = parser->state;
enum header_states header_state = parser->header_state;
size_t header_index = parser->header_index;
size_t index = parser->index;
if (len == 0) {
if (state == s_body_identity_eof) {
@ -297,7 +287,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* this state is used after a 'Connection: close' message
* the parser will error out if it reads another message
*/
return ERROR;
goto error;
case s_start_res:
{
@ -316,7 +306,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
default:
return ERROR;
goto error;
}
break;
}
@ -342,7 +332,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
case s_res_first_http_major:
if (ch < '1' || ch > '9') return ERROR;
if (ch < '1' || ch > '9') goto error;
parser->http_major = ch - '0';
state = s_res_http_major;
break;
@ -355,18 +345,18 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_major *= 10;
parser->http_major += ch - '0';
if (parser->http_major > 999) return ERROR;
if (parser->http_major > 999) goto error;
break;
}
/* first digit of minor HTTP version */
case s_res_first_http_minor:
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_minor = ch - '0';
state = s_res_http_minor;
break;
@ -379,12 +369,12 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (parser->http_minor > 999) return ERROR;
if (parser->http_minor > 999) goto error;
break;
}
@ -394,7 +384,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
if (ch == ' ') {
break;
}
return ERROR;
goto error;
}
parser->status_code = ch - '0';
state = s_res_status_code;
@ -415,7 +405,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_header_field_start;
break;
default:
return ERROR;
goto error;
}
break;
}
@ -423,7 +413,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
parser->status_code *= 10;
parser->status_code += ch - '0';
if (parser->status_code > 999) return ERROR;
if (parser->status_code > 999) goto error;
break;
}
@ -453,135 +443,128 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
CALLBACK2(message_begin);
switch (ch) {
/* GET */
case 'G':
state = s_req_method_G;
break;
if (ch < 'A' || 'Z' < ch) goto error;
/* POST, PUT */
case 'P':
state = s_req_method_P;
break;
parser->method = 0;
index = 0;
parser->buffer[0] = ch;
state = s_req_method;
break;
}
/* HEAD */
case 'H':
state = s_req_method_H;
break;
case s_req_method:
if (ch == ' ') {
assert(index+1 < HTTP_PARSER_MAX_METHOD_LEN);
parser->buffer[index+1] = '\0';
/* DELETE */
case 'D':
state = s_req_method_D;
break;
/* TODO Instead of using strncmp() use NGINX's ngx_str3Ocmp() */
case CR:
case LF:
break;
switch (index+1) {
case 3:
if (strncmp(parser->buffer, "GET", 3) == 0) {
parser->method = HTTP_GET;
break;
}
default:
return ERROR;
}
break;
}
if (strncmp(parser->buffer, "PUT", 3) == 0) {
parser->method = HTTP_PUT;
break;
}
/* GET */
break;
case s_req_method_G:
STRICT_CHECK(ch != 'E');
state = s_req_method_GE;
break;
case 4:
if (strncmp(parser->buffer, "POST", 4) == 0) {
parser->method = HTTP_POST;
break;
}
case s_req_method_GE:
STRICT_CHECK(ch != 'T');
parser->method = HTTP_GET;
state = s_req_spaces_before_url;
break;
if (strncmp(parser->buffer, "HEAD", 4) == 0) {
parser->method = HTTP_HEAD;
break;
}
/* HEAD */
if (strncmp(parser->buffer, "COPY", 4) == 0) {
parser->method = HTTP_COPY;
break;
}
case s_req_method_H:
STRICT_CHECK(ch != 'E');
state = s_req_method_HE;
break;
if (strncmp(parser->buffer, "MOVE", 4) == 0) {
parser->method = HTTP_MOVE;
break;
}
case s_req_method_HE:
STRICT_CHECK(ch != 'A');
state = s_req_method_HEA;
break;
break;
case s_req_method_HEA:
STRICT_CHECK(ch != 'D');
parser->method = HTTP_HEAD;
state = s_req_spaces_before_url;
break;
case 5:
if (strncmp(parser->buffer, "MKCOL", 5) == 0) {
parser->method = HTTP_MKCOL;
break;
}
if (strncmp(parser->buffer, "TRACE", 5) == 0) {
parser->method = HTTP_TRACE;
break;
}
/* POST, PUT */
break;
case s_req_method_P:
switch (ch) {
case 'O':
state = s_req_method_PO;
break;
case 6:
if (strncmp(parser->buffer, "DELETE", 6) == 0) {
parser->method = HTTP_DELETE;
break;
}
case 'U':
state = s_req_method_PU;
break;
if (strncmp(parser->buffer, "UNLOCK", 6) == 0) {
parser->method = HTTP_UNLOCK;
break;
}
default:
return ERROR;
}
break;
break;
/* PUT */
case 7:
if (strncmp(parser->buffer, "OPTIONS", 7) == 0) {
parser->method = HTTP_OPTIONS;
break;
}
case s_req_method_PU:
STRICT_CHECK(ch != 'T');
parser->method = HTTP_PUT;
state = s_req_spaces_before_url;
break;
if (strncmp(parser->buffer, "CONNECT", 7) == 0) {
parser->method = HTTP_CONNECT;
break;
}
/* POST */
break;
case s_req_method_PO:
STRICT_CHECK(ch != 'S');
state = s_req_method_POS;
break;
case 8:
if (strncmp(parser->buffer, "PROPFIND", 8) == 0) {
parser->method = HTTP_PROPFIND;
break;
}
case s_req_method_POS:
STRICT_CHECK(ch != 'T');
parser->method = HTTP_POST;
state = s_req_spaces_before_url;
break;
break;
/* DELETE */
case 9:
if (strncmp(parser->buffer, "PROPPATCH", 9) == 0) {
parser->method = HTTP_PROPPATCH;
break;
}
case s_req_method_D:
STRICT_CHECK(ch != 'E');
state = s_req_method_DE;
break;
break;
}
state = s_req_spaces_before_url;
break;
}
case s_req_method_DE:
STRICT_CHECK(ch != 'L');
state = s_req_method_DEL;
break;
if (ch < 'A' || 'Z' < ch) goto error;
case s_req_method_DEL:
STRICT_CHECK(ch != 'E');
state = s_req_method_DELE;
break;
if (++index >= HTTP_PARSER_MAX_METHOD_LEN - 1) {
goto error;
}
case s_req_method_DELE:
STRICT_CHECK(ch != 'T');
state = s_req_method_DELET;
break;
parser->buffer[index] = ch;
case s_req_method_DELET:
STRICT_CHECK(ch != 'E');
parser->method = HTTP_DELETE;
state = s_req_spaces_before_url;
break;
case s_req_spaces_before_url:
{
if (ch == ' ') break;
@ -601,7 +584,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
return ERROR;
goto error;
}
case s_req_schema:
@ -615,7 +598,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
return ERROR;
goto error;
}
case s_req_schema_slash:
@ -650,7 +633,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_req_http_start;
break;
default:
return ERROR;
goto error;
}
break;
}
@ -672,14 +655,14 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_req_http_start;
break;
default:
return ERROR;
goto error;
}
break;
}
case s_req_path:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) break;
if (USUAL(ch)) break;
switch (ch) {
case ' ':
@ -708,14 +691,14 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_req_fragment_start;
break;
default:
return ERROR;
goto error;
}
break;
}
case s_req_query_string_start:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
if (USUAL(ch)) {
MARK(query_string);
state = s_req_query_string;
break;
@ -742,14 +725,14 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_req_fragment_start;
break;
default:
return ERROR;
goto error;
}
break;
}
case s_req_query_string:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) break;
if (USUAL(ch)) break;
switch (ch) {
case ' ':
@ -774,14 +757,14 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_req_fragment_start;
break;
default:
return ERROR;
goto error;
}
break;
}
case s_req_fragment_start:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
if (USUAL(ch)) {
MARK(fragment);
state = s_req_fragment;
break;
@ -809,14 +792,14 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
case '#':
break;
default:
return ERROR;
goto error;
}
break;
}
case s_req_fragment:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) break;
if (USUAL(ch)) break;
switch (ch) {
case ' ':
@ -840,7 +823,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
case '#':
break;
default:
return ERROR;
goto error;
}
break;
}
@ -853,7 +836,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
case ' ':
break;
default:
return ERROR;
goto error;
}
break;
@ -879,7 +862,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* first digit of major HTTP version */
case s_req_first_http_major:
if (ch < '1' || ch > '9') return ERROR;
if (ch < '1' || ch > '9') goto error;
parser->http_major = ch - '0';
state = s_req_http_major;
break;
@ -892,18 +875,18 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_major *= 10;
parser->http_major += ch - '0';
if (parser->http_major > 999) return ERROR;
if (parser->http_major > 999) goto error;
break;
}
/* first digit of minor HTTP version */
case s_req_first_http_minor:
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_minor = ch - '0';
state = s_req_http_minor;
break;
@ -923,19 +906,19 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* XXX allow spaces after digit? */
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (parser->http_minor > 999) return ERROR;
if (parser->http_minor > 999) goto error;
break;
}
/* end of request line */
case s_req_line_almost_done:
{
if (ch != LF) return ERROR;
if (ch != LF) goto error;
state = s_header_field_start;
break;
}
@ -954,11 +937,11 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
c = LOWER(ch);
if (c < 'a' || 'z' < c) return ERROR;
if (c < 'a' || 'z' < c) goto error;
MARK(header_field);
header_index = 0;
index = 0;
state = s_header_field;
switch (c) {
@ -987,17 +970,17 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
case h_C:
header_index++;
index++;
header_state = (c == 'o' ? h_CO : h_general);
break;
case h_CO:
header_index++;
index++;
header_state = (c == 'n' ? h_CON : h_general);
break;
case h_CON:
header_index++;
index++;
switch (c) {
case 'n':
header_state = h_matching_connection;
@ -1014,11 +997,11 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* connection */
case h_matching_connection:
header_index++;
if (header_index > sizeof(CONNECTION)-1
|| c != CONNECTION[header_index]) {
index++;
if (index > sizeof(CONNECTION)-1
|| c != CONNECTION[index]) {
header_state = h_general;
} else if (header_index == sizeof(CONNECTION)-2) {
} else if (index == sizeof(CONNECTION)-2) {
header_state = h_connection;
}
break;
@ -1026,11 +1009,11 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* content-length */
case h_matching_content_length:
header_index++;
if (header_index > sizeof(CONTENT_LENGTH)-1
|| c != CONTENT_LENGTH[header_index]) {
index++;
if (index > sizeof(CONTENT_LENGTH)-1
|| c != CONTENT_LENGTH[index]) {
header_state = h_general;
} else if (header_index == sizeof(CONTENT_LENGTH)-2) {
} else if (index == sizeof(CONTENT_LENGTH)-2) {
header_state = h_content_length;
}
break;
@ -1038,11 +1021,11 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
/* transfer-encoding */
case h_matching_transfer_encoding:
header_index++;
if (header_index > sizeof(TRANSFER_ENCODING)-1
|| c != TRANSFER_ENCODING[header_index]) {
index++;
if (index > sizeof(TRANSFER_ENCODING)-1
|| c != TRANSFER_ENCODING[index]) {
header_state = h_general;
} else if (header_index == sizeof(TRANSFER_ENCODING)-2) {
} else if (index == sizeof(TRANSFER_ENCODING)-2) {
header_state = h_transfer_encoding;
}
break;
@ -1078,7 +1061,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
}
return ERROR;
goto error;
}
case s_header_value_start:
@ -1088,7 +1071,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
MARK(header_value);
state = s_header_value;
header_index = 0;
index = 0;
c = lowcase[(int)ch];
@ -1119,7 +1102,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
case h_content_length:
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->content_length = ch - '0';
break;
@ -1171,39 +1154,39 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
break;
case h_content_length:
if (ch < '0' || ch > '9') return ERROR;
if (ch < '0' || ch > '9') goto error;
parser->content_length *= 10;
parser->content_length += ch - '0';
break;
/* Transfer-Encoding: chunked */
case h_matching_transfer_encoding_chunked:
header_index++;
if (header_index > sizeof(CHUNKED)-1
|| c != CHUNKED[header_index]) {
index++;
if (index > sizeof(CHUNKED)-1
|| c != CHUNKED[index]) {
header_state = h_general;
} else if (header_index == sizeof(CHUNKED)-2) {
} else if (index == sizeof(CHUNKED)-2) {
header_state = h_transfer_encoding_chunked;
}
break;
/* looking for 'Connection: keep-alive' */
case h_matching_connection_keep_alive:
header_index++;
if (header_index > sizeof(KEEP_ALIVE)-1
|| c != KEEP_ALIVE[header_index]) {
index++;
if (index > sizeof(KEEP_ALIVE)-1
|| c != KEEP_ALIVE[index]) {
header_state = h_general;
} else if (header_index == sizeof(KEEP_ALIVE)-2) {
} else if (index == sizeof(KEEP_ALIVE)-2) {
header_state = h_connection_keep_alive;
}
break;
/* looking for 'Connection: close' */
case h_matching_connection_close:
header_index++;
if (header_index > sizeof(CLOSE)-1 || c != CLOSE[header_index]) {
index++;
if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
header_state = h_general;
} else if (header_index == sizeof(CLOSE)-2) {
} else if (index == sizeof(CLOSE)-2) {
header_state = h_connection_close;
}
break;
@ -1313,7 +1296,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
assert(parser->flags & F_CHUNKED);
c = unhex[(int)ch];
if (c == -1) return ERROR;
if (c == -1) goto error;
parser->content_length = c;
state = s_chunk_size;
break;
@ -1335,7 +1318,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
state = s_chunk_parameters;
break;
}
return ERROR;
goto error;
}
parser->content_length *= 16;
@ -1401,7 +1384,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
default:
assert(0 && "unhandled state");
return ERROR;
goto error;
}
}
@ -1414,9 +1397,12 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state
parser->state = state;
parser->header_state = header_state;
parser->header_index = header_index;
parser->index = index;
return len;
error:
return (p - data);
}

21
deps/http_parser/http_parser.h

@ -50,20 +50,34 @@ typedef struct http_parser http_parser;
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
/* Should be at least one longer than the longest request method */
#define HTTP_PARSER_MAX_METHOD_LEN 10
/* Request Methods */
enum http_method
{ HTTP_DELETE = 0x0002
, HTTP_GET = 0x0004
, HTTP_HEAD = 0x0008
, HTTP_POST = 0x0100
, HTTP_PUT = 0x0800
, HTTP_POST = 0x0010
, HTTP_PUT = 0x0020
/* webdav methods */
, HTTP_COPY = 0x0040
, HTTP_LOCK = 0x0080
, HTTP_MKCOL = 0x0100
, HTTP_MOVE = 0x0200
, HTTP_OPTIONS = 0x0400
, HTTP_PROPFIND = 0x0800
, HTTP_PROPPATCH = 0x1000
, HTTP_TRACE = 0x2000
, HTTP_UNLOCK = 0x4000
, HTTP_CONNECT = 0x8000
};
struct http_parser {
/** PRIVATE **/
unsigned short state;
unsigned short header_state;
size_t header_index;
size_t index;
char flags;
@ -88,6 +102,7 @@ struct http_parser {
enum http_method method; /* requests only */
unsigned short http_major;
unsigned short http_minor;
char buffer[HTTP_PARSER_MAX_METHOD_LEN];
/** PUBLIC **/
void *data; /* A pointer to get hook to the "connection" or "socket" object */

10
src/node_http.cc

@ -210,6 +210,16 @@ GetMethod (int method)
case HTTP_HEAD: s = "HEAD"; break;
case HTTP_POST: s = "POST"; break;
case HTTP_PUT: s = "PUT"; break;
case HTTP_COPY: s = "COPY"; 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_PROPFIND: s = "PROPFIND"; break;
case HTTP_PROPPATCH: s = "PROPPATCH"; break;
case HTTP_TRACE: s = "TRACE"; break;
case HTTP_UNLOCK: s = "UNLOCK"; break;
case HTTP_CONNECT: s = "CONNECT"; break;
}
HandleScope scope;
Local<String> method = String::NewSymbol(s);

Loading…
Cancel
Save