}
}
+
function socketCloseListener() {
var socket = this;
var parser = socket.parser;
var req = socket._httpMessage;
debug('HTTP socket close');
+ var req = socket._httpMessage;
req.emit('close');
if (req.res && req.res.readable) {
- // Socket closed before we emitted "end" below.
+ // Socket closed before we emitted 'end' below.
req.res.emit('aborted');
- req.res._emitEnd();
- req.res.emit('close');
+ var res = req.res;
+ req.res._emitPending(function() {
+ res._emitEnd();
+ res.emit('close');
+ res = null;
+ });
} else if (!req.res && !req._hadError) {
// This socket error fired before we started to
// receive a response. The error needs to
var parser = socket.parser;
var req = socket._httpMessage;
debug('HTTP SOCKET ERROR: ' + err.message + '\n' + err.stack);
+
if (req) {
req.emit('error', err);
// For Safety. Some additional errors might fire later on
// and we need to make sure we don't double-fire the error event.
req._hadError = true;
}
+
if (parser) {
parser.finish();
freeParser(parser, req);
socket.destroy();
}
+function socketOnEnd() {
+ var socket = this;
+ var req = this._httpMessage;
+ var parser = this.parser;
-function responseOnEnd() {
- var req = this.req;
- var socket = req.socket;
+ if (!req.res) {
+ // If we don't have a response then we know that the socket
+ // ended prematurely and we need to emit an error on the request.
+ req.emit('error', createHangUpError());
+ req._hadError = true;
+ }
+ if (parser) {
+ parser.finish();
+ freeParser(parser, req);
+ }
+ socket.destroy();
+}
- if (req.shouldKeepAlive) {
- debug('AGENT socket keep-alive');
- socket.removeListener('close', socketCloseListener);
- socket.removeListener('error', socketErrorListener);
- socket.emit('free');
- } else {
- if (socket.writable) {
- debug('AGENT socket.destroySoon()');
- socket.destroySoon();
+function socketOnData(d, start, end) {
+ var socket = this;
+ var req = this._httpMessage;
+ var parser = this.parser;
+
+ var ret = parser.execute(d, start, end - start);
+ if (ret instanceof Error) {
+ debug('parse error');
+ freeParser(parser, req);
+ socket.destroy(ret);
+ } else if (parser.incoming && parser.incoming.upgrade) {
+ // Upgrade or CONNECT
+ var bytesParsed = ret;
+ var res = parser.incoming;
+ req.res = res;
+
+ socket.ondata = null;
+ socket.onend = null;
+ parser.finish();
+
+ // This is start + byteParsed
+ var bodyHead = d.slice(start + bytesParsed, end);
+
+ var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
+ if (req.listeners(eventName).length) {
+ req.upgradeOrConnect = true;
+
+ // detach the socket
+ socket.emit('agentRemove');
+ socket.removeListener('close', socketCloseListener);
+ socket.removeListener('error', socketErrorListener);
+
+ req.emit(eventName, res, socket, bodyHead);
+ req.emit('close');
+ } else {
+ // Got Upgrade header or CONNECT method, but have no handler.
+ socket.destroy();
}
- assert(!socket.writable);
+ freeParser(parser, req);
+ } else if (parser.incoming && parser.incoming.complete &&
+ // When the status code is 100 (Continue), the server will
+ // send a final response after this client sends a request
+ // body. So, we must not free the parser.
+ parser.incoming.statusCode !== 100) {
+ freeParser(parser, req);
}
}
+
function parserOnIncomingClient(res, shouldKeepAlive) {
var parser = this;
var socket = this.socket;
}
req.res = res;
+ // Responses to CONNECT request is handled as Upgrade.
+ if (req.method === 'CONNECT') {
+ res.upgrade = true;
+ return true; // skip body
+ }
+
// Responses to HEAD requests are crazy.
// HEAD responses aren't allowed to have an entity-body
// but *can* have a content-length which actually corresponds
return true;
}
- if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgraded) {
+ if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
// Server MUST respond with Connection:keep-alive for us to enable it.
// If we've been upgraded (via WebSockets) we also shouldn't try to
// keep the connection open.
req.shouldKeepAlive = false;
}
+
DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
req.emit('response', res);
req.res = res;
return isHeadResponse;
}
-function socketOnEnd() {
- var socket = this;
- var req = this._httpMessage;
- var parser = this.parser;
- if (!req.res) {
- // If we don't have a response then we know that the socket
- // ended prematurely and we need to emit an error on the request.
- req.emit('error', createHangUpError());
- req._hadError = true;
- }
- if (parser) {
- parser.finish();
- freeParser(parser, req);
- }
- socket.destroy();
-}
-
-function socketOnData(d, start, end) {
- var socket = this;
- var req = this._httpMessage;
- var parser = this.parser;
-
- var ret = parser.execute(d, start, end - start);
- if (ret instanceof Error) {
- debug('parse error');
- freeParser(parser, req);
- socket.destroy(ret);
- } else if (parser.incoming && parser.incoming.upgrade) {
- var bytesParsed = ret;
- socket.ondata = null;
- socket.onend = null;
-
- var res = parser.incoming;
- req.res = res;
+function responseOnEnd() {
+ var res = this;
+ var req = res.req;
+ var socket = req.socket;
- // This is start + byteParsed
- var upgradeHead = d.slice(start + bytesParsed, end);
- if (req.listeners('upgrade').length) {
- // Emit 'upgrade' on the Agent.
- req.upgraded = true;
- req.emit('upgrade', res, socket, upgradeHead);
- socket.emit('agentRemove');
- } else {
- // Got upgrade header, but have no handler.
- socket.destroy();
+ if (!req.shouldKeepAlive) {
+ if (socket.writable) {
+ debug('AGENT socket.destroySoon()');
+ socket.destroySoon();
}
- freeParser(parser, req);
- } else if (parser.incoming && parser.incoming.complete &&
- // When the status code is 100 (Continue), the server will
- // send a final response after this client sends a request
- // body. So, we must not free the parser.
- parser.incoming.statusCode !== 100) {
- freeParser(parser, req);
+ assert(!socket.writable);
+ } else {
+ debug('AGENT socket keep-alive');
+ socket.removeListener('close', socketCloseListener);
+ socket.removeListener('error', socketErrorListener);
+ socket.emit('free');
}
}
process.nextTick(function() {
var parser = parsers.alloc();
-
req.socket = socket;
req.connection = socket;
- parser.socket = socket;
- socket.parser = parser;
parser.reinitialize(HTTPParser.RESPONSE);
+ parser.socket = socket;
parser.incoming = null;
req.parser = parser;
+ parser.socket = socket;
+ socket.parser = parser;
+ parser.incoming = null;
+ socket._httpMessage = req;
+
+ // Setup "drain" propogation.
+ httpSocketSetup(socket);
+
// Propagate headers limit from request object to parser
if (typeof req.maxHeadersCount === 'number') {
parser.maxHeaderPairs = req.maxHeadersCount << 1;
parser.maxHeaderPairs = 2000;
}
- socket._httpMessage = req;
-
- // Setup "drain" propogation.
- httpSocketSetup(socket);
+ socket.on('error', socketErrorListener);
socket.ondata = socketOnData;
socket.onend = socketOnEnd;
- socket.on('error', socketErrorListener);
socket.on('close', socketCloseListener);
parser.onIncoming = parserOnIncomingClient;
-
req.emit('socket', socket);
});
+
};
ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
var parser = parsers.alloc();
parser.reinitialize(HTTPParser.REQUEST);
parser.socket = socket;
+ socket.parser = parser;
parser.incoming = null;
// Propagate headers limit from server instance to parser