#include <assert.h>
#include <stdio.h>
+#include <strings.h>
-#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")
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<HTTPConnection*> (parser->data);
HandleScope scope;
if (on_body_v->IsFunction() == false) return 0;
Handle<Function> on_body = Handle<Function>::Cast(on_body_v);
- Handle<Value> 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<Value> encoding_v = message_handler->Get(ENCODING_SYMBOL);
+ if (encoding_v->IsString()) {
+ Local<String> 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<Value> 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<String> chunk = String::New((const char*)buf, len);
argv[0] = chunk;
* 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) {
this.onMessage = function ( ) {
var res = new Response();
- var req = new RequestHandler(res);
+ var req = new RequestHandler(res, connection);
this.encoding = req.encoding || "raw";