From 96f08cf05c3bd5ee291e85e2135d8020dd7f662a Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 12 Mar 2010 18:39:02 -0800 Subject: [PATCH] Work on net2 http client --- lib/http2.js | 189 +++++++++++++++++++++++++++++++++++++++++++ lib/net.js | 18 +++-- test/simple/test-http-1.0.js | 2 +- test/simple/test-http-cat.js | 2 +- 4 files changed, 201 insertions(+), 10 deletions(-) diff --git a/lib/http2.js b/lib/http2.js index 8b26a3f..c040dd2 100644 --- a/lib/http2.js +++ b/lib/http2.js @@ -447,3 +447,192 @@ exports.createServer = function (requestListener, options) { }; + +function Client () { + net.Socket.call(this); + + var self = this; + var requests = []; + var currentRequest; + + var parser = newParser('response'); + parser.socket = self; + + self.addListener("connect", function () { + self.resetParser(); + currentRequest = requests.shift(); + currentRequest.flush(); + }); + + self.ondata = function (d, start, end) { + parser.execute(d, start, end - start); + }; + + parser.onIncoming = function (res) { + //sys.debug("incoming response!"); + + res.addListener('end', function ( ) { + //sys.debug("request complete disconnecting. readyState = " + self.readyState); + self.close(); + }); + + currentRequest.emit("response", res); + }; + + self._pushRequest = function (req) { + }; + + self.addListener("end", function () { + self.close(); + }); + + self.onend = function () { + parser.finish(); + // unref the parser for easy gc + freeParser(parser); + //sys.debug("self got end closing. readyState = " + self.readyState); + self.close(); + }; + + self.addListener("close", function (had_error) { + if (had_error) { + self.emit("error"); + return; + } + + //sys.debug("HTTP CLIENT onClose. readyState = " + self.readyState); + + // If there are more requests to handle, reconnect. + if (requests.length > 0) { + self._reconnect(); + } + }); +} +sys.inherits(Client, net.Socket); + + +exports.Client = Client; + + +exports.createClient = function (port, host) { + var client = new Client(); + client.port = port; + client.host = host; + client.connect(port, host); + return client; +}; + + +Client.prototype._reconnect = function () { + if (this.readyState != "opening") { + //sys.debug("HTTP CLIENT: reconnecting readyState = " + self.readyState); + this.connect(this.port, this.host); + } +}; + + +Client.prototype.request = function (method, url, headers) { + var self = this; + + if (typeof(url) != "string") { // assume method was omitted, shift arguments + headers = url; + url = method; + method = null; + } + var req = new ClientRequest(this, method || "GET", url, headers); + + req.addListener("flush", function () { + if (self.readyState == "closed") { + //sys.debug("HTTP CLIENT request flush. reconnect. readyState = " + self.readyState); + self._reconnect(); + return; + } + //sys.debug("self flush readyState = " + self.readyState); + if (req == currentRequest) flushMessageQueue(self, [req]); + }); + requests.push(req); + + return req; +}; + +Client.prototype.get = function () { + throw new Error("client.get(...) is now client.request('GET', ...)"); +}; + +Client.prototype.head = function () { + throw new Error("client.head(...) is now client.request('HEAD', ...)"); +}; + +Client.prototype.post = function () { + throw new Error("client.post(...) is now client.request('POST', ...)"); +}; + +Client.prototype.del = function () { + throw new Error("client.del(...) is now client.request('DELETE', ...)"); +}; + +Client.prototype.put = function () { + throw new Error("client.put(...) is now client.request('PUT', ...)"); +}; + + +exports.cat = function (url, encoding_, headers_) { + var encoding = 'utf8', + headers = {}, + callback = null; + + // parse the arguments for the various options... very ugly + if (typeof(arguments[1]) == 'string') { + encoding = arguments[1]; + if (typeof(arguments[2]) == 'object') { + headers = arguments[2]; + if (typeof(arguments[3]) == 'function') callback = arguments[3]; + } else { + if (typeof(arguments[2]) == 'function') callback = arguments[2]; + } + } else { + // didn't specify encoding + if (typeof(arguments[1]) == 'object') { + headers = arguments[1]; + callback = arguments[2]; + } else { + callback = arguments[1]; + } + } + + var url = require("url").parse(url); + + var hasHost = false; + for (var i in headers) { + if (i.toLowerCase() === "host") { + hasHost = true; + break; + } + } + if (!hasHost) headers["Host"] = url.hostname; + + var content = ""; + + var client = exports.createClient(url.port || 80, url.hostname); + var req = client.request((url.pathname || "/")+(url.search || "")+(url.hash || ""), headers); + + req.addListener('response', function (res) { + if (res.statusCode < 200 || res.statusCode >= 300) { + if (callback) callback(res.statusCode); + client.close(); + return; + } + res.setBodyEncoding(encoding); + res.addListener('data', function (chunk) { content += chunk; }); + res.addListener('end', function () { + if (callback) callback(null, content); + }); + }); + + client.addListener("error", function (err) { + // todo an error should actually be passed here... + if (callback) callback(new Error('Connection error')); + }); + + req.close(); +}; diff --git a/lib/net.js b/lib/net.js index 1b2b348..fe5fa0d 100644 --- a/lib/net.js +++ b/lib/net.js @@ -632,19 +632,16 @@ function doConnect (socket, port, host) { // stream.connect(80, 'nodejs.org') - TCP connect to port 80 on nodejs.org // stream.connect('/tmp/socket') - UNIX connect to socket specified by path Socket.prototype.connect = function () { - initSocket(this); - var self = this; + initSocket(self); if (self.fd) throw new Error('Socket already opened'); + if (!self._readWatcher) throw new Error('No readWatcher'); timeout.active(socket); - if (typeof(arguments[0]) == 'string') { - self.fd = socket('unix'); - self.type = 'unix'; - // TODO check if sockfile exists? - doConnect(self, arguments[0]); - } else { + var port = parseInt(arguments[0]); + + if (port >= 0) { self.fd = socket('tcp'); debug('new fd = ' + self.fd); self.type = 'tcp'; @@ -653,6 +650,11 @@ Socket.prototype.connect = function () { lookupDomainName(arguments[1], function (ip) { doConnect(self, port, ip); }); + } else { + self.fd = socket('unix'); + self.type = 'unix'; + // TODO check if sockfile exists? + doConnect(self, arguments[0]); } }; diff --git a/test/simple/test-http-1.0.js b/test/simple/test-http-1.0.js index ceca527..b3f69c2 100644 --- a/test/simple/test-http-1.0.js +++ b/test/simple/test-http-1.0.js @@ -1,6 +1,6 @@ require("../common"); tcp = require("tcp"); -http = require("http"); +http = require("http2"); var body = "hello world\n"; var server_response = ""; diff --git a/test/simple/test-http-cat.js b/test/simple/test-http-cat.js index e81a5c5..32177ee 100644 --- a/test/simple/test-http-cat.js +++ b/test/simple/test-http-cat.js @@ -1,5 +1,5 @@ require("../common"); -http = require("http"); +http = require("http2"); var body = "exports.A = function() { return 'A';}"; var server = http.createServer(function (req, res) { -- 2.7.4