(function () {
+CRLF = "\r\n";
node.http.STATUS_CODES = { 100 : 'Continue'
, 101 : 'Switching Protocols'
, 200 : 'OK'
+ status_code.toString()
+ " "
+ reason
- + "\r\n"
+ + CRLF
;
for (var i = 0; i < headers.length; i++) {
var field = headers[i][0];
var value = headers[i][1];
- header += field + ": " + value + "\r\n";
+ header += field + ": " + value + CRLF;
if (connection_expression.exec(field)) {
sent_connection_header = true;
chunked_encoding = true;
}
- header += "\r\n";
+ header += CRLF;
send(header);
};
this.sendBody = function (chunk) {
if (chunked_encoding) {
send(chunk.length.toString(16));
- send("\r\n");
+ send(CRLF);
send(chunk);
- send("\r\n");
+ send(CRLF);
} else {
send(chunk);
}
this.onHeadersComplete = function () {
req.http_version = this.http_version;
req.method = this.method;
- req.uri = parseUri(req.uri); // TODO parse the URI lazily
+ req.uri = node.http.parseUri(req.uri); // TODO parse the URI lazily
res.should_keep_alive = this.should_keep_alive;
new node.http.LowLevelServer(ConnectionHandler, options);
};
-node.http.Client = function (port, host) {
- var port = port;
- var host = host;
+node.http.Client = function (port, host, options) {
+ var connection = new node.http.LowLevelClient(options);
+ var requests = [];
- var pending_responses = [];
- function Message (method, uri, header_lines) {
- pending_responses.push(this);
-
- this.method = method;
- this.path = path;
- this.headers = headers;
+ function ClientRequest (method, uri, header_lines) {
var chunked_encoding = false;
var connection_close = false;
var header = method + " " + uri + " HTTP/1.1\r\n";
+ header_lines = header_lines || [];
for (var i = 0; i < header_lines.length; i++) {
var field = header_lines[i][0];
var value = header_lines[i][1];
- header += field + ": " + value + "\r\n";
+ header += field + ": " + value + CRLF;
if (connection_expression.exec(field)) {
sent_connection_header = true;
header += "Transfer-Encoding: chunked\r\n";
chunked_encoding = true;
}
- header += "\r\n";
+ header += CRLF;
var output = [header];
+ function send (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.sendBody = function (chunk) {
+ if (chunked_encoding) {
+ send(chunk.length.toString(16));
+ send(CRLF);
+ send(chunk);
+ send(CRLF);
+ } else {
+ send(chunk);
+ }
+
+ this.flush();
};
- this.finish = function () {
+ this.flush = function ( ) {
+ if (connection.readyState !== "open") {
+ connection.connect(port, host);
+ return;
+ }
+ while (this === requests[0] && output.length > 0) {
+ connection.send(output.shift());
+ }
+ };
+
+ this.finished = false;
+ this.finish = function (responseHandler) {
+ this.responseHandler = responseHandler;
+ if (chunked_encoding)
+ send("0\r\n\r\n"); // last chunk
+
+ this.flush();
};
}
- function spawn_client ( ) {
- var c = new node.http.LowLevelClient();
+ connection.onConnect = function () {
+ puts("HTTP CLIENT: connected");
+ requests[0].flush();
+ };
+
+ connection.onDisconnect = function () {
+ // If there are more requests to handle, reconnect.
+ if (requests.length > 0) {
+ puts("HTTP CLIENT: reconnecting");
+ connection.connect(port, host);
+ }
+ };
- c.onConnect = function () {
+ // On response
+ connection.onMessage = function () {
+ var req = requests.shift();
+ var res = { status_code : null // set in onHeadersComplete
+ , http_version : null // set in onHeadersComplete
+ , headers : [] // set in onHeaderField/Value
+ };
+
+ var headers = res.headers;
+ var last_was_value = false;
+
+ this.onHeaderField = function (data) {
+ if (headers.length > 0 && last_was_value == false)
+ headers[headers.length-1][0] += data;
+ else
+ headers.push([data]);
+ last_was_value = false;
+ return true;
};
- // On response
- c.onMessage = function () {
+ this.onHeaderValue = function (data) {
+ var last_pair = headers[headers.length-1];
+ if (last_pair.length == 1)
+ last_pair[1] = data;
+ else
+ last_pair[1] += data;
+ last_was_value = true;
+ return true;
};
+
+ this.onHeadersComplete = function () {
+ res.status_code = this.status_code;
+ res.http_version = this.http_version;
+ res.headers = headers;
+
+ req.responseHandler(res);
+ };
+
+ this.onBody = function (chunk) {
+ if (res.onBody)
+ return res.onBody(chunk);
+ else
+ return true;
+ };
+
+ this.onMessageComplete = function () {
+ connection.close();
+
+ if (res.onBodyComplete)
+ return res.onBodyComplete();
+ else
+ return true;
+ };
+ };
+
+ function newRequest (method, uri, headers) {
+ var req = new ClientRequest(method, uri, headers);
+ requests.push(req);
+ return req;
}
- this.get = function (path, headers) {
- var m = new Message("GET", path, headers);
- m.finish();
- return m;
+ this.get = function (uri, headers) {
+ return newRequest("GET", uri, headers);
};
- this.head = function (path, headers) {
- var m = new Message("HEAD", path, headers);
- m.finish();
- return m;
+ this.head = function (uri, headers) {
+ return newRequest("HEAD", uri, headers);
};
- this.post = function (path, headers) {
- return new Message("POST", path, headers);
+ this.post = function (uri, headers) {
+ return newRequest("POST", uri, headers);
};
- this.del = function (path, headers) {
- return new Message("DELETE", path, headers);
+ this.del = function (uri, headers) {
+ return newRequest("DELETE", uri, headers);
};
- this.put = function (path, headers) {
- return new Message("PUT", path, headers);
+ this.put = function (uri, headers) {
+ return newRequest("PUT", uri, headers);
};
};