From 103a8800c7bce44bf819523be553e10648cbcba8 Mon Sep 17 00:00:00 2001 From: Ryan Date: Thu, 7 May 2009 12:15:01 +0200 Subject: [PATCH] Binary HTTP bodies for both requests and responses. --- src/http.cc | 44 +++++++++++++++++--------- src/http.js | 103 +++++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/src/http.cc b/src/http.cc index bef2638..8aeb1fa 100644 --- a/src/http.cc +++ b/src/http.cc @@ -4,19 +4,22 @@ #include #include +#include -#define ON_MESSAGE_SYMBOL String::NewSymbol("onMessage") -#define MESSAGE_HANDLER_SYMBOL String::NewSymbol("messageHandler") -#define ON_HEADERS_COMPLETE_SYMBOL String::NewSymbol("onHeadersComplete") -#define ON_BODY_SYMBOL String::NewSymbol("onBody") -#define ON_MESSAGE_COMPLETE_SYMBOL String::NewSymbol("onMessageComplete") +#define ENCODING_SYMBOL String::NewSymbol("encoding") -#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 MESSAGE_HANDLER_SYMBOL String::NewSymbol("messageHandler") + +#define ON_MESSAGE_SYMBOL String::NewSymbol("onMessage") +#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 ON_HEADERS_COMPLETE_SYMBOL String::NewSymbol("onHeadersComplete") +#define ON_BODY_SYMBOL String::NewSymbol("onBody") +#define ON_MESSAGE_COMPLETE_SYMBOL String::NewSymbol("onMessageComplete") #define STATUS_CODE_SYMBOL String::NewSymbol("status_code") #define HTTP_VERSION_SYMBOL String::NewSymbol("http_version") @@ -180,7 +183,7 @@ HTTPConnection::on_headers_complete (http_parser *parser) int HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len) { - if(len == 0) return 0; + assert(len != 0); HTTPConnection *connection = static_cast (parser->data); HandleScope scope; @@ -193,11 +196,24 @@ HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len) if (on_body_v->IsFunction() == false) return 0; Handle on_body = Handle::Cast(on_body_v); - Handle argv[1]; + /* Look at the value of message_handler.encoding to decide how to + * send the body chunk. This is rather sloppy and unnecesary. FIXME + */ + enum encoding encoding = RAW; + Local encoding_v = message_handler->Get(ENCODING_SYMBOL); + if (encoding_v->IsString()) { + Local encoding_string = encoding_v->ToString(); + char buf[5]; // need enough room for "utf8" or "raw" + encoding_string->WriteAscii(buf, 0, 4); + buf[4] = '\0'; + if(strcasecmp(buf, "utf8") == 0) + encoding = UTF8; + } + Handle argv[1]; // TODO each message should have their encoding. // don't look at the conneciton for encoding - if(connection->encoding_ == UTF8) { + if(encoding == UTF8) { // utf8 encoding Handle chunk = String::New((const char*)buf, len); argv[0] = chunk; diff --git a/src/http.js b/src/http.js index ebf74ca..e335006 100644 --- a/src/http.js +++ b/src/http.js @@ -20,76 +20,109 @@ node.http.Server = function (RequestHandler, options) { * are writing to responses out of order! HTTP requires that responses * are returned in the same order the requests come. */ - this.output = ""; + var output = []; + + function toRaw(string) { + var a = []; + for (var i = 0; i < string.length; i++) + a.push(string.charCodeAt(i)); + return a; + } + + // The send method appends data onto the output array. The deal is, + // the data is either an array of integer, representing binary or it + // is a string in which case it's UTF8 encoded. + // Two things to considered: + // - we should be able to send mixed encodings. + // - we don't want to call connection.send("smallstring") because that + // is wasteful. *I think* its rather faster to concat inside of JS + // Thus I attempt to concat as much as possible. function send (data) { - if (responses[0] === this) { - connection.send(data); - } else { - this.output += data; + if (output.length == 0) { + output.push(data); + return; } + + var li = output.length-1; + + if (data.constructor == String && output[li].constructor == String) { + output[li] += data; + return; + } + + if (data.constructor == Array && output[li].constructor == Array) { + output[li] = output[li].concat(data); + return; + } + + // If the string is small enough, just convert it to binary + if (data.constructor == String + && data.length < 128 + && output[li].constructor == Array) + { + output[li] = output[li].concat(toRaw(data)); + return; + } + + output.push(data); + }; + + this.flush = function () { + if (responses.length > 0 && responses[0] === this) + while (output.length > 0) + connection.send(output.shift()); }; this.sendStatus = function (status, reason) { // XXX http/1.0 until i get the keep-alive logic below working. - this.output += "HTTP/1.0 " + status.toString() + " " + reason + "\r\n"; + send("HTTP/1.0 " + status.toString() + " " + reason + "\r\n"); }; var chunked_encoding = false; var connection_close = false; this.sendHeader = function (field, value) { - this.output += field + ": " + value.toString() + "\r\n"; + send(field + ": " + value.toString() + "\r\n"); + if (/Connection/i.exec(field) && /close/i.exec(value)) connection_close = true; + else if (/Transfer-Encoding/i.exec(field) && /chunk/i.exec(value)) chunked_encoding = true; - }; var bodyBegan = false; - function toRaw(string) { - var a = []; - for (var i = 0; i < string.length; i++) - a.push(string.charCodeAt(i)); - return i; - } - - function chunkEncode (chunk) { - var hexlen = chunk.length.toString(16); - if (chunk.constructor === String) - return hexlen + "\r\n" + chunk + "\r\n"; - // er.. - } - this.sendBody = function (chunk) { if (bodyBegan === false) { - this.output += "\r\n"; + send("\r\n"); bodyBegan = true; } - if (chunked_encoding) - this.output += chunkEncode(chunk) - else - this.output += chunk; - - if (responses[0] === this) { - connection.send(this.output); - this.output = ""; + if (chunked_encoding) { + send(chunk.length.toString(16)); + send("\r\n"); + send(chunk); + send("\r\n"); + } else { + send(chunk); } + + this.flush(); }; var finished = false; this.finish = function () { if (chunked_encoding) - this.output += "0\r\n\r\n"; // last chunk + send("0\r\n\r\n"); // last chunk this.finished = true; while (responses.length > 0 && responses[0].finished) { - var res = responses.shift(); - connection.send(res.output); + var res = responses[0]; + res.flush(); + responses.shift(); } if (responses.length == 0) { @@ -104,7 +137,7 @@ node.http.Server = function (RequestHandler, options) { this.onMessage = function ( ) { var res = new Response(); - var req = new RequestHandler(res); + var req = new RequestHandler(res, connection); this.encoding = req.encoding || "raw"; -- 2.7.4