Browse Source

events for http.Server

v0.7.4-release
Ryan 16 years ago
parent
commit
20c0e1fdfb
  1. 504
      src/http.js

504
src/http.js

@ -123,7 +123,119 @@ function send (output, data, encoding) {
output.push([data, encoding]); output.push([data, encoding]);
}; };
node.http.ServerResponse = function (connection, responses) { /* This is a wrapper around the LowLevelServer interface. It provides
* connection handling, overflow checking, and some data buffering.
*/
node.http.createServer = function (requestListener, options) {
var server = new node.http.LowLevelServer();
server.addListener("Connection", connectionListener);
server.addListener("Request", requestListener);
//server.setOptions(options);
return server;
};
node.http.createServerRequest = function (connection) {
var req = new node.EventEmitter;
req.connection = connection;
req.method = null;
req.uri = "";
req.httpVersion = null;
req.headers = [];
req.last_was_value = false; // used internally XXX remove me
req.setBodyEncoding = function (enc) {
connection.setEncoding(enc);
};
return req;
};
// ^
// |
// | combine these two functions
// |
// v
createClientResponse = function (connection) {
var res = new node.EventEmitter;
res.connection = connection;
res.statusCode = null;
res.httpVersion = null;
res.headers = [];
res.last_was_value = false; // used internally XXX remove me
res.setBodyEncoding = function (enc) {
connection.setEncoding(enc);
};
return res;
};
function connectionListener (connection) {
// An array of responses for each connection. In pipelined connections
// we need to keep track of the order they were sent.
connection.responses = [];
// is this really needed?
connection.addListener("EOF", function () {
if (connection.responses.length == 0) {
connection.close();
} else {
connection.responses[connection.responses.length-1].closeOnFinish = true;
}
});
var req, res;
connection.addListener("MessageBegin", function () {
req = new node.http.createServerRequest(connection);
res = new node.http.ServerResponse(connection);
});
connection.addListener("URI", function (data) {
req.uri += data;
});
connection.addListener("HeaderField", function (data) {
if (req.headers.length > 0 && req.last_was_value == false)
req.headers[req.headers.length-1][0] += data;
else
req.headers.push([data]);
req.last_was_value = false;
});
connection.addListener("HeaderValue", 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;
req.last_was_value = true;
});
connection.addListener("HeadersComplete", function (info) {
req.httpVersion = info.httpVersion;
req.method = info.method;
req.uri = node.http.parseUri(req.uri); // TODO parse the URI lazily?
res.should_keep_alive = info.should_keep_alive;
connection.server.emit("Request", [req, res]);
});
connection.addListener("Body", function (chunk) {
req.emit("Body", [chunk]);
});
connection.addListener("MessageComplete", function () {
req.emit("BodyComplete");
});
};
node.http.ServerResponse = function (connection) {
var responses = connection.responses;
responses.push(this); responses.push(this);
this.connection = connection; this.connection = connection;
this.closeOnFinish = false; this.closeOnFinish = false;
@ -226,301 +338,198 @@ node.http.ServerResponse = function (connection, responses) {
}; };
}; };
/* This is a wrapper around the LowLevelServer interface. It provides
* connection handling, overflow checking, and some data buffering.
*/
node.http.createServer = function (requestHandler, options) {
var server = new node.http.LowLevelServer();
server.addListener("Connection", node.http.connectionListener);
//server.setOptions(options);
server.requestHandler = requestHandler;
return server;
};
node.http.connectionListener = function (connection) { node.http.Client = node.http.LowLevelClient; // FIXME
// An array of responses for each connection. In pipelined connections
// we need to keep track of the order they were sent.
var responses = [];
// is this really needed? node.http.createClient = function (port, host) {
connection.addListener("EOF", function () { var client = new node.http.Client();
if (responses.length == 0) { var requests = client.requests = [];
connection.close();
} else { client.reconnect = function () { return client.connect(port, host) };
responses[responses.length-1].closeOnFinish = true;
} client.addListener("Connect", function () {
//node.debug("HTTP CLIENT onConnect. readyState = " + client.readyState);
//node.debug("requests[0].uri = '" + requests[0].uri + "'");
requests[0].flush();
}); });
connection.onMessage = function () { client.addListener("EOF", function () {
var interrupted = false; client.close();
// filled in ... });
var req = { method : null // at onHeadersComplete
, uri : "" // at onURI
, httpVersion : null // at onHeadersComplete
, headers : [] // at onHeaderField, onHeaderValue
, onBody : null // by user
, onBodyComplete : null // by user
, interrupt : function ( ) { interrupted = true; }
, setBodyEncoding : function (enc) {
connection.setEncoding(enc);
}
};
var res = new node.http.ServerResponse(connection, responses);
this.onURI = function (data) {
req.uri += data;
return !interrupted;
};
var last_was_value = false; client.addListener("Disconnect", function (had_error) {
var headers = req.headers; if (had_error) {
client.emit("Error");
return;
}
this.onHeaderField = function (data) { //node.debug("HTTP CLIENT onDisconnect. readyState = " + client.readyState);
if (headers.length > 0 && last_was_value == false) // If there are more requests to handle, reconnect.
headers[headers.length-1][0] += data; if (requests.length > 0) {
else //node.debug("HTTP CLIENT: reconnecting");
headers.push([data]); client.connect(port, host);
last_was_value = false; }
return !interrupted; });
};
this.onHeaderValue = function (data) { var req, res;
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 !interrupted;
};
this.onHeadersComplete = function () { client.addListener("MessageBegin", function () {
req.httpVersion = this.httpVersion; req = requests.shift();
req.method = this.method; res = createClientResponse(client);
// TODO parse the URI lazily? });
req.uri = node.http.parseUri(req.uri);
res.should_keep_alive = this.should_keep_alive;
connection.server.requestHandler.apply(connection.server, [req, res]); client.addListener("HeaderField", function (data) {
if (res.headers.length > 0 && res.last_was_value == false)
res.headers[res.headers.length-1][0] += data;
else
res.headers.push([data]);
res.last_was_value = false;
});
return !interrupted; client.addListener("HeaderValue", function (data) {
}; var last_pair = res.headers[res.headers.length-1];
if (last_pair.length == 1) {
last_pair[1] = data;
} else {
last_pair[1] += data;
}
res.last_was_value = true;
});
this.onBody = function (chunk) { client.addListener("HeadersComplete", function (info) {
if (req.onBody) req.onBody(chunk); res.statusCode = info.statusCode;
return !interrupted; res.httpVersion = info.httpVersion;
};
this.onMessageComplete = function () { req.emit("Response", [res]);
if (req.onBodyComplete) req.onBodyComplete(); });
return !interrupted;
};
};
};
node.http.Client = function (port, host) { client.addListener("Body", function (chunk) {
var connection = new node.http.LowLevelClient(); res.emit("Body", [chunk]);
var requests = []; });
var self = this;
function ClientRequest (method, uri, header_lines) { client.addListener("MessageComplete", function () {
this.uri = uri; client.close();
res.emit("BodyComplete");
});
var chunked_encoding = false; return client;
this.closeOnFinish = false; };
var sent_connection_header = false; node.http.Client.prototype.get = function (uri, headers) {
var sent_transfer_encoding_header = false; return createClientRequest(this, "GET", uri, headers);
var sent_content_length_header = false; };
var header = method + " " + uri + " HTTP/1.1\r\n"; node.http.Client.prototype.head = function (uri, headers) {
return createClientRequest(this, "HEAD", uri, headers);
};
header_lines = header_lines || []; node.http.Client.prototype.post = function (uri, headers) {
for (var i = 0; i < header_lines.length; i++) { return createClientRequest(this, "POST", uri, headers);
var field = header_lines[i][0]; };
var value = header_lines[i][1];
header += field + ": " + value + CRLF; node.http.Client.prototype.del = function (uri, headers) {
return createClientRequest(this, "DELETE", uri, headers);
};
if (connection_expression.exec(field)) { node.http.Client.prototype.put = function (uri, headers) {
sent_connection_header = true; return createClientRequest(this, "PUT", uri, headers);
if (close_expression.exec(value)) this.closeOnFinish = true; };
} else if (transfer_encoding_expression.exec(field)) {
sent_transfer_encoding_header = true;
if (chunk_expression.exec(value)) chunked_encoding = true;
} else if (content_length_expression.exec(field)) {
sent_content_length_header = true;
}
}
if (sent_connection_header == false) { function createClientRequest (connection, method, uri, header_lines) {
header += "Connection: keep-alive\r\n"; var req = new node.EventEmitter;
} var requests = connection.requests;
header += CRLF; requests.push(this);
var output = []; req.uri = uri;
send(output, header);
this.sendBody = function (chunk, encoding) { var chunked_encoding = false;
if (sent_content_length_header == false && chunked_encoding == false) { req.closeOnFinish = false;
throw "Content-Length header (or Transfer-Encoding:chunked) not set";
return;
}
if (chunked_encoding) { var sent_connection_header = false;
send(output, chunk.length.toString(16)); var sent_transfer_encoding_header = false;
send(output, CRLF); var sent_content_length_header = false;
send(output, chunk, encoding);
send(output, CRLF);
} else {
send(output, chunk, encoding);
}
this.flush(); var header = method + " " + uri + " HTTP/1.1\r\n";
};
this.flush = function ( ) { header_lines = header_lines || [];
if (connection.readyState == "closed") { for (var i = 0; i < header_lines.length; i++) {
connection.connect(port, host); var field = header_lines[i][0];
return; var value = header_lines[i][1];
}
//node.debug("HTTP CLIENT flush. readyState = " + connection.readyState);
while ( this === requests[0]
&& output.length > 0
&& connection.readyState == "open"
)
{
var out = output.shift();
connection.send(out[0], out[1]);
}
};
this.finished = false; header += field + ": " + value + CRLF;
this.finish = function (responseHandler) {
this.responseHandler = responseHandler;
if (chunked_encoding)
send(output, "0\r\n\r\n"); // last chunk
this.flush(); if (connection_expression.exec(field)) {
}; sent_connection_header = true;
if (close_expression.exec(value)) req.closeOnFinish = true;
} else if (transfer_encoding_expression.exec(field)) {
sent_transfer_encoding_header = true;
if (chunk_expression.exec(value)) chunked_encoding = true;
} else if (content_length_expression.exec(field)) {
sent_content_length_header = true;
}
} }
connection.onConnect = function () { if (sent_connection_header == false) {
//node.debug("HTTP CLIENT onConnect. readyState = " + connection.readyState); header += "Connection: keep-alive\r\n";
//node.debug("requests[0].uri = '" + requests[0].uri + "'"); }
requests[0].flush();
};
connection.onEOF = function () { header += CRLF;
connection.close();
};
connection.onDisconnect = function (had_error) { var output = [];
if (had_error) { send(output, header);
if (self.onError) self.onError();
req.sendBody = function (chunk, encoding) {
if (sent_content_length_header == false && chunked_encoding == false) {
throw "Content-Length header (or Transfer-Encoding:chunked) not set";
return; return;
} }
//node.debug("HTTP CLIENT onDisconnect. readyState = " + connection.readyState); if (chunked_encoding) {
// If there are more requests to handle, reconnect. send(output, chunk.length.toString(16));
if (requests.length > 0) { send(output, CRLF);
//node.debug("HTTP CLIENT: reconnecting"); send(output, chunk, encoding);
this.connect(port, host); send(output, CRLF);
} else {
send(output, chunk, encoding);
} }
};
// On response
connection.onMessage = function () {
var req = requests.shift();
var res = { statusCode : null // set in onHeadersComplete
, httpVersion : null // set in onHeadersComplete
, headers : [] // set in onHeaderField/Value
, setBodyEncoding : function (enc) {
connection.setEncoding(enc);
}
};
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;
};
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.statusCode = this.statusCode;
res.httpVersion = this.httpVersion;
res.headers = headers;
req.responseHandler(res);
return true;
};
this.onBody = function (chunk) {
if (res.onBody) {
return res.onBody(chunk);
} else {
return true;
}
};
this.onMessageComplete = function () { req.flush();
connection.close();
if (res.onBodyComplete) {
return res.onBodyComplete();
} else {
return true;
}
};
}; };
function newRequest (method, uri, headers) { req.flush = function ( ) {
var req = new ClientRequest(method, uri, headers); if (connection.readyState == "closed") {
requests.push(req); connection.reconnect();
return req; return;
} }
//node.debug("HTTP CLIENT flush. readyState = " + connection.readyState);
this.get = function (uri, headers) { while ( req === requests[0]
return newRequest("GET", uri, headers); && output.length > 0
&& connection.readyState == "open"
)
{
var out = output.shift();
connection.send(out[0], out[1]);
}
}; };
this.head = function (uri, headers) { req.finished = false;
return newRequest("HEAD", uri, headers);
};
this.post = function (uri, headers) { req.finish = function (responseListener) {
return newRequest("POST", uri, headers); req.addListener("Response", responseListener);
};
this.del = function (uri, headers) { if (chunked_encoding)
return newRequest("DELETE", uri, headers); send(output, "0\r\n\r\n"); // last chunk
};
this.put = function (uri, headers) { req.flush();
return newRequest("PUT", uri, headers);
}; };
};
return req;
}
node.http.cat = function (url, encoding, callback) { node.http.cat = function (url, encoding, callback) {
var uri = node.http.parseUri(url); var uri = node.http.parseUri(url);
var req = new node.http.Client(uri.port || 80, uri.host).get(uri.path || "/"); var req = node.http.createClient(uri.port || 80, uri.host).get(uri.path || "/");
req.finish(function (res) { req.finish(function (res) {
var status = res.statusCode == 200 ? 0 : -1; var status = res.statusCode == 200 ? 0 : -1;
res.setBodyEncoding(encoding); res.setBodyEncoding(encoding);
@ -535,4 +544,3 @@ node.http.cat = function (url, encoding, callback) {
}; };
})(); // anonymous namespace })(); // anonymous namespace

Loading…
Cancel
Save