| 1 |
/** |
|---|
| 2 |
* Copyright (c) 2005 Zed A. Shaw |
|---|
| 3 |
* You can redistribute it and/or modify it under the same terms as Ruby. |
|---|
| 4 |
*/ |
|---|
| 5 |
#include "http11_parser.h" |
|---|
| 6 |
#include <stdio.h> |
|---|
| 7 |
#include <assert.h> |
|---|
| 8 |
#include <stdlib.h> |
|---|
| 9 |
#include <ctype.h> |
|---|
| 10 |
#include <string.h> |
|---|
| 11 |
|
|---|
| 12 |
#define LEN(AT, FPC) (FPC - buffer - parser->AT) |
|---|
| 13 |
#define MARK(M,FPC) (parser->M = (FPC) - buffer) |
|---|
| 14 |
#define PTR_TO(F) (buffer + parser->F) |
|---|
| 15 |
|
|---|
| 16 |
/** machine **/ |
|---|
| 17 |
%%{ |
|---|
| 18 |
machine http_parser; |
|---|
| 19 |
|
|---|
| 20 |
action mark {MARK(mark, fpc); } |
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
action start_field { MARK(field_start, fpc); } |
|---|
| 24 |
action write_field { |
|---|
| 25 |
parser->field_len = LEN(field_start, fpc); |
|---|
| 26 |
} |
|---|
| 27 |
|
|---|
| 28 |
action start_value { MARK(mark, fpc); } |
|---|
| 29 |
action write_value { |
|---|
| 30 |
if(parser->http_field != NULL) { |
|---|
| 31 |
parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc)); |
|---|
| 32 |
} |
|---|
| 33 |
} |
|---|
| 34 |
action request_method { |
|---|
| 35 |
if(parser->request_method != NULL) |
|---|
| 36 |
parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc)); |
|---|
| 37 |
} |
|---|
| 38 |
action request_uri { |
|---|
| 39 |
if(parser->request_uri != NULL) |
|---|
| 40 |
parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc)); |
|---|
| 41 |
} |
|---|
| 42 |
|
|---|
| 43 |
action start_query {MARK(query_start, fpc); } |
|---|
| 44 |
action query_string { |
|---|
| 45 |
if(parser->query_string != NULL) |
|---|
| 46 |
parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc)); |
|---|
| 47 |
} |
|---|
| 48 |
|
|---|
| 49 |
action http_version { |
|---|
| 50 |
if(parser->http_version != NULL) |
|---|
| 51 |
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc)); |
|---|
| 52 |
} |
|---|
| 53 |
|
|---|
| 54 |
action request_path { |
|---|
| 55 |
if(parser->request_path != NULL) |
|---|
| 56 |
parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc)); |
|---|
| 57 |
} |
|---|
| 58 |
|
|---|
| 59 |
action done { |
|---|
| 60 |
parser->body_start = fpc - buffer + 1; |
|---|
| 61 |
if(parser->header_done != NULL) |
|---|
| 62 |
parser->header_done(parser->data, fpc + 1, pe - fpc - 1); |
|---|
| 63 |
fbreak; |
|---|
| 64 |
} |
|---|
| 65 |
|
|---|
| 66 |
|
|---|
| 67 |
#### HTTP PROTOCOL GRAMMAR |
|---|
| 68 |
# line endings |
|---|
| 69 |
CRLF = "\r\n"; |
|---|
| 70 |
|
|---|
| 71 |
# character types |
|---|
| 72 |
CTL = (cntrl | 127); |
|---|
| 73 |
safe = ("$" | "-" | "_" | "."); |
|---|
| 74 |
extra = ("!" | "*" | "'" | "(" | ")" | ","); |
|---|
| 75 |
reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+"); |
|---|
| 76 |
unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">"); |
|---|
| 77 |
national = any -- (alpha | digit | reserved | extra | safe | unsafe); |
|---|
| 78 |
unreserved = (alpha | digit | safe | extra | national); |
|---|
| 79 |
escape = ("%" xdigit xdigit); |
|---|
| 80 |
uchar = (unreserved | escape); |
|---|
| 81 |
pchar = (uchar | ":" | "@" | "&" | "=" | "+"); |
|---|
| 82 |
tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t"); |
|---|
| 83 |
|
|---|
| 84 |
# elements |
|---|
| 85 |
token = (ascii -- (CTL | tspecials)); |
|---|
| 86 |
|
|---|
| 87 |
# URI schemes and absolute paths |
|---|
| 88 |
scheme = ( alpha | digit | "+" | "-" | "." )* ; |
|---|
| 89 |
absolute_uri = (scheme ":" (uchar | reserved )*); |
|---|
| 90 |
|
|---|
| 91 |
path = (pchar+ ( "/" pchar* )*) ; |
|---|
| 92 |
query = ( uchar | reserved )* %query_string ; |
|---|
| 93 |
param = ( pchar | "/" )* ; |
|---|
| 94 |
params = (param ( ";" param )*) ; |
|---|
| 95 |
rel_path = (path? %request_path (";" params)?) ("?" %start_query query)?; |
|---|
| 96 |
absolute_path = ("/"+ rel_path); |
|---|
| 97 |
|
|---|
| 98 |
Request_URI = ("*" | absolute_uri | absolute_path) >mark %request_uri; |
|---|
| 99 |
Method = (upper | digit | safe){1,20} >mark %request_method; |
|---|
| 100 |
|
|---|
| 101 |
http_number = (digit+ "." digit+) ; |
|---|
| 102 |
HTTP_Version = ("HTTP/" http_number) >mark %http_version ; |
|---|
| 103 |
Request_Line = (Method " " Request_URI " " HTTP_Version CRLF) ; |
|---|
| 104 |
|
|---|
| 105 |
field_name = (token -- ":")+ >start_field %write_field; |
|---|
| 106 |
|
|---|
| 107 |
field_value = any* >start_value %write_value; |
|---|
| 108 |
|
|---|
| 109 |
message_header = field_name ":" " "* field_value :> CRLF; |
|---|
| 110 |
|
|---|
| 111 |
Request = Request_Line (message_header)* ( CRLF @done); |
|---|
| 112 |
|
|---|
| 113 |
main := Request; |
|---|
| 114 |
}%% |
|---|
| 115 |
|
|---|
| 116 |
/** Data **/ |
|---|
| 117 |
%% write data; |
|---|
| 118 |
|
|---|
| 119 |
int http_parser_init(http_parser *parser) { |
|---|
| 120 |
int cs = 0; |
|---|
| 121 |
%% write init; |
|---|
| 122 |
parser->cs = cs; |
|---|
| 123 |
parser->body_start = 0; |
|---|
| 124 |
parser->content_len = 0; |
|---|
| 125 |
parser->mark = 0; |
|---|
| 126 |
parser->nread = 0; |
|---|
| 127 |
parser->field_len = 0; |
|---|
| 128 |
parser->field_start = 0; |
|---|
| 129 |
|
|---|
| 130 |
return(1); |
|---|
| 131 |
} |
|---|
| 132 |
|
|---|
| 133 |
|
|---|
| 134 |
/** exec **/ |
|---|
| 135 |
size_t http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off) { |
|---|
| 136 |
const char *p, *pe; |
|---|
| 137 |
int cs = parser->cs; |
|---|
| 138 |
|
|---|
| 139 |
assert(off <= len && "offset past end of buffer"); |
|---|
| 140 |
|
|---|
| 141 |
p = buffer+off; |
|---|
| 142 |
pe = buffer+len; |
|---|
| 143 |
|
|---|
| 144 |
assert(*pe == '\0' && "pointer does not end on NUL"); |
|---|
| 145 |
assert(pe - p == len - off && "pointers aren't same distance"); |
|---|
| 146 |
|
|---|
| 147 |
|
|---|
| 148 |
%% write exec; |
|---|
| 149 |
|
|---|
| 150 |
parser->cs = cs; |
|---|
| 151 |
parser->nread += p - (buffer + off); |
|---|
| 152 |
|
|---|
| 153 |
assert(p <= pe && "buffer overflow after parsing execute"); |
|---|
| 154 |
assert(parser->nread <= len && "nread longer than length"); |
|---|
| 155 |
assert(parser->body_start <= len && "body starts after buffer end"); |
|---|
| 156 |
assert(parser->mark < len && "mark is after buffer end"); |
|---|
| 157 |
assert(parser->field_len <= len && "field has length longer than whole buffer"); |
|---|
| 158 |
assert(parser->field_start < len && "field starts after buffer end"); |
|---|
| 159 |
|
|---|
| 160 |
if(parser->body_start) { |
|---|
| 161 |
/* final \r\n combo encountered so stop right here */ |
|---|
| 162 |
%%write eof; |
|---|
| 163 |
parser->nread++; |
|---|
| 164 |
} |
|---|
| 165 |
|
|---|
| 166 |
return(parser->nread); |
|---|
| 167 |
} |
|---|
| 168 |
|
|---|
| 169 |
int http_parser_finish(http_parser *parser) |
|---|
| 170 |
{ |
|---|
| 171 |
int cs = parser->cs; |
|---|
| 172 |
|
|---|
| 173 |
%%write eof; |
|---|
| 174 |
|
|---|
| 175 |
parser->cs = cs; |
|---|
| 176 |
|
|---|
| 177 |
if (http_parser_has_error(parser) ) { |
|---|
| 178 |
return -1; |
|---|
| 179 |
} else if (http_parser_is_finished(parser) ) { |
|---|
| 180 |
return 1; |
|---|
| 181 |
} else { |
|---|
| 182 |
return 0; |
|---|
| 183 |
} |
|---|
| 184 |
} |
|---|
| 185 |
|
|---|
| 186 |
int http_parser_has_error(http_parser *parser) { |
|---|
| 187 |
return parser->cs == http_parser_error; |
|---|
| 188 |
} |
|---|
| 189 |
|
|---|
| 190 |
int http_parser_is_finished(http_parser *parser) { |
|---|
| 191 |
return parser->cs == http_parser_first_final; |
|---|
| 192 |
} |
|---|