Browse Source

Create node.http.Server and node.http.LowLevelServer

The LowLevelServer is a direct interface to the parser given people access
to things like partially received headers. This could be used to implement
an extremely optimized server which acts before parsing is complete.

Most people will be using node.http.Server which is still rather low-level
compared to other http interfaces, but does take care of some details for
you.
v0.7.4-release
Ryan 16 years ago
parent
commit
a80591aff6
  1. 169
      src/http.cc
  2. 126
      src/http.js
  3. 6
      src/node.cc

169
src/http.cc

@ -11,10 +11,12 @@
#define ON_BODY_SYMBOL String::NewSymbol("onBody")
#define ON_MESSAGE_COMPLETE_SYMBOL String::NewSymbol("onMessageComplete")
#define PATH_SYMBOL String::NewSymbol("path")
#define QUERY_STRING_SYMBOL String::NewSymbol("query_string")
#define URI_SYMBOL String::NewSymbol("uri")
#define FRAGMENT_SYMBOL String::NewSymbol("fragment")
#define ON_PATH_SYMBOL String::NewSymbol("onPath")
#define ON_QUERY_STRING_SYMBOL String::NewSymbol("onQueryString")
#define ON_URI_SYMBOL String::NewSymbol("onURI")
#define ON_FRAGMENT_SYMBOL String::NewSymbol("onFragment")
#define ON_HEADER_FIELD_SYMBOL String::NewSymbol("onHeaderField")
#define ON_HEADER_VALUE_SYMBOL String::NewSymbol("onHeaderValue")
#define STATUS_CODE_SYMBOL String::NewSymbol("status_code")
#define HTTP_VERSION_SYMBOL String::NewSymbol("http_version")
@ -23,56 +25,12 @@ using namespace v8;
using namespace node;
using namespace std;
// Native Helper Functions
static Persistent<Function> _fill_field;
static Persistent<Function> _append_header_field;
static Persistent<Function> _append_header_value;
#define CATCH_NATIVE_HTTP_FUNCTION(variable, jsname) \
do { \
if (variable.IsEmpty()) { \
Local<Object> __g = Context::GetCurrent()->Global(); \
Local<Value> __node_v = __g->Get(String::NewSymbol("node")); \
Local<Object> __node = __node_v->ToObject(); \
Local<Value> __http_v = __node->Get(String::NewSymbol("http")); \
Local<Object> __http = __http_v->ToObject(); \
Local<Value> __value = __http->Get(String::NewSymbol(jsname)); \
Handle<Function> __function_handle = Handle<Function>::Cast(__value); \
variable = Persistent<Function>::New(__function_handle); \
} \
} while(0)
void
fillField (Handle<Value> message, Handle<Value> field, Handle<Value> value)
{
HandleScope scope;
CATCH_NATIVE_HTTP_FUNCTION(_fill_field, "fillField");
Handle<Value> argv[] = { message, field, value };
_fill_field->Call(message->ToObject(), 3, argv);
}
void
appendHeaderField (Handle<Value> message, Handle<Value> d)
{
HandleScope scope;
CATCH_NATIVE_HTTP_FUNCTION(_append_header_field, "appendHeaderField");
Handle<Value> argv[] = { message, d };
_append_header_field->Call(message->ToObject(), 2, argv);
}
void
appendHeaderValue (Handle<Value> message, Handle<Value> d)
{
HandleScope scope;
CATCH_NATIVE_HTTP_FUNCTION(_append_header_value, "appendHeaderValue");
Handle<Value> argv[] = { message, d };
_append_header_value->Call(message->ToObject(), 2, argv);
}
Persistent<FunctionTemplate> HTTPConnection::client_constructor_template;
Persistent<FunctionTemplate> HTTPConnection::server_constructor_template;
static Persistent<Object> http_module;
void
HTTPConnection::Initialize (Handle<Object> target)
{
@ -82,13 +40,13 @@ HTTPConnection::Initialize (Handle<Object> target)
client_constructor_template = Persistent<FunctionTemplate>::New(t);
client_constructor_template->Inherit(Connection::constructor_template);
client_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
target->Set(String::NewSymbol("HTTPClient"), client_constructor_template->GetFunction());
target->Set(String::NewSymbol("Client"), client_constructor_template->GetFunction());
t = FunctionTemplate::New(v8NewServer);
server_constructor_template = Persistent<FunctionTemplate>::New(t);
server_constructor_template->Inherit(Connection::constructor_template);
server_constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
target->Set(String::NewSymbol("HTTPServerSideSocket"),
target->Set(String::NewSymbol("ServerSideSocket"),
server_constructor_template->GetFunction());
}
@ -119,13 +77,8 @@ HTTPConnection::OnReceive (const void *buf, size_t len)
{
http_parser_execute(&parser_, static_cast<const char*>(buf), len);
if (http_parser_has_error(&parser_)) {
// do something?
Close();
return;
}
// XXX when do we close the connection?
if (http_parser_has_error(&parser_))
ForceClose();
}
int
@ -138,48 +91,50 @@ HTTPConnection::on_message_begin (http_parser *parser)
Local<Value> on_message_v = protocol->Get(ON_MESSAGE_SYMBOL);
if (!on_message_v->IsFunction()) return -1;
Handle<Function> on_message = Handle<Function>::Cast(on_message_v);
TryCatch try_catch;
Local<Object> message_handler = on_message->NewInstance();
if (try_catch.HasCaught()) {
fatal_exception(try_catch);
return -1;
}
connection->handle_->SetHiddenValue(MESSAGE_HANDLER_SYMBOL, message_handler);
return 0;
}
#define DEFINE_FILL_VALUE_CALLBACK(callback_name, symbol) \
#define DEFINE_PARSER_CALLBACK(name, symbol) \
int \
HTTPConnection::callback_name (http_parser *parser, const char *buf, size_t len) \
HTTPConnection::name (http_parser *parser, const char *buf, size_t len) \
{ \
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data); \
HandleScope scope; \
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data); \
Local<Value> message_handler_v = \
connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); \
fillField(message_handler_v, symbol, String::New(buf, len)); \
if (message_handler_v->IsObject() == false) \
return -1; \
Local<Object> message_handler = message_handler_v->ToObject(); \
Local<Value> callback_v = message_handler->Get(symbol); \
if (callback_v->IsFunction() == false) \
return 0; \
Local<Function> callback = Local<Function>::Cast(callback_v); \
TryCatch try_catch; \
Local<Value> argv[1] = { String::New(buf, len) }; \
Local<Value> ret = callback->Call(message_handler, 1, argv); \
if (ret.IsEmpty()) { \
fatal_exception(try_catch); \
return -2; \
} \
if (ret->IsFalse()) return -3; \
return 0; \
}
DEFINE_FILL_VALUE_CALLBACK(on_path, PATH_SYMBOL)
DEFINE_FILL_VALUE_CALLBACK(on_query_string, QUERY_STRING_SYMBOL)
DEFINE_FILL_VALUE_CALLBACK(on_uri, URI_SYMBOL)
DEFINE_FILL_VALUE_CALLBACK(on_fragment, FRAGMENT_SYMBOL)
int
HTTPConnection::on_header_field (http_parser *parser, const char *buf, size_t len)
{
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data);
HandleScope scope;
Local<Value> message_handler_v =
connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL);
appendHeaderField(message_handler_v, String::New(buf, len));
return 0;
}
int
HTTPConnection::on_header_value (http_parser *parser, const char *buf, size_t len)
{
HTTPConnection *connection = static_cast<HTTPConnection*> (parser->data);
HandleScope scope;
Local<Value> message_handler_v =
connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL);
appendHeaderValue(message_handler_v, String::New(buf, len));
return 0;
}
DEFINE_PARSER_CALLBACK(on_path, ON_PATH_SYMBOL)
DEFINE_PARSER_CALLBACK(on_query_string, ON_QUERY_STRING_SYMBOL)
DEFINE_PARSER_CALLBACK(on_uri, ON_URI_SYMBOL)
DEFINE_PARSER_CALLBACK(on_fragment, ON_FRAGMENT_SYMBOL)
DEFINE_PARSER_CALLBACK(on_header_field, ON_HEADER_FIELD_SYMBOL)
DEFINE_PARSER_CALLBACK(on_header_value, ON_HEADER_VALUE_SYMBOL)
int
HTTPConnection::on_headers_complete (http_parser *parser)
@ -211,8 +166,13 @@ HTTPConnection::on_headers_complete (http_parser *parser)
Handle<Function> on_headers_complete = Handle<Function>::Cast(on_headers_complete_v);
on_headers_complete->Call(message_handler, 0, NULL);
TryCatch try_catch;
Local<Value> ret = on_headers_complete->Call(message_handler, 0, NULL);
if (ret.IsEmpty()) {
fatal_exception(try_catch);
return -2;
}
if (ret->IsFalse()) return -3;
return 0;
}
@ -235,8 +195,8 @@ HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len)
Handle<Value> argv[1];
// XXX whose encoding should we check? each message should have their own,
// probably. it ought to default to raw.
// TODO each message should have their encoding.
// don't look at the conneciton for encoding
if(connection->encoding_ == UTF8) {
// utf8 encoding
Handle<String> chunk = String::New((const char*)buf, len);
@ -252,6 +212,15 @@ HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len)
argv[0] = array;
}
on_body->Call(message_handler, 1, argv);
TryCatch try_catch;
Local<Value> ret = on_body->Call(message_handler, 0, NULL);
if (ret.IsEmpty()) {
fatal_exception(try_catch);
return -2;
}
if (ret->IsFalse()) return -3;
return 0;
}
@ -264,13 +233,21 @@ HTTPConnection::on_message_complete (http_parser *parser)
Local<Value> message_handler_v =
connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL);
Local<Object> message_handler = message_handler_v->ToObject();
connection->handle_->DeleteHiddenValue(MESSAGE_HANDLER_SYMBOL);
Local<Value> on_msg_complete_v = message_handler->Get(ON_MESSAGE_COMPLETE_SYMBOL);
if (on_msg_complete_v->IsFunction()) {
if (on_msg_complete_v->IsFunction() == false)
return 0;
Handle<Function> on_msg_complete = Handle<Function>::Cast(on_msg_complete_v);
on_msg_complete->Call(message_handler, 0, NULL);
TryCatch try_catch;
Local<Value> ret = on_msg_complete->Call(message_handler, 0, NULL);
if (ret.IsEmpty()) {
fatal_exception(try_catch);
return -2;
}
connection->handle_->DeleteHiddenValue(MESSAGE_HANDLER_SYMBOL);
if (ret->IsFalse()) return -3;
return 0;
}
@ -302,7 +279,7 @@ HTTPServer::Initialize (Handle<Object> target)
constructor_template = Persistent<FunctionTemplate>::New(t);
constructor_template->Inherit(Acceptor::constructor_template);
constructor_template->InstanceTemplate()->SetInternalFieldCount(1);
target->Set(String::NewSymbol("HTTPServer"), constructor_template->GetFunction());
target->Set(String::NewSymbol("LowLevelServer"), constructor_template->GetFunction());
}
Handle<Value>

126
src/http.js

@ -1,25 +1,121 @@
node.http = {
fillField : function (msg, field, data) {
msg[field] = (msg[field] || "") + data;
},
appendHeaderField : function (msg, data) {
if (msg.hasOwnProperty("headers")) {
var last_pair = msg.headers[msg.headers.length-1];
/* This is a wrapper around the LowLevelServer interface. It provides
* connection handling, overflow checking, and some data buffering.
*/
node.http.Server = function (RequestHandler, options) {
var MAX_FIELD_SIZE = 80*1024;
function Protocol (connection) {
function fillField (field, data) {
field = (field || "") + data;
if (field.length > MAX_FIELD_SIZE) {
connection.fullClose();
return false;
}
return true;
}
var responses = [];
function Response () {
responses.push(this);
/* This annoying output buisness is necessary for the case that users
* are writing to responses out of order! HTTP requires that responses
* are returned in the same order the requests come.
*/
var output = "";
this.output = output;
function send (data) {
if (responses[0] === this) {
connection.send(data);
} else {
output += data;
}
};
this.sendStatus = function (status, reason) {
output += "HTTP/1.1 " + status.toString() + " " + reason + "\r\n";
};
this.sendHeader = function (field, value) {
output += field + ": " + value.toString() + "\r\n";
};
var headersSent = false;
this.sendBody = function (chunk) {
if (headersSent === false) {
output += "\r\n";
}
output += chunk;
if (responses[0] === this) {
connection.send(output);
output = "";
}
};
var finished = false;
this.finish = function () {
this.finished = true;
while (responses.length > 0 && responses[0].finished) {
var res = responses.shift();
connection.send(res.output);
}
if (responses.length == 0) {
connection.fullClose();
// TODO keep-alive logic
// if http/1.0 without "Connection: keep-alive" header - yes
// if http/1.1 if the request was not GET or HEAD - yes
// if http/1.1 without "Connection: close" header - yes
}
};
}
this.onMessage = function ( ) {
var res = new Response();
var req = new RequestHandler(res);
this.encoding = req.encoding || "raw";
this.onPath = function (data) { return fillField(req.path, data); };
this.onURI = function (data) { return fillField(req.uri, data); };
this.onQueryString = function (data) { return fillField(req.query_string, data); };
this.onFragment = function (data) { return fillField(req.fragment, data); };
this.onHeaderField = function (data) {
if (req.hasOwnProperty("headers")) {
var last_pair = req.headers[req.headers.length-1];
if (last_pair.length == 1)
last_pair[0] += data;
return fillField(last_pair[0], data);
else
msg.headers.push([data]);
req.headers.push([data]);
} else {
msg.headers = [[data]];
req.headers = [[data]];
}
},
return true;
};
appendHeaderValue : function (msg, data) {
var last_pair = msg.headers[msg.headers.length-1];
this.onHeaderValue = function (data) {
var last_pair = req.headers[req.headers.length-1];
if (last_pair.length == 1)
last_pair[1] = data;
else
last_pair[1] += data;
return fillField(last_pair[1], data);
return true;
};
this.onHeadersComplete = function () {
req.http_version = this.http_version;
req.method = this.method;
return req.onHeadersComplete();
};
this.onBody = function (chunk) { return req.onBody(chunk); };
this.onBodyComplete = function () { return req.onBodyComplete(); };
};
}
var server = new node.http.LowLevelServer(Protocol, options);
this.listen = function (port, host) { server.listen(port, host); }
this.close = function () { server.close(); }
};

6
src/node.cc

@ -270,8 +270,10 @@ main (int argc, char *argv[])
Acceptor::Initialize(g);
Connection::Initialize(g);
HTTPServer::Initialize(g);
HTTPConnection::Initialize(g);
Local<Object> http = Object::New();
node->Set(String::New("http"), http);
HTTPServer::Initialize(http);
HTTPConnection::Initialize(http);
// NATIVE JAVASCRIPT MODULES
TryCatch try_catch;

Loading…
Cancel
Save