Skip to content

Commit 42ee169

Browse files
committed
Implement new http-parser binding using Buffer
1 parent 6f738d6 commit 42ee169

File tree

5 files changed

+399
-0
lines changed

5 files changed

+399
-0
lines changed

src/node.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <node_file.h>
2020
#include <node_idle_watcher.h>
2121
#include <node_http.h>
22+
#include <node_http_parser.h>
2223
#include <node_signal_handler.h>
2324
#include <node_stat.h>
2425
#include <node_timer.h>
@@ -996,6 +997,7 @@ static Local<Object> Load(int argc, char *argv[]) {
996997
SignalHandler::Initialize(process); // signal_handler.cc
997998

998999
InitNet2(process); // net2.cc
1000+
InitHttpParser(process); // http_parser.cc
9991001

10001002
Stdio::Initialize(process); // stdio.cc
10011003
ChildProcess::Initialize(process); // child_process.cc

src/node_http_parser.cc

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
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() */
10+
11+
// This is a binding to http_parser (http://github.com/ry/http-parser)
12+
// The goal is to decouple sockets from parsing for more javascript-level
13+
// agility. A Buffer is read from a socket and passed to parser.execute().
14+
// The parser then issues callbacks with slices of the data
15+
// parser.onMessageBegin
16+
// parser.onPath
17+
// parser.onBody
18+
// ...
19+
// No copying is performed when slicing the buffer, only small reference
20+
// allocations.
21+
22+
namespace node {
23+
24+
using namespace v8;
25+
26+
static Persistent<String> on_message_begin_sym;
27+
static Persistent<String> on_path_sym;
28+
static Persistent<String> on_query_string_sym;
29+
static Persistent<String> on_url_sym;
30+
static Persistent<String> on_fragment_sym;
31+
static Persistent<String> on_header_field_sym;
32+
static Persistent<String> on_header_value_sym;
33+
static Persistent<String> on_headers_complete_sym;
34+
static Persistent<String> on_body_sym;
35+
static Persistent<String> on_message_complete_sym;
36+
37+
static Persistent<String> delete_sym;
38+
static Persistent<String> get_sym;
39+
static Persistent<String> head_sym;
40+
static Persistent<String> post_sym;
41+
static Persistent<String> put_sym;
42+
static Persistent<String> connect_sym;
43+
static Persistent<String> options_sym;
44+
static Persistent<String> trace_sym;
45+
static Persistent<String> copy_sym;
46+
static Persistent<String> lock_sym;
47+
static Persistent<String> mkcol_sym;
48+
static Persistent<String> move_sym;
49+
static Persistent<String> propfind_sym;
50+
static Persistent<String> proppatch_sym;
51+
static Persistent<String> unlock_sym;
52+
static Persistent<String> unknown_method_sym;
53+
54+
static Persistent<String> method_sym;
55+
static Persistent<String> status_code_sym;
56+
static Persistent<String> http_version_sym;
57+
static Persistent<String> version_major_sym;
58+
static Persistent<String> version_minor_sym;
59+
static Persistent<String> should_keep_alive_sym;
60+
61+
// Callback prototype for http_cb
62+
#define DEFINE_HTTP_CB(name) \
63+
static int name(http_parser *p) { \
64+
Parser *parser = static_cast<Parser*>(p->data); \
65+
\
66+
HandleScope scope; \
67+
\
68+
Local<Value> cb_value = parser->handle_->Get(name##_sym); \
69+
if (!cb_value->IsFunction()) return 0; \
70+
Local<Function> cb = Local<Function>::Cast(cb_value); \
71+
\
72+
Local<Value> ret = cb->Call(parser->handle_, 0, NULL); \
73+
return ret.IsEmpty() ? -1 : 0; \
74+
}
75+
76+
// Callback prototype for http_data_cb
77+
#define DEFINE_HTTP_DATA_CB(name) \
78+
static int name(http_parser *p, const char *at, size_t length) { \
79+
Parser *parser = static_cast<Parser*>(p->data); \
80+
\
81+
HandleScope scope; \
82+
\
83+
assert(parser->buffer_); \
84+
char * base = buffer_p(parser->buffer_, 0); \
85+
\
86+
Local<Value> cb_value = parser->handle_->Get(name##_sym); \
87+
if (!cb_value->IsFunction()) return 0; \
88+
Local<Function> cb = Local<Function>::Cast(cb_value); \
89+
\
90+
Local<Integer> off = Integer::New(at - base); \
91+
Local<Integer> len = Integer::New(length); \
92+
Local<Value> argv[2] = { off, len }; \
93+
\
94+
Local<Value> ret = cb->Call(parser->handle_, 2, argv); \
95+
return ret.IsEmpty() ? -1 : 0; \
96+
}
97+
98+
99+
static inline Persistent<String>
100+
method_to_str(enum http_method m) {
101+
switch (m) {
102+
case HTTP_DELETE: return delete_sym;
103+
case HTTP_GET: return get_sym;
104+
case HTTP_HEAD: return head_sym;
105+
case HTTP_POST: return post_sym;
106+
case HTTP_PUT: return put_sym;
107+
case HTTP_CONNECT: return connect_sym;
108+
case HTTP_OPTIONS: return options_sym;
109+
case HTTP_TRACE: return trace_sym;
110+
case HTTP_COPY: return copy_sym;
111+
case HTTP_LOCK: return lock_sym;
112+
case HTTP_MKCOL: return mkcol_sym;
113+
case HTTP_MOVE: return move_sym;
114+
case HTTP_PROPFIND: return propfind_sym;
115+
case HTTP_PROPPATCH: return proppatch_sym;
116+
case HTTP_UNLOCK: return unlock_sym;
117+
default: return unknown_method_sym;
118+
}
119+
}
120+
121+
122+
class Parser : public ObjectWrap {
123+
public:
124+
Parser(enum http_parser_type type) : ObjectWrap() {
125+
buffer_ = NULL;
126+
127+
http_parser_init(&parser_, type);
128+
129+
parser_.on_message_begin = on_message_begin;
130+
parser_.on_path = on_path;
131+
parser_.on_query_string = on_query_string;
132+
parser_.on_url = on_url;
133+
parser_.on_fragment = on_fragment;
134+
parser_.on_header_field = on_header_field;
135+
parser_.on_header_value = on_header_value;
136+
parser_.on_headers_complete = on_headers_complete;
137+
parser_.on_body = on_body;
138+
parser_.on_message_complete = on_message_complete;
139+
140+
parser_.data = this;
141+
}
142+
143+
DEFINE_HTTP_CB(on_message_begin)
144+
DEFINE_HTTP_CB(on_message_complete)
145+
146+
DEFINE_HTTP_DATA_CB(on_path)
147+
DEFINE_HTTP_DATA_CB(on_url)
148+
DEFINE_HTTP_DATA_CB(on_fragment)
149+
DEFINE_HTTP_DATA_CB(on_query_string)
150+
DEFINE_HTTP_DATA_CB(on_header_field)
151+
DEFINE_HTTP_DATA_CB(on_header_value)
152+
DEFINE_HTTP_DATA_CB(on_body)
153+
154+
static int on_headers_complete(http_parser *p) {
155+
Parser *parser = static_cast<Parser*>(p->data);
156+
157+
HandleScope scope;
158+
159+
Local<Value> cb_value = parser->handle_->Get(on_headers_complete_sym);
160+
if (!cb_value->IsFunction()) return 0;
161+
Local<Function> cb = Local<Function>::Cast(cb_value);
162+
163+
164+
Local<Object> message_info = Object::New();
165+
166+
// METHOD
167+
if (p->type == HTTP_REQUEST) {
168+
message_info->Set(method_sym, method_to_str(p->method));
169+
}
170+
171+
// STATUS
172+
if (p->type == HTTP_RESPONSE) {
173+
message_info->Set(status_code_sym, Integer::New(p->status_code));
174+
}
175+
176+
// VERSION
177+
message_info->Set(version_major_sym, Integer::New(p->http_major));
178+
message_info->Set(version_minor_sym, Integer::New(p->http_minor));
179+
180+
message_info->Set(should_keep_alive_sym,
181+
http_should_keep_alive(p) ? True() : False());
182+
183+
Local<Value> argv[1] = { message_info };
184+
185+
Local<Value> ret = cb->Call(parser->handle_, 1, argv);
186+
return ret.IsEmpty() ? -1 : 0;
187+
}
188+
189+
static Handle<Value> New(const Arguments& args) {
190+
HandleScope scope;
191+
192+
String::Utf8Value type(args[0]->ToString());
193+
194+
Parser *parser;
195+
196+
if (0 == strcasecmp(*type, "request")) {
197+
parser = new Parser(HTTP_REQUEST);
198+
} else if (0 == strcasecmp(*type, "response")) {
199+
parser = new Parser(HTTP_RESPONSE);
200+
} else {
201+
return ThrowException(Exception::Error(
202+
String::New("Constructor argument be 'request' or 'response'")));
203+
}
204+
205+
parser->Wrap(args.This());
206+
207+
return args.This();
208+
}
209+
210+
// var bytesParsed = parser->execute(buffer, off, len);
211+
static Handle<Value> Execute(const Arguments& args) {
212+
HandleScope scope;
213+
214+
Parser *parser = ObjectWrap::Unwrap<Parser>(args.This());
215+
216+
if (parser->buffer_) {
217+
return ThrowException(Exception::TypeError(
218+
String::New("Already parsing a buffer")));
219+
}
220+
221+
if (!IsBuffer(args[0])) {
222+
return ThrowException(Exception::TypeError(
223+
String::New("Argument should be a buffer")));
224+
}
225+
226+
struct buffer * buffer = BufferUnwrap(args[0]);
227+
228+
size_t off = args[1]->Int32Value();
229+
if (buffer_p(buffer, off) == NULL) {
230+
return ThrowException(Exception::Error(
231+
String::New("Offset is out of bounds")));
232+
}
233+
234+
size_t len = args[2]->Int32Value();
235+
if (buffer_remaining(buffer, off) < len) {
236+
return ThrowException(Exception::Error(
237+
String::New("Length is extends beyond buffer")));
238+
}
239+
240+
TryCatch try_catch;
241+
242+
// Assign 'buffer_' while we parse. The callbacks will access that varible.
243+
parser->buffer_ = buffer;
244+
245+
size_t nparsed =
246+
http_parser_execute(&(parser->parser_), buffer_p(buffer, off), len);
247+
248+
// Unassign the 'buffer_' variable
249+
assert(parser->buffer_);
250+
parser->buffer_ = NULL;
251+
252+
// If there was an exception in one of the callbacks
253+
if (try_catch.HasCaught()) return try_catch.ReThrow();
254+
255+
Local<Integer> nparsed_obj = Integer::New(nparsed);
256+
// If there was a parse error in one of the callbacks
257+
// TODO What if there is an error on EOF?
258+
if (nparsed != len) {
259+
Local<Value> e = Exception::Error(String::New("Parse Error"));
260+
Local<Object> obj = e->ToObject();
261+
obj->Set(String::NewSymbol("bytesParsed"), nparsed_obj);
262+
return ThrowException(e);
263+
}
264+
265+
return scope.Close(nparsed_obj);
266+
}
267+
268+
269+
private:
270+
271+
http_parser parser_;
272+
struct buffer * buffer_; // The buffer currently being parsed.
273+
};
274+
275+
276+
void InitHttpParser(Handle<Object> target) {
277+
HandleScope scope;
278+
279+
Local<FunctionTemplate> t = FunctionTemplate::New(Parser::New);
280+
t->InstanceTemplate()->SetInternalFieldCount(1);
281+
//t->SetClassName(String::NewSymbol("HTTPParser"));
282+
283+
NODE_SET_PROTOTYPE_METHOD(t, "execute", Parser::Execute);
284+
285+
target->Set(String::NewSymbol("HTTPParser"), t->GetFunction());
286+
287+
on_message_begin_sym = NODE_PSYMBOL("onMessageBegin");
288+
on_path_sym = NODE_PSYMBOL("onPath");
289+
on_query_string_sym = NODE_PSYMBOL("onQueryString");
290+
on_url_sym = NODE_PSYMBOL("onURL");
291+
on_fragment_sym = NODE_PSYMBOL("onFragment");
292+
on_header_field_sym = NODE_PSYMBOL("onHeaderField");
293+
on_header_value_sym = NODE_PSYMBOL("onHeaderValue");
294+
on_headers_complete_sym = NODE_PSYMBOL("onHeadersComplete");
295+
on_body_sym = NODE_PSYMBOL("onBody");
296+
on_message_complete_sym = NODE_PSYMBOL("onMessageComplete");
297+
298+
delete_sym = NODE_PSYMBOL("DELETE");
299+
get_sym = NODE_PSYMBOL("GET");
300+
head_sym = NODE_PSYMBOL("HEAD");
301+
post_sym = NODE_PSYMBOL("POST");
302+
put_sym = NODE_PSYMBOL("PUT");
303+
connect_sym = NODE_PSYMBOL("CONNECT");
304+
options_sym = NODE_PSYMBOL("OPTIONS");
305+
trace_sym = NODE_PSYMBOL("TRACE");
306+
copy_sym = NODE_PSYMBOL("COPY");
307+
lock_sym = NODE_PSYMBOL("LOCK");
308+
mkcol_sym = NODE_PSYMBOL("MKCOL");
309+
move_sym = NODE_PSYMBOL("MOVE");
310+
propfind_sym = NODE_PSYMBOL("PROPFIND");
311+
proppatch_sym = NODE_PSYMBOL("PROPPATCH");
312+
unlock_sym = NODE_PSYMBOL("UNLOCK");
313+
unknown_method_sym = NODE_PSYMBOL("UNKNOWN_METHOD");
314+
315+
method_sym = NODE_PSYMBOL("method");
316+
status_code_sym = NODE_PSYMBOL("statusCode");
317+
http_version_sym = NODE_PSYMBOL("httpVersion");
318+
version_major_sym = NODE_PSYMBOL("versionMajor");
319+
version_minor_sym = NODE_PSYMBOL("versionMinor");
320+
should_keep_alive_sym = NODE_PSYMBOL("shouldKeepAlive");
321+
}
322+
323+
} // namespace node
324+

src/node_http_parser.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#ifndef NODE_HTTP_PARSER
2+
#define NODE_HTTP_PARSER
3+
4+
#include <v8.h>
5+
6+
namespace node {
7+
8+
void InitHttpParser(v8::Handle<v8::Object> target);
9+
10+
}
11+
12+
#endif // NODE_HTTP_PARSER

0 commit comments

Comments
 (0)