3 const FreeList = require('internal/freelist').FreeList;
4 const HTTPParser = process.binding('http_parser').HTTPParser;
6 const incoming = require('_http_incoming');
7 const IncomingMessage = incoming.IncomingMessage;
8 const readStart = incoming.readStart;
9 const readStop = incoming.readStop;
11 const debug = require('util').debuglog('http');
12 exports.debug = debug;
14 exports.CRLF = '\r\n';
15 exports.chunkExpression = /chunk/i;
16 exports.continueExpression = /100-continue/i;
17 exports.methods = HTTPParser.methods;
19 const kOnHeaders = HTTPParser.kOnHeaders | 0;
20 const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
21 const kOnBody = HTTPParser.kOnBody | 0;
22 const kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
23 const kOnExecute = HTTPParser.kOnExecute | 0;
25 // Only called in the slow case where slow means
26 // that the request headers were either fragmented
27 // across multiple TCP packets or too large to be
28 // processed in a single run. This method is also
29 // called to process trailing HTTP headers.
30 function parserOnHeaders(headers, url) {
31 // Once we exceeded headers limit - stop collecting them
32 if (this.maxHeaderPairs <= 0 ||
33 this._headers.length < this.maxHeaderPairs) {
34 this._headers = this._headers.concat(headers);
39 // `headers` and `url` are set only if .onHeaders() has not been called for
41 // `url` is not set for response parsers but that's not applicable here since
42 // all our parsers are request parsers.
43 function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
44 url, statusCode, statusMessage, upgrade,
49 headers = parser._headers;
58 parser.incoming = new IncomingMessage(parser.socket);
59 parser.incoming.httpVersionMajor = versionMajor;
60 parser.incoming.httpVersionMinor = versionMinor;
61 parser.incoming.httpVersion = versionMajor + '.' + versionMinor;
62 parser.incoming.url = url;
64 var n = headers.length;
66 // If parser.maxHeaderPairs <= 0 assume that there's no limit.
67 if (parser.maxHeaderPairs > 0)
68 n = Math.min(n, parser.maxHeaderPairs);
70 parser.incoming._addHeaderLines(headers, n);
72 if (typeof method === 'number') {
74 parser.incoming.method = HTTPParser.methods[method];
77 parser.incoming.statusCode = statusCode;
78 parser.incoming.statusMessage = statusMessage;
81 // The client made non-upgrade request, and server is just advertising
82 // supported protocols.
84 // See RFC7230 Section 6.7
86 // NOTE: RegExp below matches `upgrade` in `Connection: abc, upgrade, def`
89 parser.outgoing !== null &&
90 (parser.outgoing._headers.upgrade === undefined ||
91 !/(^|\W)upgrade(\W|$)/i.test(parser.outgoing._headers.connection))) {
95 parser.incoming.upgrade = upgrade;
97 var skipBody = false; // response to HEAD or CONNECT
100 // For upgraded connections and CONNECT method request, we'll emit this
101 // after parser.execute so that we can capture the first part of the new
103 skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
109 // XXX This is a mess.
110 // TODO: http.Parser should be a Writable emits request/response events.
111 function parserOnBody(b, start, len) {
113 var stream = parser.incoming;
115 // if the stream has already been removed, then drop it.
119 var socket = stream.socket;
121 // pretend this was the result of a stream._read call.
122 if (len > 0 && !stream._dumped) {
123 var slice = b.slice(start, start + len);
124 var ret = stream.push(slice);
130 function parserOnMessageComplete() {
132 var stream = parser.incoming;
135 stream.complete = true;
136 // Emit any trailing headers.
137 var headers = parser._headers;
139 parser.incoming._addHeaderLines(headers, headers.length);
140 parser._headers = [];
144 // For emit end event
148 // force to read the next incoming message
149 readStart(parser.socket);
153 var parsers = new FreeList('parsers', 1000, function() {
154 var parser = new HTTPParser(HTTPParser.REQUEST);
156 parser._headers = [];
158 parser._consumed = false;
160 parser.socket = null;
161 parser.incoming = null;
162 parser.outgoing = null;
164 // Only called in the slow case where slow means
165 // that the request headers were either fragmented
166 // across multiple TCP packets or too large to be
167 // processed in a single run. This method is also
168 // called to process trailing HTTP headers.
169 parser[kOnHeaders] = parserOnHeaders;
170 parser[kOnHeadersComplete] = parserOnHeadersComplete;
171 parser[kOnBody] = parserOnBody;
172 parser[kOnMessageComplete] = parserOnMessageComplete;
173 parser[kOnExecute] = null;
177 exports.parsers = parsers;
180 // Free the parser and also break any links that it
181 // might have to any other things.
182 // TODO: All parser data should be attached to a
183 // single object, so that it can be easily cleaned
184 // up by doing `parser.data = {}`, which should
185 // be done in FreeList.free. `parsers.free(parser)`
186 // should be all that is needed.
187 function freeParser(parser, req, socket) {
189 parser._headers = [];
190 parser.onIncoming = null;
191 if (parser._consumed)
193 parser._consumed = false;
195 parser.socket.parser = null;
196 parser.socket = null;
197 parser.incoming = null;
198 parser.outgoing = null;
199 parser[kOnExecute] = null;
200 if (parsers.free(parser) === false)
208 socket.parser = null;
211 exports.freeParser = freeParser;
215 if (this._httpMessage) this._httpMessage.emit('drain');
219 function httpSocketSetup(socket) {
220 socket.removeListener('drain', ondrain);
221 socket.on('drain', ondrain);
223 exports.httpSocketSetup = httpSocketSetup;
226 * Verifies that the given val is a valid HTTP token
227 * per the rules defined in RFC 7230
229 const token = /^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$/;
230 function checkIsHttpToken(val) {
231 return typeof val === 'string' && token.test(val);
233 exports._checkIsHttpToken = checkIsHttpToken;
236 * True if val contains an invalid field-vchar
237 * field-value = *( field-content / obs-fold )
238 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
239 * field-vchar = VCHAR / obs-text
241 function checkInvalidHeaderChar(val) {
243 for (var i = 0; i < val.length; i++) {
244 const ch = val.charCodeAt(i);
245 if (ch === 9) continue;
246 if (ch <= 31 || ch > 255 || ch === 127) return true;
250 exports._checkInvalidHeaderChar = checkInvalidHeaderChar;