'use strict'; const FreeList = require('internal/freelist').FreeList; const HTTPParser = process.binding('http_parser').HTTPParser; const incoming = require('_http_incoming'); const IncomingMessage = incoming.IncomingMessage; const readStart = incoming.readStart; const readStop = incoming.readStop; const debug = require('util').debuglog('http'); exports.debug = debug; exports.CRLF = '\r\n'; exports.chunkExpression = /chunk/i; exports.continueExpression = /100-continue/i; exports.methods = HTTPParser.methods; const kOnHeaders = HTTPParser.kOnHeaders | 0; const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0; const kOnBody = HTTPParser.kOnBody | 0; const kOnMessageComplete = HTTPParser.kOnMessageComplete | 0; // 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; } // `headers` and `url` are set only if .onHeaders() has not been called for // this request. // `url` is not set for response parsers but that's not applicable here since // all our parsers are request parsers. function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, url, statusCode, statusMessage, upgrade, shouldKeepAlive) { var parser = this; if (!headers) { headers = parser._headers; parser._headers = []; } if (!url) { url = parser._url; parser._url = ''; } parser.incoming = new IncomingMessage(parser.socket); parser.incoming.httpVersionMajor = versionMajor; parser.incoming.httpVersionMinor = versionMinor; parser.incoming.httpVersion = versionMajor + '.' + versionMinor; parser.incoming.url = url; var n = headers.length; // If parser.maxHeaderPairs <= 0 assume that there's no limit. if (parser.maxHeaderPairs > 0) n = Math.min(n, parser.maxHeaderPairs); parser.incoming._addHeaderLines(headers, n); if (typeof method === 'number') { // server only parser.incoming.method = HTTPParser.methods[method]; } else { // client only parser.incoming.statusCode = statusCode; parser.incoming.statusMessage = statusMessage; } parser.incoming.upgrade = upgrade; var skipBody = false; // response to HEAD or CONNECT if (!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, 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) { parser.incoming._addHeaderLines(headers, headers.length); parser._headers = []; parser._url = ''; } // For emit end event stream.push(null); } // force to read the next incoming message readStart(parser.socket); } var parsers = new FreeList('parsers', 1000, function() { var parser = new HTTPParser(HTTPParser.REQUEST); parser._headers = []; parser._url = ''; parser._consumed = false; // 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[kOnHeaders] = parserOnHeaders; parser[kOnHeadersComplete] = parserOnHeadersComplete; parser[kOnBody] = parserOnBody; parser[kOnMessageComplete] = parserOnMessageComplete; 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. function freeParser(parser, req, socket) { if (parser) { parser._headers = []; parser.onIncoming = null; if (parser._consumed) parser.unconsume(); parser._consumed = false; if (parser.socket) parser.socket.parser = null; parser.socket = null; parser.incoming = null; if (parsers.free(parser) === false) parser.close(); parser = null; } if (req) { req.parser = null; } if (socket) { socket.parser = null; } } exports.freeParser = freeParser; function ondrain() { if (this._httpMessage) this._httpMessage.emit('drain'); } function httpSocketSetup(socket) { socket.removeListener('drain', ondrain); socket.on('drain', ondrain); } exports.httpSocketSetup = httpSocketSetup; /** * Verifies that the given val is a valid HTTP token * per the rules defined in RFC 7230 **/ const token = /^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$/; function checkIsHttpToken(val) { return typeof val === 'string' && token.test(val); } exports._checkIsHttpToken = checkIsHttpToken;