Commit | Line | Data |
---|---|---|
2340bcd3 JVH |
1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. |
2 | * | |
3 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
4 | * of this software and associated documentation files (the "Software"), to | |
5 | * deal in the Software without restriction, including without limitation the | |
6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | |
7 | * sell copies of the Software, and to permit persons to whom the Software is | |
8 | * furnished to do so, subject to the following conditions: | |
9 | * | |
10 | * The above copyright notice and this permission notice shall be included in | |
11 | * all copies or substantial portions of the Software. | |
12 | * | |
13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
19 | * IN THE SOFTWARE. | |
20 | */ | |
21 | #ifndef http_parser_h | |
22 | #define http_parser_h | |
23 | #ifdef __cplusplus | |
24 | extern "C" { | |
25 | #endif | |
26 | ||
27 | #define HTTP_PARSER_VERSION_MAJOR 1 | |
28 | #define HTTP_PARSER_VERSION_MINOR 0 | |
29 | ||
30 | #include <sys/types.h> | |
31 | #if defined(_WIN32) && !defined(__MINGW32__) && !defined(_MSC_VER) | |
32 | typedef __int8 int8_t; | |
33 | typedef unsigned __int8 uint8_t; | |
34 | typedef __int16 int16_t; | |
35 | typedef unsigned __int16 uint16_t; | |
36 | typedef __int32 int32_t; | |
37 | typedef unsigned __int32 uint32_t; | |
38 | typedef __int64 int64_t; | |
39 | typedef unsigned __int64 uint64_t; | |
40 | ||
41 | typedef unsigned int size_t; | |
42 | typedef int ssize_t; | |
43 | #else | |
44 | #include <stdint.h> | |
45 | #endif | |
46 | ||
47 | /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run | |
48 | * faster | |
49 | */ | |
50 | #ifndef HTTP_PARSER_STRICT | |
51 | # define HTTP_PARSER_STRICT 0 | |
52 | #endif | |
53 | ||
54 | /* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to | |
55 | * the error reporting facility. | |
56 | */ | |
57 | #ifndef HTTP_PARSER_DEBUG | |
58 | # define HTTP_PARSER_DEBUG 0 | |
59 | #endif | |
60 | ||
61 | ||
62 | /* Maximium header size allowed */ | |
63 | #define HTTP_MAX_HEADER_SIZE (80*1024) | |
64 | ||
65 | ||
66 | typedef struct http_parser http_parser; | |
67 | typedef struct http_parser_settings http_parser_settings; | |
68 | typedef struct http_parser_result http_parser_result; | |
69 | ||
70 | ||
71 | /* Callbacks should return non-zero to indicate an error. The parser will | |
72 | * then halt execution. | |
73 | * | |
74 | * The one exception is on_headers_complete. In a HTTP_RESPONSE parser | |
75 | * returning '1' from on_headers_complete will tell the parser that it | |
76 | * should not expect a body. This is used when receiving a response to a | |
77 | * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: | |
78 | * chunked' headers that indicate the presence of a body. | |
79 | * | |
80 | * http_data_cb does not return data chunks. It will be call arbitrarally | |
81 | * many times for each string. E.G. you might get 10 callbacks for "on_path" | |
82 | * each providing just a few characters more data. | |
83 | */ | |
84 | typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); | |
85 | typedef int (*http_cb) (http_parser*); | |
86 | ||
87 | ||
88 | /* Request Methods */ | |
89 | enum http_method | |
90 | { HTTP_DELETE = 0 | |
91 | , HTTP_GET | |
92 | , HTTP_HEAD | |
93 | , HTTP_POST | |
94 | , HTTP_PUT | |
95 | /* pathological */ | |
96 | , HTTP_CONNECT | |
97 | , HTTP_OPTIONS | |
98 | , HTTP_TRACE | |
99 | /* webdav */ | |
100 | , HTTP_COPY | |
101 | , HTTP_LOCK | |
102 | , HTTP_MKCOL | |
103 | , HTTP_MOVE | |
104 | , HTTP_PROPFIND | |
105 | , HTTP_PROPPATCH | |
106 | , HTTP_UNLOCK | |
107 | /* subversion */ | |
108 | , HTTP_REPORT | |
109 | , HTTP_MKACTIVITY | |
110 | , HTTP_CHECKOUT | |
111 | , HTTP_MERGE | |
112 | /* upnp */ | |
113 | , HTTP_MSEARCH | |
114 | , HTTP_NOTIFY | |
115 | , HTTP_SUBSCRIBE | |
116 | , HTTP_UNSUBSCRIBE | |
117 | /* RFC-5789 */ | |
118 | , HTTP_PATCH | |
119 | /* RTSP RFC 2326 */ | |
120 | , HTTP_DESCRIBE | |
121 | , HTTP_ANNOUNCE | |
122 | , HTTP_SETUP | |
123 | , HTTP_PLAY | |
124 | , HTTP_PAUSE | |
125 | , HTTP_TEARDOWN | |
126 | , HTTP_GET_PARAMETER | |
127 | , HTTP_SET_PARAMETER | |
128 | , HTTP_REDIRECT | |
129 | , HTTP_RECORD | |
130 | /* RAOP FLUSH */ | |
131 | , HTTP_FLUSH | |
132 | }; | |
133 | ||
134 | ||
135 | enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; | |
136 | ||
137 | ||
138 | /* Flag values for http_parser.flags field */ | |
139 | enum flags | |
140 | { F_CHUNKED = 1 << 0 | |
141 | , F_CONNECTION_KEEP_ALIVE = 1 << 1 | |
142 | , F_CONNECTION_CLOSE = 1 << 2 | |
143 | , F_TRAILING = 1 << 3 | |
144 | , F_UPGRADE = 1 << 4 | |
145 | , F_SKIPBODY = 1 << 5 | |
146 | }; | |
147 | ||
148 | ||
149 | /* Map for errno-related constants | |
150 | * | |
151 | * The provided argument should be a macro that takes 2 arguments. | |
152 | */ | |
153 | #define HTTP_ERRNO_MAP(XX) \ | |
154 | /* No error */ \ | |
155 | XX(OK, "success") \ | |
156 | \ | |
157 | /* Callback-related errors */ \ | |
158 | XX(CB_message_begin, "the on_message_begin callback failed") \ | |
159 | XX(CB_path, "the on_path callback failed") \ | |
160 | XX(CB_query_string, "the on_query_string callback failed") \ | |
161 | XX(CB_url, "the on_url callback failed") \ | |
162 | XX(CB_fragment, "the on_fragment callback failed") \ | |
163 | XX(CB_header_field, "the on_header_field callback failed") \ | |
164 | XX(CB_header_value, "the on_header_value callback failed") \ | |
165 | XX(CB_headers_complete, "the on_headers_complete callback failed") \ | |
166 | XX(CB_body, "the on_body callback failed") \ | |
167 | XX(CB_message_complete, "the on_message_complete callback failed") \ | |
168 | \ | |
169 | /* Parsing-related errors */ \ | |
170 | XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ | |
171 | XX(HEADER_OVERFLOW, \ | |
172 | "too many header bytes seen; overflow detected") \ | |
173 | XX(CLOSED_CONNECTION, \ | |
174 | "data received after completed connection: close message") \ | |
175 | XX(INVALID_VERSION, "invalid HTTP version") \ | |
176 | XX(INVALID_STATUS, "invalid HTTP status code") \ | |
177 | XX(INVALID_METHOD, "invalid HTTP method") \ | |
178 | XX(INVALID_URL, "invalid URL") \ | |
179 | XX(INVALID_HOST, "invalid host") \ | |
180 | XX(INVALID_PORT, "invalid port") \ | |
181 | XX(INVALID_PATH, "invalid path") \ | |
182 | XX(INVALID_QUERY_STRING, "invalid query string") \ | |
183 | XX(INVALID_FRAGMENT, "invalid fragment") \ | |
184 | XX(LF_EXPECTED, "LF character expected") \ | |
185 | XX(INVALID_HEADER_TOKEN, "invalid character in header") \ | |
186 | XX(INVALID_CONTENT_LENGTH, \ | |
187 | "invalid character in content-length header") \ | |
188 | XX(INVALID_CHUNK_SIZE, \ | |
189 | "invalid character in chunk size header") \ | |
190 | XX(INVALID_CONSTANT, "invalid constant string") \ | |
191 | XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ | |
192 | XX(STRICT, "strict mode assertion failed") \ | |
193 | XX(UNKNOWN, "an unknown error occurred") | |
194 | ||
195 | ||
196 | /* Define HPE_* values for each errno value above */ | |
197 | #define HTTP_ERRNO_GEN(n, s) HPE_##n, | |
198 | enum http_errno { | |
199 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) | |
200 | }; | |
201 | #undef HTTP_ERRNO_GEN | |
202 | ||
203 | ||
204 | /* Get an http_errno value from an http_parser */ | |
205 | #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) | |
206 | ||
207 | /* Get the line number that generated the current error */ | |
208 | #if HTTP_PARSER_DEBUG | |
209 | #define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno) | |
210 | #else | |
211 | #define HTTP_PARSER_ERRNO_LINE(p) 0 | |
212 | #endif | |
213 | ||
214 | ||
215 | struct http_parser { | |
216 | /** PRIVATE **/ | |
217 | unsigned char numerichost; | |
218 | unsigned char type : 2; | |
219 | unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */ | |
220 | unsigned char state; | |
221 | unsigned char header_state; | |
222 | unsigned char index; | |
223 | ||
224 | uint32_t nread; | |
225 | int64_t content_length; | |
226 | ||
227 | /** READ-ONLY **/ | |
228 | unsigned short http_major; | |
229 | unsigned short http_minor; | |
230 | unsigned short status_code; /* responses only */ | |
231 | unsigned char method; /* requests only */ | |
232 | unsigned char http_errno : 7; | |
233 | ||
234 | /* 1 = Upgrade header was present and the parser has exited because of that. | |
235 | * 0 = No upgrade header present. | |
236 | * Should be checked when http_parser_execute() returns in addition to | |
237 | * error checking. | |
238 | */ | |
239 | unsigned char upgrade : 1; | |
240 | ||
241 | #if HTTP_PARSER_DEBUG | |
242 | uint32_t error_lineno; | |
243 | #endif | |
244 | ||
245 | /** PUBLIC **/ | |
246 | void *data; /* A pointer to get hook to the "connection" or "socket" object */ | |
247 | }; | |
248 | ||
249 | ||
250 | struct http_parser_settings { | |
251 | http_cb on_message_begin; | |
252 | http_data_cb on_url; | |
253 | http_data_cb on_header_field; | |
254 | http_data_cb on_header_value; | |
255 | http_cb on_headers_complete; | |
256 | http_data_cb on_body; | |
257 | http_cb on_message_complete; | |
258 | }; | |
259 | ||
260 | ||
261 | void http_parser_init(http_parser *parser, enum http_parser_type type, char numerichost); | |
262 | ||
263 | ||
264 | size_t http_parser_execute(http_parser *parser, | |
265 | const http_parser_settings *settings, | |
266 | const char *data, | |
267 | size_t len); | |
268 | ||
269 | ||
270 | /* If http_should_keep_alive() in the on_headers_complete or | |
271 | * on_message_complete callback returns true, then this will be should be | |
272 | * the last message on the connection. | |
273 | * If you are the server, respond with the "Connection: close" header. | |
274 | * If you are the client, close the connection. | |
275 | */ | |
276 | int http_should_keep_alive(http_parser *parser); | |
277 | ||
278 | /* Returns a string version of the HTTP method. */ | |
279 | const char *http_method_str(enum http_method m); | |
280 | ||
281 | /* Return a string name of the given error */ | |
282 | const char *http_errno_name(enum http_errno err); | |
283 | ||
284 | /* Return a string description of the given error */ | |
285 | const char *http_errno_description(enum http_errno err); | |
286 | ||
287 | #ifdef __cplusplus | |
288 | } | |
289 | #endif | |
290 | #endif |