Skip to content

Commit

Permalink
HTTP: initial Agent implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Gábor Molnár committed Aug 17, 2013
1 parent cb27799 commit 234def4
Showing 1 changed file with 102 additions and 42 deletions.
144 changes: 102 additions & 42 deletions lib/http.js
Expand Up @@ -257,63 +257,123 @@ ServerResponse.prototype.writeHead = function writeHead(statusCode, reasonPhrase
// ======

http2.request = function request(options, callback) {
return globalAgent.request(options, callback);
};

http2.get = function get(options, callback) {
};

// Agent class
// -----------

function Agent(options) {
EventEmitter.call(this);

this._options = options || {};
this._log = (this._options.log || logging.root).child({ component: 'http' });
this._endpoints = {};
this._settings = this._options.settings || default_settings;

// * Using an own HTTPS agent, because the global agent does not look at `NPNProtocols` when
// generating the key identifying the connection, so we may get useless non-negotiated TLS
// channels even if we ask for a negotiated one. This agent will contain only negotiated
// channels.
this._httpsAgent = new https.Agent({
NPNProtocols: [implementedVersion, 'http/1.1', 'http/1.0']
});
}
Agent.prototype = Object.create(EventEmitter.prototype, { constructor: { value: Agent } });

Agent.prototype.request = function request(options, callback) {
if (options.protocol === 'http') {
this._log.error('Trying to negotiate client request with Upgrade from HTTP/1.1');
throw new Error('HTTP1.1 -> HTTP2 upgrade is not yet supported.');
}

var request = new ClientRequest(logging.root);

if (callback) {
request.on('response', callback);
}

var tlsOptions = {
host: options.hostname || options.host,
port: options.port || 80,
NPNProtocols: [implementedVersion, 'http/1.1', 'http/1.0']
};

var optionsToForward = [
'pfx',
'key',
'passphrase',
'cert',
'ca',
'ciphers',
'rejectUnauthorized',
'secureProtocol'
];
for (var i = 0; i < optionsToForward.length; i++) {
var key = optionsToForward[i];
if (key in options) {
tlsOptions[key] = options[key];
}
if (options.protocol === 'http') {
throw new Error('Protocol:' + options.protocol + ' not supported.');
}
if (options.port === undefined) {
options.port = 433;
}

var socket = tls.connect(tlsOptions, function() {
// HTTP2 is supported!
if (socket.npnProtocol === implementedVersion) {
var endpoint = new Endpoint('CLIENT', options._settings || default_settings);
endpoint.pipe(socket).pipe(endpoint);
request._start(endpoint.createStream(), options);
}
var key = [
options.host || 'localhost',
options.port
].join(':');

// Fallback
else {
socket.end();
request._fallback(https.request(options));
}
});
// * There's an existing HTTP/2 connection to this host
if (key in this._endpoints) {
var endpoint = this._endpoints[key];
request._start(endpoint.createStream(), options);
}

return request;
};
// * HTTP/2 over TLS negotiated using NPN (or later ALPN)
// * if the negotiation is unsuccessful
// * adding socket to the HTTPS agent's socket pool
// * initiating a request with the HTTPS agent
// * calling request's fallback() to fall back to use the new request object
else {
var started = false;
options.NPNProtocols = [implementedVersion, 'http/1.1', 'http/1.0'];
options.agent = this._httpsAgent;
var httpsRequest = https.request(options);
httpsRequest.on('socket', function(socket) {
if (socket.npnProtocol !== undefined) {
negotiated();
} else {
socket.on('secureConnect', negotiated);
}
});

var negotiated = function negotiated() {
if (!started) {
if (httpsRequest.socket.npnProtocol === implementedVersion) {
httpsRequest.socket.emit('agentRemove');
unbundleSocket(httpsRequest.socket);
var logger = this._log.child({ server: options.host + ':' + options.port });
var endpoint = new Endpoint('CLIENT', this._settings, logger);
endpoint.pipe(httpsRequest.socket).pipe(endpoint);
this._endpoints[key] = endpoint;
this.emit(key, endpoint);
} else {
this.emit(key, undefined);
}
}
}.bind(this);

this.once(key, function(endpoint) {
started = true;
if (endpoint) {
request._start(endpoint.createStream(), options);
} else {
request._fallback(httpsRequest);
}
});
}

http2.get = function get(options, callback) {
return request;
};

// Agent class
// -----------

function Agent(options) {

function unbundleSocket(socket) {
socket.removeAllListeners('data');
socket.removeAllListeners('end');
socket.removeAllListeners('readable');
socket.removeAllListeners('close');
socket.removeAllListeners('error');
socket.unpipe();
delete socket.ondata;
delete socket.onend;
}

var globalAgent = http2.globalAgent = new Agent();

// ClientRequest
// -------------

Expand Down

0 comments on commit 234def4

Please sign in to comment.