Browse Source

events for http.Server

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

416
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,108 +338,113 @@ 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 client.addListener("Disconnect", function (had_error) {
, httpVersion : null // at onHeadersComplete if (had_error) {
, headers : [] // at onHeaderField, onHeaderValue client.emit("Error");
, onBody : null // by user return;
, 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) { //node.debug("HTTP CLIENT onDisconnect. readyState = " + client.readyState);
req.uri += data; // If there are more requests to handle, reconnect.
return !interrupted; if (requests.length > 0) {
}; //node.debug("HTTP CLIENT: reconnecting");
client.connect(port, host);
}
});
var req, res;
var last_was_value = false; client.addListener("MessageBegin", function () {
var headers = req.headers; req = requests.shift();
res = createClientResponse(client);
});
this.onHeaderField = function (data) { client.addListener("HeaderField", function (data) {
if (headers.length > 0 && last_was_value == false) if (res.headers.length > 0 && res.last_was_value == false)
headers[headers.length-1][0] += data; res.headers[res.headers.length-1][0] += data;
else else
headers.push([data]); res.headers.push([data]);
last_was_value = false; res.last_was_value = false;
return !interrupted; });
};
this.onHeaderValue = function (data) { client.addListener("HeaderValue", function (data) {
var last_pair = headers[headers.length-1]; var last_pair = res.headers[res.headers.length-1];
if (last_pair.length == 1) if (last_pair.length == 1) {
last_pair[1] = data; last_pair[1] = data;
else } else {
last_pair[1] += data; last_pair[1] += data;
last_was_value = true; }
return !interrupted; res.last_was_value = true;
}; });
this.onHeadersComplete = function () { client.addListener("HeadersComplete", function (info) {
req.httpVersion = this.httpVersion; res.statusCode = info.statusCode;
req.method = this.method; res.httpVersion = info.httpVersion;
// 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]); req.emit("Response", [res]);
});
return !interrupted; client.addListener("Body", function (chunk) {
}; res.emit("Body", [chunk]);
});
this.onBody = function (chunk) { client.addListener("MessageComplete", function () {
if (req.onBody) req.onBody(chunk); client.close();
return !interrupted; res.emit("BodyComplete");
}; });
this.onMessageComplete = function () { return client;
if (req.onBodyComplete) req.onBodyComplete(); };
return !interrupted;
}; node.http.Client.prototype.get = function (uri, headers) {
}; return createClientRequest(this, "GET", uri, headers);
};
node.http.Client.prototype.head = function (uri, headers) {
return createClientRequest(this, "HEAD", uri, headers);
};
node.http.Client.prototype.post = function (uri, headers) {
return createClientRequest(this, "POST", uri, headers);
};
node.http.Client.prototype.del = function (uri, headers) {
return createClientRequest(this, "DELETE", uri, headers);
};
node.http.Client.prototype.put = function (uri, headers) {
return createClientRequest(this, "PUT", uri, headers);
}; };
node.http.Client = function (port, host) { function createClientRequest (connection, method, uri, header_lines) {
var connection = new node.http.LowLevelClient(); var req = new node.EventEmitter;
var requests = []; var requests = connection.requests;
var self = this;
function ClientRequest (method, uri, header_lines) { requests.push(this);
this.uri = uri;
req.uri = uri;
var chunked_encoding = false; var chunked_encoding = false;
this.closeOnFinish = false; req.closeOnFinish = false;
var sent_connection_header = false; var sent_connection_header = false;
var sent_transfer_encoding_header = false; var sent_transfer_encoding_header = false;
@ -344,7 +461,7 @@ node.http.Client = function (port, host) {
if (connection_expression.exec(field)) { if (connection_expression.exec(field)) {
sent_connection_header = true; sent_connection_header = true;
if (close_expression.exec(value)) this.closeOnFinish = true; if (close_expression.exec(value)) req.closeOnFinish = true;
} else if (transfer_encoding_expression.exec(field)) { } else if (transfer_encoding_expression.exec(field)) {
sent_transfer_encoding_header = true; sent_transfer_encoding_header = true;
if (chunk_expression.exec(value)) chunked_encoding = true; if (chunk_expression.exec(value)) chunked_encoding = true;
@ -362,7 +479,7 @@ node.http.Client = function (port, host) {
var output = []; var output = [];
send(output, header); send(output, header);
this.sendBody = function (chunk, encoding) { req.sendBody = function (chunk, encoding) {
if (sent_content_length_header == false && chunked_encoding == false) { if (sent_content_length_header == false && chunked_encoding == false) {
throw "Content-Length header (or Transfer-Encoding:chunked) not set"; throw "Content-Length header (or Transfer-Encoding:chunked) not set";
return; return;
@ -377,16 +494,16 @@ node.http.Client = function (port, host) {
send(output, chunk, encoding); send(output, chunk, encoding);
} }
this.flush(); req.flush();
}; };
this.flush = function ( ) { req.flush = function ( ) {
if (connection.readyState == "closed") { if (connection.readyState == "closed") {
connection.connect(port, host); connection.reconnect();
return; return;
} }
//node.debug("HTTP CLIENT flush. readyState = " + connection.readyState); //node.debug("HTTP CLIENT flush. readyState = " + connection.readyState);
while ( this === requests[0] while ( req === requests[0]
&& output.length > 0 && output.length > 0
&& connection.readyState == "open" && connection.readyState == "open"
) )
@ -396,131 +513,23 @@ node.http.Client = function (port, host) {
} }
}; };
this.finished = false; req.finished = false;
this.finish = function (responseHandler) {
this.responseHandler = responseHandler;
if (chunked_encoding)
send(output, "0\r\n\r\n"); // last chunk
this.flush(); req.finish = function (responseListener) {
}; req.addListener("Response", responseListener);
}
connection.onConnect = function () { if (chunked_encoding)
//node.debug("HTTP CLIENT onConnect. readyState = " + connection.readyState); send(output, "0\r\n\r\n"); // last chunk
//node.debug("requests[0].uri = '" + requests[0].uri + "'");
requests[0].flush();
};
connection.onEOF = function () {
connection.close();
};
connection.onDisconnect = function (had_error) {
if (had_error) {
if (self.onError) self.onError();
return;
}
//node.debug("HTTP CLIENT onDisconnect. readyState = " + connection.readyState);
// If there are more requests to handle, reconnect.
if (requests.length > 0) {
//node.debug("HTTP CLIENT: reconnecting");
this.connect(port, host);
}
};
// 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) {
var req = new ClientRequest(method, uri, headers);
requests.push(req);
return req; return req;
} }
this.get = function (uri, headers) {
return newRequest("GET", uri, headers);
};
this.head = function (uri, headers) {
return newRequest("HEAD", uri, headers);
};
this.post = function (uri, headers) {
return newRequest("POST", uri, headers);
};
this.del = function (uri, headers) {
return newRequest("DELETE", uri, headers);
};
this.put = function (uri, headers) {
return newRequest("PUT", uri, headers);
};
};
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