2014-11-22 15:59:48 +00:00
|
|
|
'use strict';
|
|
|
|
|
2013-04-11 22:15:41 +00:00
|
|
|
var FreeList = require('freelist').FreeList;
|
|
|
|
var HTTPParser = process.binding('http_parser').HTTPParser;
|
|
|
|
|
|
|
|
var incoming = require('_http_incoming');
|
|
|
|
var IncomingMessage = incoming.IncomingMessage;
|
|
|
|
var readStart = incoming.readStart;
|
|
|
|
var readStop = incoming.readStop;
|
|
|
|
|
2013-11-04 17:39:29 +00:00
|
|
|
var isNumber = require('util').isNumber;
|
2013-05-21 22:22:05 +00:00
|
|
|
var debug = require('util').debuglog('http');
|
2013-04-11 22:37:14 +00:00
|
|
|
exports.debug = debug;
|
|
|
|
|
|
|
|
exports.CRLF = '\r\n';
|
|
|
|
exports.chunkExpression = /chunk/i;
|
|
|
|
exports.continueExpression = /100-continue/i;
|
2013-10-28 12:35:33 +00:00
|
|
|
exports.methods = HTTPParser.methods;
|
2013-04-11 22:37:14 +00:00
|
|
|
|
2013-08-13 21:47:17 +00:00
|
|
|
var kOnHeaders = HTTPParser.kOnHeaders | 0;
|
|
|
|
var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
|
|
|
|
var kOnBody = HTTPParser.kOnBody | 0;
|
|
|
|
var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
|
|
|
|
|
2013-04-11 22:15:41 +00:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2013-07-26 02:33:15 +00:00
|
|
|
debug('parserOnHeadersComplete', info);
|
2013-04-11 22:15:41 +00:00
|
|
|
var parser = this;
|
|
|
|
var headers = info.headers;
|
|
|
|
var url = info.url;
|
|
|
|
|
|
|
|
if (!headers) {
|
|
|
|
headers = parser._headers;
|
|
|
|
parser._headers = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!url) {
|
|
|
|
url = parser._url;
|
|
|
|
parser._url = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
var n = headers.length;
|
|
|
|
|
|
|
|
// If parser.maxHeaderPairs <= 0 - assume that there're no limit
|
|
|
|
if (parser.maxHeaderPairs > 0) {
|
|
|
|
n = Math.min(n, parser.maxHeaderPairs);
|
|
|
|
}
|
|
|
|
|
2013-08-06 01:41:17 +00:00
|
|
|
parser.incoming._addHeaderLines(headers, n);
|
2013-04-11 22:15:41 +00:00
|
|
|
|
2013-11-04 17:39:29 +00:00
|
|
|
if (isNumber(info.method)) {
|
2013-04-11 22:15:41 +00:00
|
|
|
// server only
|
2013-10-28 12:44:41 +00:00
|
|
|
parser.incoming.method = HTTPParser.methods[info.method];
|
2013-04-11 22:15:41 +00:00
|
|
|
} else {
|
|
|
|
// client only
|
|
|
|
parser.incoming.statusCode = info.statusCode;
|
2013-09-22 14:06:58 +00:00
|
|
|
parser.incoming.statusMessage = info.statusMessage;
|
2013-04-11 22:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
parser.incoming.upgrade = info.upgrade;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
return skipBody;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX This is a mess.
|
|
|
|
// TODO: http.Parser should be a Writable emits request/response events.
|
|
|
|
function parserOnBody(b, start, len) {
|
|
|
|
var parser = this;
|
|
|
|
var stream = parser.incoming;
|
|
|
|
|
|
|
|
// if the stream has already been removed, then drop it.
|
|
|
|
if (!stream)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var socket = stream.socket;
|
|
|
|
|
|
|
|
// pretend this was the result of a stream._read call.
|
|
|
|
if (len > 0 && !stream._dumped) {
|
|
|
|
var slice = b.slice(start, start + len);
|
|
|
|
var ret = stream.push(slice);
|
|
|
|
if (!ret)
|
|
|
|
readStop(socket);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function parserOnMessageComplete() {
|
|
|
|
var parser = this;
|
|
|
|
var stream = parser.incoming;
|
|
|
|
|
|
|
|
if (stream) {
|
|
|
|
stream.complete = true;
|
|
|
|
// Emit any trailing headers.
|
|
|
|
var headers = parser._headers;
|
|
|
|
if (headers) {
|
2013-08-06 01:41:17 +00:00
|
|
|
parser.incoming._addHeaderLines(headers, headers.length);
|
2013-04-11 22:15:41 +00:00
|
|
|
parser._headers = [];
|
|
|
|
parser._url = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stream.upgrade)
|
|
|
|
// For upgraded connections, also emit this after parser.execute
|
|
|
|
stream.push(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stream && !parser.incoming._pendings.length) {
|
|
|
|
// For emit end event
|
|
|
|
stream.push(null);
|
|
|
|
}
|
|
|
|
|
2013-10-10 22:00:05 +00:00
|
|
|
// force to read the next incoming message
|
|
|
|
readStart(parser.socket);
|
2013-04-11 22:15:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var parsers = new FreeList('parsers', 1000, function() {
|
|
|
|
var parser = new HTTPParser(HTTPParser.REQUEST);
|
|
|
|
|
|
|
|
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.
|
2013-08-13 21:47:17 +00:00
|
|
|
parser[kOnHeaders] = parserOnHeaders;
|
|
|
|
parser[kOnHeadersComplete] = parserOnHeadersComplete;
|
|
|
|
parser[kOnBody] = parserOnBody;
|
|
|
|
parser[kOnMessageComplete] = parserOnMessageComplete;
|
2013-04-11 22:15:41 +00:00
|
|
|
|
|
|
|
return parser;
|
|
|
|
});
|
|
|
|
exports.parsers = parsers;
|
|
|
|
|
|
|
|
|
|
|
|
// 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.
|
2014-08-22 22:57:45 +00:00
|
|
|
function freeParser(parser, req, socket) {
|
2013-04-11 22:15:41 +00:00
|
|
|
if (parser) {
|
|
|
|
parser._headers = [];
|
|
|
|
parser.onIncoming = null;
|
2013-07-26 02:33:15 +00:00
|
|
|
if (parser.socket)
|
2013-04-11 22:15:41 +00:00
|
|
|
parser.socket.parser = null;
|
|
|
|
parser.socket = null;
|
|
|
|
parser.incoming = null;
|
2014-08-22 22:31:44 +00:00
|
|
|
if (parsers.free(parser) === false)
|
|
|
|
parser.close();
|
2013-04-11 22:15:41 +00:00
|
|
|
parser = null;
|
|
|
|
}
|
|
|
|
if (req) {
|
|
|
|
req.parser = null;
|
|
|
|
}
|
2014-08-22 22:57:45 +00:00
|
|
|
if (socket) {
|
|
|
|
socket.parser = null;
|
|
|
|
}
|
2013-04-11 22:15:41 +00:00
|
|
|
}
|
|
|
|
exports.freeParser = freeParser;
|
2013-04-11 23:00:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
function ondrain() {
|
|
|
|
if (this._httpMessage) this._httpMessage.emit('drain');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function httpSocketSetup(socket) {
|
|
|
|
socket.removeListener('drain', ondrain);
|
|
|
|
socket.on('drain', ondrain);
|
|
|
|
}
|
|
|
|
exports.httpSocketSetup = httpSocketSetup;
|