@ -36,128 +36,138 @@ if (process.env.NODE_DEBUG && /http/.test(process.env.NODE_DEBUG)) {
debug = function ( ) { } ;
}
// Only called in the slow case where slow means
// that the request headers were either fragmented
// across multiple TCP packets or too large to be
// processed in a single run. This method is also
// called to process trailing HTTP headers.
function parserOnHeaders ( headers , url ) {
// Once we exceeded headers limit - stop collecting them
if ( this . maxHeaderPairs <= 0 ||
this . _ headers . length < this . maxHeaderPairs ) {
this . _ headers = this . _ headers . concat ( headers ) ;
}
this . _ url += url ;
}
var parsers = new FreeList ( 'parsers' , 1000 , function ( ) {
var parser = new HTTPParser ( HTTPParser . REQUEST ) ;
// info.headers and info.url are set only if .onHeaders()
// has not been called for this request.
//
// info.url is not set for response parsers but that's not
// applicable here since all our parsers are request parsers.
function parserOnHeadersComplete ( info ) {
var parser = this ;
var headers = info . headers ;
var url = info . url ;
parser . _ headers = [ ] ;
parser . _ url = '' ;
if ( ! headers ) {
headers = parser . _ headers ;
parser . _ headers = [ ] ;
}
// Only called in the slow case where slow means
// that the request headers were either fragmented
// across multiple TCP packets or too large to be
// processed in a single run. This method is also
// called to process trailing HTTP headers.
parser . onHeaders = function ( headers , url ) {
// Once we exceeded headers limit - stop collecting them
if ( parser . maxHeaderPairs <= 0 ||
parser . _ headers . length < parser . maxHeaderPairs ) {
parser . _ headers = parser . _ headers . concat ( headers ) ;
}
parser . _ url += url ;
} ;
if ( ! url ) {
url = parser . _ url ;
parser . _ url = '' ;
}
// info.headers and info.url are set only if .onHeaders()
// has not been called for this request.
//
// info.url is not set for response parsers but that's not
// applicable here since all our parsers are request parsers.
parser . onHeadersComplete = function ( info ) {
var headers = info . headers ;
var url = info . url ;
if ( ! headers ) {
headers = parser . _ headers ;
parser . _ headers = [ ] ;
}
parser . incoming = new IncomingMessage ( parser . socket ) ;
parser . incoming . httpVersionMajor = info . versionMajor ;
parser . incoming . httpVersionMinor = info . versionMinor ;
parser . incoming . httpVersion = info . versionMajor + '.' + info . versionMinor ;
parser . incoming . url = url ;
if ( ! url ) {
url = parser . _ url ;
parser . _ url = '' ;
}
var n = headers . length ;
parser . incoming = new IncomingMessage ( parser . socket ) ;
parser . incoming . httpVersionMajor = info . versionMajor ;
parser . incoming . httpVersionMinor = info . versionMinor ;
parser . incoming . httpVersion = info . versionMajor + '.' + info . versionMinor ;
parser . incoming . url = url ;
// If parser.maxHeaderPairs <= 0 - assume that there're no limit
if ( parser . maxHeaderPairs > 0 ) {
n = Math . min ( n , parser . maxHeaderPairs ) ;
}
var n = headers . length ;
for ( var i = 0 ; i < n ; i += 2 ) {
var k = headers [ i ] ;
var v = headers [ i + 1 ] ;
parser . incoming . _ addHeaderLine ( k . toLowerCase ( ) , v ) ;
}
// If parser.maxHeaderPairs <= 0 - assume that there're no limit
if ( parser . maxHeaderPairs > 0 ) {
n = Math . min ( n , parser . maxHeaderPairs ) ;
}
for ( var i = 0 ; i < n ; i += 2 ) {
var k = headers [ i ] ;
var v = headers [ i + 1 ] ;
parser . incoming . _ addHeaderLine ( k . toLowerCase ( ) , v ) ;
}
if ( info . method ) {
// server only
parser . incoming . method = info . method ;
} else {
// client only
parser . incoming . statusCode = info . statusCode ;
// CHECKME dead code? we're always a request parser
}
if ( info . method ) {
// server only
parser . incoming . method = info . method ;
} else {
// client only
parser . incoming . statusCode = info . statusCode ;
// CHECKME dead code? we're always a request parser
}
parser . incoming . upgrade = info . upgrade ;
parser . incoming . upgrade = info . upgrade ;
var skipBody = false ; // response to HEAD or CONNECT
var skipBody = false ; // response to HEAD or CONNECT
if ( ! info . upgrade ) {
// For upgraded connections and CONNECT method request,
// we'll emit this after parser.execute
// so that we can capture the first part of the new protocol
skipBody = parser . onIncoming ( parser . incoming , info . shouldKeepAlive ) ;
}
if ( ! info . upgrade ) {
// For upgraded connections and CONNECT method request,
// we'll emit this after parser.execute
// so that we can capture the first part of the new protocol
skipBody = parser . onIncoming ( parser . incoming , info . shouldKeepAlive ) ;
}
return skipBody ;
}
return skipBody ;
} ;
function parserOnBody ( b , start , len ) {
var parser = this ;
var slice = b . slice ( start , start + len ) ;
// body encoding is done in _emitData
parser . incoming . _ emitData ( slice ) ;
}
parser . onBody = function ( b , start , len ) {
// TODO body encoding?
var slice = b . slice ( start , start + len ) ;
function parserOnMessageComplete ( ) {
var parser = this ;
parser . incoming . complete = true ;
// Emit any trailing headers.
var headers = parser . _ headers ;
if ( headers ) {
for ( var i = 0 , n = headers . length ; i < n ; i += 2 ) {
var k = headers [ i ] ;
var v = headers [ i + 1 ] ;
parser . incoming . _ addHeaderLine ( k . toLowerCase ( ) , v ) ;
}
parser . _ headers = [ ] ;
parser . _ url = '' ;
}
if ( ! parser . incoming . upgrade ) {
// For upgraded connections, also emit this after parser.execute
if ( parser . incoming . _ paused || parser . incoming . _ pendings . length ) {
parser . incoming . _ pendings . push ( slice ) ;
parser . incoming . _ pendings . push ( END_OF_FILE ) ;
} else {
parser . incoming . _ emitData ( slice ) ;
parser . incoming . readable = false ;
parser . incoming . _ emitEnd ( ) ;
}
} ;
}
parser . onMessageComplete = function ( ) {
parser . incoming . complete = true ;
if ( parser . socket . readable ) {
// force to read the next incoming message
parser . socket . resume ( ) ;
}
}
// Emit any trailing headers.
var headers = parser . _ headers ;
if ( headers ) {
for ( var i = 0 , n = headers . length ; i < n ; i += 2 ) {
var k = headers [ i ] ;
var v = headers [ i + 1 ] ;
parser . incoming . _ addHeaderLine ( k . toLowerCase ( ) , v ) ;
}
parser . _ headers = [ ] ;
parser . _ url = '' ;
}
if ( ! parser . incoming . upgrade ) {
// For upgraded connections, also emit this after parser.execute
if ( parser . incoming . _ paused || parser . incoming . _ pendings . length ) {
parser . incoming . _ pendings . push ( END_OF_FILE ) ;
} else {
parser . incoming . readable = false ;
parser . incoming . _ emitEnd ( ) ;
}
}
var parsers = new FreeList ( 'parsers' , 1000 , function ( ) {
var parser = new HTTPParser ( HTTPParser . REQUEST ) ;
if ( parser . socket . readable ) {
// force to read the next incoming message
parser . socket . resume ( ) ;
}
} ;
parser . _ headers = [ ] ;
parser . _ url = '' ;
// Only called in the slow case where slow means
// that the request headers were either fragmented
// across multiple TCP packets or too large to be
// processed in a single run. This method is also
// called to process trailing HTTP headers.
parser . onHeaders = parserOnHeaders ;
parser . onHeadersComplete = parserOnHeadersComplete ;
parser . onBody = parserOnBody ;
parser . onMessageComplete = parserOnMessageComplete ;
return parser ;
} ) ;
@ -1230,6 +1240,31 @@ function createHangUpError() {
return error ;
}
// Free the parser and also break any links that it
// might have to any other things.
// TODO: All parser data should be attached to a
// single object, so that it can be easily cleaned
// up by doing `parser.data = {}`, which should
// be done in FreeList.free. `parsers.free(parser)`
// should be all that is needed.
function freeParser ( parser , req ) {
if ( parser ) {
parser . _ headers = [ ] ;
parser . onIncoming = null ;
if ( parser . socket ) {
parser . socket . onend = null ;
parser . socket . ondata = null ;
}
parser . socket = null ;
parser . incoming = null ;
parsers . free ( parser ) ;
parser = null ;
}
if ( req ) {
req . parser = null ;
}
} ;
ClientRequest . prototype . onSocket = function ( socket ) {
var req = this ;
@ -1254,21 +1289,6 @@ ClientRequest.prototype.onSocket = function(socket) {
// Setup 'drain' propogation.
httpSocketSetup ( socket ) ;
var freeParser = function ( ) {
if ( parser ) {
parser . onIncoming = null ;
parser . socket . onend = null ;
parser . socket . ondata = null ;
parser . socket = null ;
parser . incoming = null ;
parsers . free ( parser ) ;
parser = null ;
}
if ( req ) {
req . parser = null ;
}
} ;
var errorListener = function ( err ) {
debug ( 'HTTP SOCKET ERROR: ' + err . message + '\n' + err . stack ) ;
req . emit ( 'error' , err ) ;
@ -1277,7 +1297,7 @@ ClientRequest.prototype.onSocket = function(socket) {
req . _ hadError = true ;
if ( parser ) {
parser . finish ( ) ;
freeParser ( ) ;
freeParser ( parser , req ) ;
}
socket . destroy ( ) ;
}
@ -1299,7 +1319,7 @@ ClientRequest.prototype.onSocket = function(socket) {
var ret = parser . execute ( d , start , end - start ) ;
if ( ret instanceof Error ) {
debug ( 'parse error' ) ;
freeParser ( ) ;
freeParser ( parser , req ) ;
socket . destroy ( ret ) ;
} else if ( parser . incoming && parser . incoming . upgrade ) {
// Upgrade or CONNECT
@ -1310,7 +1330,6 @@ ClientRequest.prototype.onSocket = function(socket) {
socket . ondata = null ;
socket . onend = null ;
parser . finish ( ) ;
freeParser ( ) ;
// This is start + byteParsed
var bodyHead = d . slice ( start + bytesParsed , end ) ;
@ -1331,12 +1350,13 @@ ClientRequest.prototype.onSocket = function(socket) {
// Got Upgrade header or CONNECT method, but have no handler.
socket . destroy ( ) ;
}
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 ( ) ;
freeParser ( parser , req ) ;
}
} ;
@ -1349,7 +1369,7 @@ ClientRequest.prototype.onSocket = function(socket) {
}
if ( parser ) {
parser . finish ( ) ;
freeParser ( ) ;
freeParser ( parser , req ) ;
}
socket . destroy ( ) ;
} ;
@ -1586,8 +1606,8 @@ function connectionListener(socket) {
function serverSocketCloseListener ( ) {
debug ( 'server socket close' ) ;
// unref the parser for easy gc
parsers . free ( parser ) ;
// mark this parser as reusable
freeParser ( parser ) ;
abortIncoming ( ) ;
}
@ -1632,7 +1652,7 @@ function connectionListener(socket) {
socket . onend = null ;
socket . removeListener ( 'close' , serverSocketCloseListener ) ;
parser . finish ( ) ;
parsers . free ( parser ) ;
freeParser ( parser , req ) ;
// This is start + byteParsed
var bodyHead = d . slice ( start + bytesParsed , end ) ;