Browse Source

Use old http.Client

This is meant as a path for upgrading to the new http.request() API.
http.Client will be disappearing in the future.
v0.7.4-release
Ryan Dahl 14 years ago
parent
commit
39ff40e5a2
  1. 281
      lib/http.js

281
lib/http.js

@ -1204,87 +1204,219 @@ exports.get = function(options, cb) {
}; };
// Shims to old interface.
function Client(port, host) { // Legacy Interface:
function Client() {
if (!(this instanceof Client)) return new Client();
net.Stream.call(this, { allowHalfOpen: true });
var self = this; var self = this;
this.port = port; // Possible states:
this.host = host; // - disconnected
this.agent = getAgent(this.host, this.port); // - connecting
// - connected
this._state = 'disconnected';
// proxy connect events upwards; httpSocketSetup(self);
this.agent.on('connect', function() { this._outgoing = [];
self.emit('connect');
});
this.agent.on('end', function() { function onData(d, start, end) {
self.emit('end'); if (!self.parser) {
}); throw new Error('parser not initialized prior to Client.ondata call');
}
var ret = self.parser.execute(d, start, end - start);
if (ret instanceof Error) {
self.destroy(ret);
} else if (self.parser.incoming && self.parser.incoming.upgrade) {
var bytesParsed = ret;
self.ondata = null;
self.onend = null;
self._state = 'upgraded';
var res = self.parser.incoming;
if (self._httpMessage) {
self._httpMessage.detachSocket(self);
}
var upgradeHead = d.slice(start + bytesParsed + 1, end);
// proxy upgrade events upwards;
this.agent.on('upgrade', function(res, socket, upgradeHead) {
if (self.listeners('upgrade').length) { if (self.listeners('upgrade').length) {
self.emit('upgrade', res, socket, upgradeHead); self.emit('upgrade', res, self, upgradeHead);
} else { } else {
socket.destroy(); self.destroy();
}
}
};
self.addListener('connect', function() {
debug('CLIENT connected');
self.ondata = onData;
self.onend = onEnd;
self._state = 'connected';
self._initParser();
self._cycle();
});
function onEnd() {
if (self.parser) self.parser.finish();
debug('CLIENT got end closing. state = ' + self._state);
self.end();
};
self.addListener('close', function(e) {
self._state = 'disconnected';
self._upgraded = false;
// Free the parser.
if (self.parser) {
parsers.free(self.parser);
self.parser = null;
}
if (e) return;
// If we have an http message, then drop it
var req = self._httpMessage;
if (req && !req.res) {
req.detachSocket(self);
self.emit('error', new Error('socket hang up'));
} }
debug('CLIENT onClose. state = ' + self._state);
self._cycle();
}); });
} }
util.inherits(Client, EventEmitter); util.inherits(Client, net.Stream);
exports.Client = Client;
exports.createClient = function(port, host) {
var c = new Client();
c.port = port;
c.host = host;
return c;
};
Client.prototype._initParser = function() {
var self = this;
if (!self.parser) self.parser = parsers.alloc();
self.parser.reinitialize('response');
self.parser.socket = self;
self.parser.onIncoming = function(res) {
debug('CLIENT incoming response!');
assert(self._httpMessage);
var req = self._httpMessage;
req.res = res;
// Responses to HEAD requests are AWFUL. Ask Ryan.
// A major oversight in HTTP. Hence this nastiness.
var isHeadResponse = req.method == 'HEAD';
debug('CLIENT isHeadResponse ' + isHeadResponse);
if (res.statusCode == 100) {
// restart the parser, as this is a continue message.
req.emit('continue');
return true;
}
if (req.shouldKeepAlive && res.headers.connection === 'close') {
req.shouldKeepAlive = false;
}
res.addListener('end', function() {
debug('CLIENT response complete disconnecting. state = ' + self._state);
// This method is used in a few tests to force the connections closed. if (!req.shouldKeepAlive) {
// Again - just a shim so as not to break code. Not really important. self.end();
Client.prototype.end = function() {
for (var i = 0; i < this.agent.sockets.length; i++) {
var socket = this.agent.sockets[i];
if (!socket._httpMessage && socket.writable) socket.end();
} }
req.detachSocket(self);
assert(!self._httpMessage);
self._cycle();
});
req.emit('response', res);
return isHeadResponse;
};
}; };
Client.prototype.destroy = function(e) { Client.prototype._cycle = function() {
for (var i = 0; i < this.agent.sockets.length; i++) { debug("Client _cycle");
var socket = this.agent.sockets[i]; if (this._upgraded) return;
socket.destroy(e);
switch (this._state) {
case 'connecting':
break;
case 'connected':
if (this.writable && this.readable) {
debug("Client _cycle shift()");
if (this._httpMessage) {
this._httpMessage._flush();
} else {
var req = this._outgoing.shift();
if (req) req.assignSocket(this);
}
}
break;
case 'disconnected':
if (this._httpMessage || this._outgoing.length) {
this._ensureConnection();
}
break;
} }
}; };
Client.prototype.request = function(method, path, headers) { Client.prototype._ensureConnection = function() {
if (typeof(path) != 'string') { if (this._state == 'disconnected') {
debug('CLIENT reconnecting state = ' + this._state);
this.connect(this.port, this.host);
this._state = 'connecting';
}
};
Client.prototype.request = function(method, url, headers) {
if (typeof(url) != 'string') {
// assume method was omitted, shift arguments // assume method was omitted, shift arguments
headers = path; headers = url;
path = method; url = method;
method = 'GET'; method = 'GET';
} }
var self = this;
var options = { var options = {
method: method, method: method || 'GET',
path: path, path: url || '/',
headers: headers, headers: headers
port: this.port,
host: this.host
}; };
var self = this; var req = new ClientRequest(options);
var req = exports.request(options); this._outgoing.push(req);
this._cycle();
// proxy error events from req to Client
req.on('error', function(err) {
self.emit('error', err);
});
return req; return req;
}; };
exports.createClient = function(port, host) {
return new Client(port, host);
};
exports.cat = function(url, encoding_, headers_) { exports.cat = function(url, encoding_, headers_) {
var encoding = 'utf8', var encoding = 'utf8',
headers = {}, headers = {},
@ -1311,26 +1443,49 @@ exports.cat = function(url, encoding_, headers_) {
var url = require('url').parse(url); var url = require('url').parse(url);
var options = { var hasHost = false;
method: 'GET', if (Array.isArray(headers)) {
port: url.port || 80, for (var i = 0, l = headers.length; i < l; i++) {
host: url.hostname, if (headers[i][0].toLowerCase() === 'host') {
headers: headers, hasHost = true;
path: (url.pathname || '/') + (url.search || '') + (url.hash || '') break;
}; }
}
} else if (typeof headers === 'Object') {
var keys = Object.keys(headers);
for (var i = 0, l = keys.length; i < l; i++) {
var key = keys[i];
if (key.toLowerCase() == 'host') {
hasHost = true;
break;
}
}
}
if (!hasHost) headers['Host'] = url.hostname;
var content = ''; var content = '';
var client = exports.createClient(url.port || 80, url.hostname);
var req = client.request((url.pathname || '/') +
(url.search || '') +
(url.hash || ''),
headers);
if (url.protocol == 'https:') {
client.https = true;
}
var callbackSent = false; var callbackSent = false;
var req = exports.request(options, function(res) { req.addListener('response', function(res) {
if (res.statusCode < 200 || res.statusCode >= 300) { if (res.statusCode < 200 || res.statusCode >= 300) {
if (callback && !callbackSent) { if (callback && !callbackSent) {
callback(res.statusCode); callback(res.statusCode);
callbackSent = true; callbackSent = true;
} }
client.end();
return; return;
} }
res.setEncoding(encoding); res.setEncoding(encoding);
res.addListener('data', function(chunk) { content += chunk; }); res.addListener('data', function(chunk) { content += chunk; });
res.addListener('end', function() { res.addListener('end', function() {
@ -1340,13 +1495,19 @@ exports.cat = function(url, encoding_, headers_) {
} }
}); });
}); });
req.end();
client.addListener('error', function(err) {
req.on('error', function(err) {
if (callback && !callbackSent) { if (callback && !callbackSent) {
callback(err); callback(err);
callbackSent = true; callbackSent = true;
} }
}); });
client.addListener('close', function() {
if (callback && !callbackSent) {
callback(new Error('Connection closed unexpectedly'));
callbackSent = true;
}
});
req.end();
}; };

Loading…
Cancel
Save