http_parser.rb 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,6 +12,7 @@
12
12
  # RuntimeLibrary MUST MATCH across the entire project
13
13
  'Debug': {
14
14
  'defines': [ 'DEBUG', '_DEBUG' ],
15
+ 'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
15
16
  'msvs_settings': {
16
17
  'VCCLCompilerTool': {
17
18
  'RuntimeLibrary': 1, # static debug
@@ -20,6 +21,7 @@
20
21
  },
21
22
  'Release': {
22
23
  'defines': [ 'NDEBUG' ],
24
+ 'cflags': [ '-Wall', '-Wextra', '-O3' ],
23
25
  'msvs_settings': {
24
26
  'VCCLCompilerTool': {
25
27
  'RuntimeLibrary': 0, # static release
@@ -51,6 +53,7 @@
51
53
  'type': 'static_library',
52
54
  'include_dirs': [ '.' ],
53
55
  'direct_dependent_settings': {
56
+ 'defines': [ 'HTTP_PARSER_STRICT=0' ],
54
57
  'include_dirs': [ '.' ],
55
58
  },
56
59
  'defines': [ 'HTTP_PARSER_STRICT=0' ],
@@ -69,11 +72,40 @@
69
72
  },
70
73
 
71
74
  {
72
- 'target_name': 'test',
75
+ 'target_name': 'http_parser_strict',
76
+ 'type': 'static_library',
77
+ 'include_dirs': [ '.' ],
78
+ 'direct_dependent_settings': {
79
+ 'defines': [ 'HTTP_PARSER_STRICT=1' ],
80
+ 'include_dirs': [ '.' ],
81
+ },
82
+ 'defines': [ 'HTTP_PARSER_STRICT=1' ],
83
+ 'sources': [ './http_parser.c', ],
84
+ 'conditions': [
85
+ ['OS=="win"', {
86
+ 'msvs_settings': {
87
+ 'VCCLCompilerTool': {
88
+ # Compile as C++. http_parser.c is actually C99, but C++ is
89
+ # close enough in this case.
90
+ 'CompileAs': 2,
91
+ },
92
+ },
93
+ }]
94
+ ],
95
+ },
96
+
97
+ {
98
+ 'target_name': 'test-nonstrict',
73
99
  'type': 'executable',
74
100
  'dependencies': [ 'http_parser' ],
75
101
  'sources': [ 'test.c' ]
102
+ },
103
+
104
+ {
105
+ 'target_name': 'test-strict',
106
+ 'type': 'executable',
107
+ 'dependencies': [ 'http_parser_strict' ],
108
+ 'sources': [ 'test.c' ]
76
109
  }
77
110
  ]
78
111
  }
79
-
@@ -24,11 +24,15 @@
24
24
  extern "C" {
25
25
  #endif
26
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) || _MSC_VER<1600)
27
+ /* Also update SONAME in the Makefile whenever you change these. */
28
+ #define HTTP_PARSER_VERSION_MAJOR 2
29
+ #define HTTP_PARSER_VERSION_MINOR 8
30
+ #define HTTP_PARSER_VERSION_PATCH 1
31
+
32
+ #include <stddef.h>
33
+ #if defined(_WIN32) && !defined(__MINGW32__) && \
34
+ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
35
+ #include <BaseTsd.h>
32
36
  typedef __int8 int8_t;
33
37
  typedef unsigned __int8 uint8_t;
34
38
  typedef __int16 int16_t;
@@ -37,9 +41,6 @@ typedef __int32 int32_t;
37
41
  typedef unsigned __int32 uint32_t;
38
42
  typedef __int64 int64_t;
39
43
  typedef unsigned __int64 uint64_t;
40
-
41
- typedef unsigned int size_t;
42
- typedef int ssize_t;
43
44
  #else
44
45
  #include <stdint.h>
45
46
  #endif
@@ -51,21 +52,19 @@ typedef int ssize_t;
51
52
  # define HTTP_PARSER_STRICT 1
52
53
  #endif
53
54
 
54
- /* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
55
- * the error reporting facility.
55
+ /* Maximium header size allowed. If the macro is not defined
56
+ * before including this header then the default is used. To
57
+ * change the maximum header size, define the macro in the build
58
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
59
+ * the effective limit on the size of the header, define the macro
60
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
56
61
  */
57
- #ifndef HTTP_PARSER_DEBUG
58
- # define HTTP_PARSER_DEBUG 0
62
+ #ifndef HTTP_MAX_HEADER_SIZE
63
+ # define HTTP_MAX_HEADER_SIZE (80*1024)
59
64
  #endif
60
65
 
61
-
62
- /* Maximium header size allowed */
63
- #define HTTP_MAX_HEADER_SIZE (80*1024)
64
-
65
-
66
66
  typedef struct http_parser http_parser;
67
67
  typedef struct http_parser_settings http_parser_settings;
68
- typedef struct http_parser_result http_parser_result;
69
68
 
70
69
 
71
70
  /* Callbacks should return non-zero to indicate an error. The parser will
@@ -77,46 +76,139 @@ typedef struct http_parser_result http_parser_result;
77
76
  * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
78
77
  * chunked' headers that indicate the presence of a body.
79
78
  *
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"
79
+ * Returning `2` from on_headers_complete will tell parser that it should not
80
+ * expect neither a body nor any futher responses on this connection. This is
81
+ * useful for handling responses to a CONNECT request which may not contain
82
+ * `Upgrade` or `Connection: upgrade` headers.
83
+ *
84
+ * http_data_cb does not return data chunks. It will be called arbitrarily
85
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
82
86
  * each providing just a few characters more data.
83
87
  */
84
88
  typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
85
89
  typedef int (*http_cb) (http_parser*);
86
90
 
87
91
 
92
+ /* Status Codes */
93
+ #define HTTP_STATUS_MAP(XX) \
94
+ XX(100, CONTINUE, Continue) \
95
+ XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
96
+ XX(102, PROCESSING, Processing) \
97
+ XX(200, OK, OK) \
98
+ XX(201, CREATED, Created) \
99
+ XX(202, ACCEPTED, Accepted) \
100
+ XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
101
+ XX(204, NO_CONTENT, No Content) \
102
+ XX(205, RESET_CONTENT, Reset Content) \
103
+ XX(206, PARTIAL_CONTENT, Partial Content) \
104
+ XX(207, MULTI_STATUS, Multi-Status) \
105
+ XX(208, ALREADY_REPORTED, Already Reported) \
106
+ XX(226, IM_USED, IM Used) \
107
+ XX(300, MULTIPLE_CHOICES, Multiple Choices) \
108
+ XX(301, MOVED_PERMANENTLY, Moved Permanently) \
109
+ XX(302, FOUND, Found) \
110
+ XX(303, SEE_OTHER, See Other) \
111
+ XX(304, NOT_MODIFIED, Not Modified) \
112
+ XX(305, USE_PROXY, Use Proxy) \
113
+ XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
114
+ XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
115
+ XX(400, BAD_REQUEST, Bad Request) \
116
+ XX(401, UNAUTHORIZED, Unauthorized) \
117
+ XX(402, PAYMENT_REQUIRED, Payment Required) \
118
+ XX(403, FORBIDDEN, Forbidden) \
119
+ XX(404, NOT_FOUND, Not Found) \
120
+ XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
121
+ XX(406, NOT_ACCEPTABLE, Not Acceptable) \
122
+ XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
123
+ XX(408, REQUEST_TIMEOUT, Request Timeout) \
124
+ XX(409, CONFLICT, Conflict) \
125
+ XX(410, GONE, Gone) \
126
+ XX(411, LENGTH_REQUIRED, Length Required) \
127
+ XX(412, PRECONDITION_FAILED, Precondition Failed) \
128
+ XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
129
+ XX(414, URI_TOO_LONG, URI Too Long) \
130
+ XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
131
+ XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
132
+ XX(417, EXPECTATION_FAILED, Expectation Failed) \
133
+ XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
134
+ XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
135
+ XX(423, LOCKED, Locked) \
136
+ XX(424, FAILED_DEPENDENCY, Failed Dependency) \
137
+ XX(426, UPGRADE_REQUIRED, Upgrade Required) \
138
+ XX(428, PRECONDITION_REQUIRED, Precondition Required) \
139
+ XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
140
+ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
141
+ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
142
+ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
143
+ XX(501, NOT_IMPLEMENTED, Not Implemented) \
144
+ XX(502, BAD_GATEWAY, Bad Gateway) \
145
+ XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
146
+ XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
147
+ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
148
+ XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
149
+ XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
150
+ XX(508, LOOP_DETECTED, Loop Detected) \
151
+ XX(510, NOT_EXTENDED, Not Extended) \
152
+ XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
153
+
154
+ enum http_status
155
+ {
156
+ #define XX(num, name, string) HTTP_STATUS_##name = num,
157
+ HTTP_STATUS_MAP(XX)
158
+ #undef XX
159
+ };
160
+
161
+
88
162
  /* Request Methods */
163
+ #define HTTP_METHOD_MAP(XX) \
164
+ XX(0, DELETE, DELETE) \
165
+ XX(1, GET, GET) \
166
+ XX(2, HEAD, HEAD) \
167
+ XX(3, POST, POST) \
168
+ XX(4, PUT, PUT) \
169
+ /* pathological */ \
170
+ XX(5, CONNECT, CONNECT) \
171
+ XX(6, OPTIONS, OPTIONS) \
172
+ XX(7, TRACE, TRACE) \
173
+ /* WebDAV */ \
174
+ XX(8, COPY, COPY) \
175
+ XX(9, LOCK, LOCK) \
176
+ XX(10, MKCOL, MKCOL) \
177
+ XX(11, MOVE, MOVE) \
178
+ XX(12, PROPFIND, PROPFIND) \
179
+ XX(13, PROPPATCH, PROPPATCH) \
180
+ XX(14, SEARCH, SEARCH) \
181
+ XX(15, UNLOCK, UNLOCK) \
182
+ XX(16, BIND, BIND) \
183
+ XX(17, REBIND, REBIND) \
184
+ XX(18, UNBIND, UNBIND) \
185
+ XX(19, ACL, ACL) \
186
+ /* subversion */ \
187
+ XX(20, REPORT, REPORT) \
188
+ XX(21, MKACTIVITY, MKACTIVITY) \
189
+ XX(22, CHECKOUT, CHECKOUT) \
190
+ XX(23, MERGE, MERGE) \
191
+ /* upnp */ \
192
+ XX(24, MSEARCH, M-SEARCH) \
193
+ XX(25, NOTIFY, NOTIFY) \
194
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
195
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
196
+ /* RFC-5789 */ \
197
+ XX(28, PATCH, PATCH) \
198
+ XX(29, PURGE, PURGE) \
199
+ /* CalDAV */ \
200
+ XX(30, MKCALENDAR, MKCALENDAR) \
201
+ /* RFC-2068, section 19.6.1.2 */ \
202
+ XX(31, LINK, LINK) \
203
+ XX(32, UNLINK, UNLINK) \
204
+ /* icecast */ \
205
+ XX(33, SOURCE, SOURCE) \
206
+
89
207
  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
- , HTTP_PURGE
208
+ {
209
+ #define XX(num, name, string) HTTP_##name = num,
210
+ HTTP_METHOD_MAP(XX)
211
+ #undef XX
120
212
  };
121
213
 
122
214
 
@@ -128,14 +220,16 @@ enum flags
128
220
  { F_CHUNKED = 1 << 0
129
221
  , F_CONNECTION_KEEP_ALIVE = 1 << 1
130
222
  , F_CONNECTION_CLOSE = 1 << 2
131
- , F_TRAILING = 1 << 3
132
- , F_UPGRADE = 1 << 4
133
- , F_SKIPBODY = 1 << 5
223
+ , F_CONNECTION_UPGRADE = 1 << 3
224
+ , F_TRAILING = 1 << 4
225
+ , F_UPGRADE = 1 << 5
226
+ , F_SKIPBODY = 1 << 6
227
+ , F_CONTENTLENGTH = 1 << 7
134
228
  };
135
229
 
136
230
 
137
231
  /* Map for errno-related constants
138
- *
232
+ *
139
233
  * The provided argument should be a macro that takes 2 arguments.
140
234
  */
141
235
  #define HTTP_ERRNO_MAP(XX) \
@@ -150,6 +244,9 @@ enum flags
150
244
  XX(CB_headers_complete, "the on_headers_complete callback failed") \
151
245
  XX(CB_body, "the on_body callback failed") \
152
246
  XX(CB_message_complete, "the on_message_complete callback failed") \
247
+ XX(CB_status, "the on_status callback failed") \
248
+ XX(CB_chunk_header, "the on_chunk_header callback failed") \
249
+ XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
153
250
  \
154
251
  /* Parsing-related errors */ \
155
252
  XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
@@ -170,6 +267,8 @@ enum flags
170
267
  XX(INVALID_HEADER_TOKEN, "invalid character in header") \
171
268
  XX(INVALID_CONTENT_LENGTH, \
172
269
  "invalid character in content-length header") \
270
+ XX(UNEXPECTED_CONTENT_LENGTH, \
271
+ "unexpected content-length header") \
173
272
  XX(INVALID_CHUNK_SIZE, \
174
273
  "invalid character in chunk size header") \
175
274
  XX(INVALID_CONSTANT, "invalid constant string") \
@@ -190,21 +289,15 @@ enum http_errno {
190
289
  /* Get an http_errno value from an http_parser */
191
290
  #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
192
291
 
193
- /* Get the line number that generated the current error */
194
- #if HTTP_PARSER_DEBUG
195
- #define HTTP_PARSER_ERRNO_LINE(p) ((p)->error_lineno)
196
- #else
197
- #define HTTP_PARSER_ERRNO_LINE(p) 0
198
- #endif
199
-
200
292
 
201
293
  struct http_parser {
202
294
  /** PRIVATE **/
203
- unsigned char type : 2; /* enum http_parser_type */
204
- unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
205
- unsigned char state; /* enum state from http_parser.c */
206
- unsigned char header_state; /* enum header_state from http_parser.c */
207
- unsigned char index; /* index into current matcher */
295
+ unsigned int type : 2; /* enum http_parser_type */
296
+ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
297
+ unsigned int state : 7; /* enum state from http_parser.c */
298
+ unsigned int header_state : 7; /* enum header_state from http_parser.c */
299
+ unsigned int index : 7; /* index into current matcher */
300
+ unsigned int lenient_http_headers : 1;
208
301
 
209
302
  uint32_t nread; /* # bytes read in various scenarios */
210
303
  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
@@ -212,20 +305,16 @@ struct http_parser {
212
305
  /** READ-ONLY **/
213
306
  unsigned short http_major;
214
307
  unsigned short http_minor;
215
- unsigned short status_code; /* responses only */
216
- unsigned char method; /* requests only */
217
- unsigned char http_errno : 7;
308
+ unsigned int status_code : 16; /* responses only */
309
+ unsigned int method : 8; /* requests only */
310
+ unsigned int http_errno : 7;
218
311
 
219
312
  /* 1 = Upgrade header was present and the parser has exited because of that.
220
313
  * 0 = No upgrade header present.
221
314
  * Should be checked when http_parser_execute() returns in addition to
222
315
  * error checking.
223
316
  */
224
- unsigned char upgrade : 1;
225
-
226
- #if HTTP_PARSER_DEBUG
227
- uint32_t error_lineno;
228
- #endif
317
+ unsigned int upgrade : 1;
229
318
 
230
319
  /** PUBLIC **/
231
320
  void *data; /* A pointer to get hook to the "connection" or "socket" object */
@@ -235,11 +324,17 @@ struct http_parser {
235
324
  struct http_parser_settings {
236
325
  http_cb on_message_begin;
237
326
  http_data_cb on_url;
327
+ http_data_cb on_status;
238
328
  http_data_cb on_header_field;
239
329
  http_data_cb on_header_value;
240
330
  http_cb on_headers_complete;
241
331
  http_data_cb on_body;
242
332
  http_cb on_message_complete;
333
+ /* When on_chunk_header is called, the current chunk length is stored
334
+ * in parser->content_length.
335
+ */
336
+ http_cb on_chunk_header;
337
+ http_cb on_chunk_complete;
243
338
  };
244
339
 
245
340
 
@@ -250,7 +345,8 @@ enum http_parser_url_fields
250
345
  , UF_PATH = 3
251
346
  , UF_QUERY = 4
252
347
  , UF_FRAGMENT = 5
253
- , UF_MAX = 6
348
+ , UF_USERINFO = 6
349
+ , UF_MAX = 7
254
350
  };
255
351
 
256
352
 
@@ -272,9 +368,28 @@ struct http_parser_url {
272
368
  };
273
369
 
274
370
 
371
+ /* Returns the library version. Bits 16-23 contain the major version number,
372
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
373
+ * Usage example:
374
+ *
375
+ * unsigned long version = http_parser_version();
376
+ * unsigned major = (version >> 16) & 255;
377
+ * unsigned minor = (version >> 8) & 255;
378
+ * unsigned patch = version & 255;
379
+ * printf("http_parser v%u.%u.%u\n", major, minor, patch);
380
+ */
381
+ unsigned long http_parser_version(void);
382
+
275
383
  void http_parser_init(http_parser *parser, enum http_parser_type type);
276
384
 
277
385
 
386
+ /* Initialize http_parser_settings members to 0
387
+ */
388
+ void http_parser_settings_init(http_parser_settings *settings);
389
+
390
+
391
+ /* Executes the parser. Returns number of parsed bytes. Sets
392
+ * `parser->http_errno` on error. */
278
393
  size_t http_parser_execute(http_parser *parser,
279
394
  const http_parser_settings *settings,
280
395
  const char *data,
@@ -282,12 +397,12 @@ size_t http_parser_execute(http_parser *parser,
282
397
 
283
398
 
284
399
  /* If http_should_keep_alive() in the on_headers_complete or
285
- * on_message_complete callback returns true, then this will be should be
400
+ * on_message_complete callback returns 0, then this should be
286
401
  * the last message on the connection.
287
402
  * If you are the server, respond with the "Connection: close" header.
288
403
  * If you are the client, close the connection.
289
404
  */
290
- int http_should_keep_alive(http_parser *parser);
405
+ int http_should_keep_alive(const http_parser *parser);
291
406
 
292
407
  /* Returns a string version of the HTTP method. */
293
408
  const char *http_method_str(enum http_method m);
@@ -298,6 +413,9 @@ const char *http_errno_name(enum http_errno err);
298
413
  /* Return a string description of the given error */
299
414
  const char *http_errno_description(enum http_errno err);
300
415
 
416
+ /* Initialize all http_parser_url members to 0 */
417
+ void http_parser_url_init(struct http_parser_url *u);
418
+
301
419
  /* Parse a URL; return nonzero on failure */
302
420
  int http_parser_parse_url(const char *buf, size_t buflen,
303
421
  int is_connect,
@@ -306,6 +424,9 @@ int http_parser_parse_url(const char *buf, size_t buflen,
306
424
  /* Pause or un-pause the parser; a nonzero value pauses */
307
425
  void http_parser_pause(http_parser *parser, int paused);
308
426
 
427
+ /* Checks if this is the final chunk of the body. */
428
+ int http_body_is_final(const http_parser *parser);
429
+
309
430
  #ifdef __cplusplus
310
431
  }
311
432
  #endif