Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 477 lines (391 sloc) 15.886 kb
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
1 #include <node_http_parser.h>
2
3 #include <v8.h>
4 #include <node.h>
5 #include <node_buffer.h>
6
7 #include <http_parser.h>
8
9 #include <strings.h> /* strcasecmp() */
c2e2479 @ry Fix Linux build
ry authored
10 #include <string.h> /* strdup() */
11 #include <stdlib.h> /* free() */
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
12
13 // This is a binding to http_parser (http://github.com/ry/http-parser)
14 // The goal is to decouple sockets from parsing for more javascript-level
15 // agility. A Buffer is read from a socket and passed to parser.execute().
16 // The parser then issues callbacks with slices of the data
17 // parser.onMessageBegin
18 // parser.onPath
19 // parser.onBody
20 // ...
21 // No copying is performed when slicing the buffer, only small reference
22 // allocations.
23
8c85340 @bmizerany fix whitespace errors
bmizerany authored
24
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
25 namespace node {
26
27 using namespace v8;
28
29 static Persistent<String> on_message_begin_sym;
30 static Persistent<String> on_path_sym;
31 static Persistent<String> on_query_string_sym;
32 static Persistent<String> on_url_sym;
33 static Persistent<String> on_fragment_sym;
34 static Persistent<String> on_header_field_sym;
35 static Persistent<String> on_header_value_sym;
36 static Persistent<String> on_headers_complete_sym;
37 static Persistent<String> on_body_sym;
38 static Persistent<String> on_message_complete_sym;
39
40 static Persistent<String> delete_sym;
41 static Persistent<String> get_sym;
42 static Persistent<String> head_sym;
43 static Persistent<String> post_sym;
44 static Persistent<String> put_sym;
45 static Persistent<String> connect_sym;
46 static Persistent<String> options_sym;
47 static Persistent<String> trace_sym;
48 static Persistent<String> copy_sym;
49 static Persistent<String> lock_sym;
50 static Persistent<String> mkcol_sym;
51 static Persistent<String> move_sym;
52 static Persistent<String> propfind_sym;
53 static Persistent<String> proppatch_sym;
54 static Persistent<String> unlock_sym;
d49d53f @ry Expose new HTTP methods
ry authored
55 static Persistent<String> report_sym;
56 static Persistent<String> mkactivity_sym;
57 static Persistent<String> checkout_sym;
58 static Persistent<String> merge_sym;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
59 static Persistent<String> unknown_method_sym;
60
61 static Persistent<String> method_sym;
62 static Persistent<String> status_code_sym;
63 static Persistent<String> http_version_sym;
64 static Persistent<String> version_major_sym;
65 static Persistent<String> version_minor_sym;
66 static Persistent<String> should_keep_alive_sym;
760bba5 @ry Support Upgrade in HTTP messages
ry authored
67 static Persistent<String> upgrade_sym;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
68
54d4efd @ry Upgrade http-parser
ry authored
69 static struct http_parser_settings settings;
70
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
71 // Callback prototype for http_cb
bf803f4 @ry Reimplment Buffers
ry authored
72 #define DEFINE_HTTP_CB(name) \
73 static int name(http_parser *p) { \
74 Parser *parser = static_cast<Parser*>(p->data); \
75 Local<Value> cb_value = parser->handle_->Get(name##_sym); \
76 if (!cb_value->IsFunction()) return 0; \
77 Local<Function> cb = Local<Function>::Cast(cb_value); \
78 Local<Value> ret = cb->Call(parser->handle_, 0, NULL); \
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
79 if (ret.IsEmpty()) { \
80 parser->got_exception_ = true; \
81 return -1; \
82 } else { \
83 return 0; \
84 } \
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
85 }
86
87 // Callback prototype for http_data_cb
bf803f4 @ry Reimplment Buffers
ry authored
88 #define DEFINE_HTTP_DATA_CB(name) \
89 static int name(http_parser *p, const char *at, size_t length) { \
90 Parser *parser = static_cast<Parser*>(p->data); \
91 assert(parser->buffer_); \
92 Local<Value> cb_value = parser->handle_->Get(name##_sym); \
93 if (!cb_value->IsFunction()) return 0; \
94 Local<Function> cb = Local<Function>::Cast(cb_value); \
95 Local<Value> argv[3] = { Local<Value>::New(parser->buffer_->handle_) \
96 , Integer::New(at - parser->buffer_->data()) \
97 , Integer::New(length) \
98 }; \
99 Local<Value> ret = cb->Call(parser->handle_, 3, argv); \
100 assert(parser->buffer_); \
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
101 if (ret.IsEmpty()) { \
102 parser->got_exception_ = true; \
103 return -1; \
104 } else { \
105 return 0; \
106 } \
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
107 }
108
109
110 static inline Persistent<String>
9be6c50 @ry Upgrade http-parser
ry authored
111 method_to_str(unsigned short m) {
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
112 switch (m) {
113 case HTTP_DELETE: return delete_sym;
114 case HTTP_GET: return get_sym;
115 case HTTP_HEAD: return head_sym;
116 case HTTP_POST: return post_sym;
117 case HTTP_PUT: return put_sym;
118 case HTTP_CONNECT: return connect_sym;
119 case HTTP_OPTIONS: return options_sym;
120 case HTTP_TRACE: return trace_sym;
121 case HTTP_COPY: return copy_sym;
122 case HTTP_LOCK: return lock_sym;
123 case HTTP_MKCOL: return mkcol_sym;
124 case HTTP_MOVE: return move_sym;
125 case HTTP_PROPFIND: return propfind_sym;
126 case HTTP_PROPPATCH: return proppatch_sym;
127 case HTTP_UNLOCK: return unlock_sym;
d49d53f @ry Expose new HTTP methods
ry authored
128 case HTTP_REPORT: return report_sym;
129 case HTTP_MKACTIVITY: return mkactivity_sym;
130 case HTTP_CHECKOUT: return checkout_sym;
131 case HTTP_MERGE: return merge_sym;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
132 default: return unknown_method_sym;
133 }
134 }
135
136
137 class Parser : public ObjectWrap {
138 public:
139 Parser(enum http_parser_type type) : ObjectWrap() {
140 buffer_ = NULL;
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
141 Init(type);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
142 }
143
741e3fa @ry HTTP works somewhat on net2 now
ry authored
144 ~Parser() {
145 assert(buffer_ == NULL && "Destroying a parser while it's parsing");
146 }
147
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
148 DEFINE_HTTP_CB(on_message_begin)
149 DEFINE_HTTP_CB(on_message_complete)
150
151 DEFINE_HTTP_DATA_CB(on_path)
152 DEFINE_HTTP_DATA_CB(on_url)
153 DEFINE_HTTP_DATA_CB(on_fragment)
154 DEFINE_HTTP_DATA_CB(on_query_string)
155 DEFINE_HTTP_DATA_CB(on_header_field)
156 DEFINE_HTTP_DATA_CB(on_header_value)
157 DEFINE_HTTP_DATA_CB(on_body)
158
159 static int on_headers_complete(http_parser *p) {
160 Parser *parser = static_cast<Parser*>(p->data);
161
162 Local<Value> cb_value = parser->handle_->Get(on_headers_complete_sym);
163 if (!cb_value->IsFunction()) return 0;
164 Local<Function> cb = Local<Function>::Cast(cb_value);
165
166
167 Local<Object> message_info = Object::New();
168
169 // METHOD
170 if (p->type == HTTP_REQUEST) {
171 message_info->Set(method_sym, method_to_str(p->method));
172 }
173
174 // STATUS
175 if (p->type == HTTP_RESPONSE) {
176 message_info->Set(status_code_sym, Integer::New(p->status_code));
177 }
178
179 // VERSION
180 message_info->Set(version_major_sym, Integer::New(p->http_major));
181 message_info->Set(version_minor_sym, Integer::New(p->http_minor));
182
183 message_info->Set(should_keep_alive_sym,
184 http_should_keep_alive(p) ? True() : False());
185
760bba5 @ry Support Upgrade in HTTP messages
ry authored
186 message_info->Set(upgrade_sym, p->upgrade ? True() : False());
187
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
188 Local<Value> argv[1] = { message_info };
189
35c14f6 @ry In HTTP parser, proxy return value of onHeadersComplete
ry authored
190 Local<Value> head_response = cb->Call(parser->handle_, 1, argv);
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
191
35c14f6 @ry In HTTP parser, proxy return value of onHeadersComplete
ry authored
192 if (head_response.IsEmpty()) {
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
193 parser->got_exception_ = true;
194 return -1;
195 } else {
35c14f6 @ry In HTTP parser, proxy return value of onHeadersComplete
ry authored
196 return head_response->IsTrue() ? 1 : 0;
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
197 }
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
198 }
199
200 static Handle<Value> New(const Arguments& args) {
201 HandleScope scope;
202
203 String::Utf8Value type(args[0]->ToString());
204
8c85340 @bmizerany fix whitespace errors
bmizerany authored
205 Parser *parser;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
206
207 if (0 == strcasecmp(*type, "request")) {
208 parser = new Parser(HTTP_REQUEST);
209 } else if (0 == strcasecmp(*type, "response")) {
210 parser = new Parser(HTTP_RESPONSE);
211 } else {
212 return ThrowException(Exception::Error(
213 String::New("Constructor argument be 'request' or 'response'")));
214 }
215
216 parser->Wrap(args.This());
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
217 assert(!parser->buffer_);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
218
219 return args.This();
220 }
221
222 // var bytesParsed = parser->execute(buffer, off, len);
223 static Handle<Value> Execute(const Arguments& args) {
224 HandleScope scope;
225
226 Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
227
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
228 assert(!parser->buffer_);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
229 if (parser->buffer_) {
230 return ThrowException(Exception::TypeError(
231 String::New("Already parsing a buffer")));
232 }
233
bf803f4 @ry Reimplment Buffers
ry authored
234 if (!Buffer::HasInstance(args[0])) {
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
235 return ThrowException(Exception::TypeError(
236 String::New("Argument should be a buffer")));
237 }
238
bf803f4 @ry Reimplment Buffers
ry authored
239 Buffer * buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
240
241 size_t off = args[1]->Int32Value();
bf803f4 @ry Reimplment Buffers
ry authored
242 if (off >= buffer->length()) {
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
243 return ThrowException(Exception::Error(
244 String::New("Offset is out of bounds")));
245 }
246
247 size_t len = args[2]->Int32Value();
bf803f4 @ry Reimplment Buffers
ry authored
248 if (off+len > buffer->length()) {
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
249 return ThrowException(Exception::Error(
250 String::New("Length is extends beyond buffer")));
251 }
252
253 // Assign 'buffer_' while we parse. The callbacks will access that varible.
254 parser->buffer_ = buffer;
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
255 parser->got_exception_ = false;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
256
bf803f4 @ry Reimplment Buffers
ry authored
257 size_t nparsed =
2fca40e @ry Upgrade http-parser
ry authored
258 http_parser_execute(&parser->parser_, &settings, buffer->data()+off, len);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
259
260 // Unassign the 'buffer_' variable
261 assert(parser->buffer_);
262 parser->buffer_ = NULL;
263
264 // If there was an exception in one of the callbacks
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
265 if (parser->got_exception_) return Local<Value>();
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
266
267 Local<Integer> nparsed_obj = Integer::New(nparsed);
8c85340 @bmizerany fix whitespace errors
bmizerany authored
268 // If there was a parse error in one of the callbacks
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
269 // TODO What if there is an error on EOF?
760bba5 @ry Support Upgrade in HTTP messages
ry authored
270 if (!parser->parser_.upgrade && nparsed != len) {
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
271 Local<Value> e = Exception::Error(String::NewSymbol("Parse Error"));
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
272 Local<Object> obj = e->ToObject();
273 obj->Set(String::NewSymbol("bytesParsed"), nparsed_obj);
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
274 return scope.Close(e);
275 } else {
276 return scope.Close(nparsed_obj);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
277 }
278 }
279
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
280 static Handle<Value> Finish(const Arguments& args) {
741e3fa @ry HTTP works somewhat on net2 now
ry authored
281 HandleScope scope;
282
283 Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
284
285 assert(!parser->buffer_);
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
286 parser->got_exception_ = false;
741e3fa @ry HTTP works somewhat on net2 now
ry authored
287
2fca40e @ry Upgrade http-parser
ry authored
288 http_parser_execute(&(parser->parser_), &settings, NULL, 0);
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
289
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
290 if (parser->got_exception_) return Local<Value>();
291
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
292 return Undefined();
293 }
294
295 static Handle<Value> Reinitialize(const Arguments& args) {
296 HandleScope scope;
297 Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
298
299 String::Utf8Value type(args[0]->ToString());
300
301 if (0 == strcasecmp(*type, "request")) {
302 parser->Init(HTTP_REQUEST);
303 } else if (0 == strcasecmp(*type, "response")) {
304 parser->Init(HTTP_RESPONSE);
305 } else {
306 return ThrowException(Exception::Error(
307 String::New("Argument be 'request' or 'response'")));
308 }
741e3fa @ry HTTP works somewhat on net2 now
ry authored
309 return Undefined();
310 }
311
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
312
313 private:
314
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
315 void Init (enum http_parser_type type) {
316 assert(buffer_ == NULL); // don't call this during Execute()
317 http_parser_init(&parser_, type);
318
319 parser_.data = this;
320 }
321
bf803f4 @ry Reimplment Buffers
ry authored
322 Buffer * buffer_; // The buffer currently being parsed.
cbd2c39 @ry Throwing in a callback should kill the process
ry authored
323 bool got_exception_;
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
324 http_parser parser_;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
325 };
326
327
4ce100f @isaacs A replacement for decodeURIComponent that doesn't throw.
isaacs authored
328 static Handle<Value> UrlDecode (const Arguments& args) {
329 HandleScope scope;
330
331 if (!args[0]->IsString()) {
332 return ThrowException(Exception::TypeError(
333 String::New("First arg must be a string")));
334 }
335
336 bool decode_spaces = args[1]->IsTrue();
337
338 String::Utf8Value in_v(args[0]->ToString());
339 size_t l = in_v.length();
340 char* out = strdup(*in_v);
341
342 enum { CHAR, HEX0, HEX1 } state = CHAR;
343
8c85340 @bmizerany fix whitespace errors
bmizerany authored
344 int n, m, hexchar;
4ce100f @isaacs A replacement for decodeURIComponent that doesn't throw.
isaacs authored
345 size_t in_index = 0, out_index = 0;
346 char c;
347 for (; in_index <= l; in_index++) {
348 c = out[in_index];
349 switch (state) {
350 case CHAR:
351 switch (c) {
352 case '%':
353 n = 0;
354 m = 0;
355 state = HEX0;
356 break;
357 case '+':
358 if (decode_spaces) c = ' ';
359 // pass thru
360 default:
361 out[out_index++] = c;
362 break;
363 }
364 break;
365
366 case HEX0:
367 state = HEX1;
368 hexchar = c;
369 if ('0' <= c && c <= '9') {
370 n = c - '0';
371 } else if ('a' <= c && c <= 'f') {
372 n = c - 'a' + 10;
373 } else if ('A' <= c && c <= 'F') {
374 n = c - 'A' + 10;
375 } else {
376 out[out_index++] = '%';
377 out[out_index++] = c;
378 state = CHAR;
379 break;
380 }
381 break;
382
383 case HEX1:
384 state = CHAR;
385 if ('0' <= c && c <= '9') {
386 m = c - '0';
387 } else if ('a' <= c && c <= 'f') {
388 m = c - 'a' + 10;
389 } else if ('A' <= c && c <= 'F') {
390 m = c - 'A' + 10;
391 } else {
392 out[out_index++] = '%';
393 out[out_index++] = hexchar;
394 out[out_index++] = c;
395 break;
396 }
397 out[out_index++] = 16*n + m;
398 break;
399 }
400 }
401
402 Local<String> out_v = String::New(out, out_index-1);
403 free(out);
404 return scope.Close(out_v);
405 }
406
407
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
408 void InitHttpParser(Handle<Object> target) {
409 HandleScope scope;
410
411 Local<FunctionTemplate> t = FunctionTemplate::New(Parser::New);
412 t->InstanceTemplate()->SetInternalFieldCount(1);
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
413 t->SetClassName(String::NewSymbol("HTTPParser"));
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
414
415 NODE_SET_PROTOTYPE_METHOD(t, "execute", Parser::Execute);
a668d07 @ry Clean up http_parser binding - add asserts
ry authored
416 NODE_SET_PROTOTYPE_METHOD(t, "finish", Parser::Finish);
417 NODE_SET_PROTOTYPE_METHOD(t, "reinitialize", Parser::Reinitialize);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
418
419 target->Set(String::NewSymbol("HTTPParser"), t->GetFunction());
4ce100f @isaacs A replacement for decodeURIComponent that doesn't throw.
isaacs authored
420 NODE_SET_METHOD(target, "urlDecode", UrlDecode);
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
421
422 on_message_begin_sym = NODE_PSYMBOL("onMessageBegin");
423 on_path_sym = NODE_PSYMBOL("onPath");
424 on_query_string_sym = NODE_PSYMBOL("onQueryString");
425 on_url_sym = NODE_PSYMBOL("onURL");
426 on_fragment_sym = NODE_PSYMBOL("onFragment");
427 on_header_field_sym = NODE_PSYMBOL("onHeaderField");
428 on_header_value_sym = NODE_PSYMBOL("onHeaderValue");
429 on_headers_complete_sym = NODE_PSYMBOL("onHeadersComplete");
430 on_body_sym = NODE_PSYMBOL("onBody");
431 on_message_complete_sym = NODE_PSYMBOL("onMessageComplete");
432
433 delete_sym = NODE_PSYMBOL("DELETE");
434 get_sym = NODE_PSYMBOL("GET");
435 head_sym = NODE_PSYMBOL("HEAD");
436 post_sym = NODE_PSYMBOL("POST");
437 put_sym = NODE_PSYMBOL("PUT");
438 connect_sym = NODE_PSYMBOL("CONNECT");
439 options_sym = NODE_PSYMBOL("OPTIONS");
440 trace_sym = NODE_PSYMBOL("TRACE");
441 copy_sym = NODE_PSYMBOL("COPY");
442 lock_sym = NODE_PSYMBOL("LOCK");
443 mkcol_sym = NODE_PSYMBOL("MKCOL");
444 move_sym = NODE_PSYMBOL("MOVE");
445 propfind_sym = NODE_PSYMBOL("PROPFIND");
446 proppatch_sym = NODE_PSYMBOL("PROPPATCH");
447 unlock_sym = NODE_PSYMBOL("UNLOCK");
d49d53f @ry Expose new HTTP methods
ry authored
448 report_sym = NODE_PSYMBOL("REPORT");
449 mkactivity_sym = NODE_PSYMBOL("MKACTIVITY");
450 checkout_sym = NODE_PSYMBOL("CHECKOUT");
451 merge_sym = NODE_PSYMBOL("MERGE");
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
452 unknown_method_sym = NODE_PSYMBOL("UNKNOWN_METHOD");
453
454 method_sym = NODE_PSYMBOL("method");
455 status_code_sym = NODE_PSYMBOL("statusCode");
456 http_version_sym = NODE_PSYMBOL("httpVersion");
457 version_major_sym = NODE_PSYMBOL("versionMajor");
458 version_minor_sym = NODE_PSYMBOL("versionMinor");
459 should_keep_alive_sym = NODE_PSYMBOL("shouldKeepAlive");
760bba5 @ry Support Upgrade in HTTP messages
ry authored
460 upgrade_sym = NODE_PSYMBOL("upgrade");
54d4efd @ry Upgrade http-parser
ry authored
461
462 settings.on_message_begin = Parser::on_message_begin;
463 settings.on_path = Parser::on_path;
464 settings.on_query_string = Parser::on_query_string;
465 settings.on_url = Parser::on_url;
466 settings.on_fragment = Parser::on_fragment;
467 settings.on_header_field = Parser::on_header_field;
468 settings.on_header_value = Parser::on_header_value;
469 settings.on_headers_complete = Parser::on_headers_complete;
470 settings.on_body = Parser::on_body;
471 settings.on_message_complete = Parser::on_message_complete;
42ee169 @ry Implement new http-parser binding using Buffer
ry authored
472 }
473
474 } // namespace node
475
e65c270 @pquerna Move http parser to extension model.
pquerna authored
476 NODE_MODULE(node_http_parser, node::InitHttpParser);
Something went wrong with that request. Please try again.