revise installing a license file
[platform/upstream/nodejs.git] / lib / _http_common.js
1 'use strict';
2
3 const FreeList = require('internal/freelist').FreeList;
4 const HTTPParser = process.binding('http_parser').HTTPParser;
5
6 const incoming = require('_http_incoming');
7 const IncomingMessage = incoming.IncomingMessage;
8 const readStart = incoming.readStart;
9 const readStop = incoming.readStop;
10
11 const debug = require('util').debuglog('http');
12 exports.debug = debug;
13
14 exports.CRLF = '\r\n';
15 exports.chunkExpression = /chunk/i;
16 exports.continueExpression = /100-continue/i;
17 exports.methods = HTTPParser.methods;
18
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;
24
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);
35   }
36   this._url += url;
37 }
38
39 // `headers` and `url` are set only if .onHeaders() has not been called for
40 // this request.
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,
45                                  shouldKeepAlive) {
46   var parser = this;
47
48   if (!headers) {
49     headers = parser._headers;
50     parser._headers = [];
51   }
52
53   if (!url) {
54     url = parser._url;
55     parser._url = '';
56   }
57
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;
63
64   var n = headers.length;
65
66   // If parser.maxHeaderPairs <= 0 assume that there's no limit.
67   if (parser.maxHeaderPairs > 0)
68     n = Math.min(n, parser.maxHeaderPairs);
69
70   parser.incoming._addHeaderLines(headers, n);
71
72   if (typeof method === 'number') {
73     // server only
74     parser.incoming.method = HTTPParser.methods[method];
75   } else {
76     // client only
77     parser.incoming.statusCode = statusCode;
78     parser.incoming.statusMessage = statusMessage;
79   }
80
81   // The client made non-upgrade request, and server is just advertising
82   // supported protocols.
83   //
84   // See RFC7230 Section 6.7
85   //
86   // NOTE: RegExp below matches `upgrade` in `Connection: abc, upgrade, def`
87   // header.
88   if (upgrade &&
89       parser.outgoing !== null &&
90       (parser.outgoing._headers.upgrade === undefined ||
91        !/(^|\W)upgrade(\W|$)/i.test(parser.outgoing._headers.connection))) {
92     upgrade = false;
93   }
94
95   parser.incoming.upgrade = upgrade;
96
97   var skipBody = false; // response to HEAD or CONNECT
98
99   if (!upgrade) {
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
102     // protocol.
103     skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
104   }
105
106   return skipBody;
107 }
108
109 // XXX This is a mess.
110 // TODO: http.Parser should be a Writable emits request/response events.
111 function parserOnBody(b, start, len) {
112   var parser = this;
113   var stream = parser.incoming;
114
115   // if the stream has already been removed, then drop it.
116   if (!stream)
117     return;
118
119   var socket = stream.socket;
120
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);
125     if (!ret)
126       readStop(socket);
127   }
128 }
129
130 function parserOnMessageComplete() {
131   var parser = this;
132   var stream = parser.incoming;
133
134   if (stream) {
135     stream.complete = true;
136     // Emit any trailing headers.
137     var headers = parser._headers;
138     if (headers) {
139       parser.incoming._addHeaderLines(headers, headers.length);
140       parser._headers = [];
141       parser._url = '';
142     }
143
144     // For emit end event
145     stream.push(null);
146   }
147
148   // force to read the next incoming message
149   readStart(parser.socket);
150 }
151
152
153 var parsers = new FreeList('parsers', 1000, function() {
154   var parser = new HTTPParser(HTTPParser.REQUEST);
155
156   parser._headers = [];
157   parser._url = '';
158   parser._consumed = false;
159
160   parser.socket = null;
161   parser.incoming = null;
162   parser.outgoing = null;
163
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;
174
175   return parser;
176 });
177 exports.parsers = parsers;
178
179
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) {
188   if (parser) {
189     parser._headers = [];
190     parser.onIncoming = null;
191     if (parser._consumed)
192       parser.unconsume();
193     parser._consumed = false;
194     if (parser.socket)
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)
201       parser.close();
202     parser = null;
203   }
204   if (req) {
205     req.parser = null;
206   }
207   if (socket) {
208     socket.parser = null;
209   }
210 }
211 exports.freeParser = freeParser;
212
213
214 function ondrain() {
215   if (this._httpMessage) this._httpMessage.emit('drain');
216 }
217
218
219 function httpSocketSetup(socket) {
220   socket.removeListener('drain', ondrain);
221   socket.on('drain', ondrain);
222 }
223 exports.httpSocketSetup = httpSocketSetup;
224
225 /**
226  * Verifies that the given val is a valid HTTP token
227  * per the rules defined in RFC 7230
228  **/
229 const token = /^[a-zA-Z0-9_!#$%&'*+.^`|~-]+$/;
230 function checkIsHttpToken(val) {
231   return typeof val === 'string' && token.test(val);
232 }
233 exports._checkIsHttpToken = checkIsHttpToken;
234
235 /**
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
240  **/
241 function checkInvalidHeaderChar(val) {
242   val = '' + 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;
247   }
248   return false;
249 }
250 exports._checkInvalidHeaderChar = checkInvalidHeaderChar;