lib: turn on strict mode
[platform/upstream/nodejs.git] / lib / _http_common.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22 'use strict';
23
24 var FreeList = require('freelist').FreeList;
25 var HTTPParser = process.binding('http_parser').HTTPParser;
26
27 var incoming = require('_http_incoming');
28 var IncomingMessage = incoming.IncomingMessage;
29 var readStart = incoming.readStart;
30 var readStop = incoming.readStop;
31
32 var isNumber = require('util').isNumber;
33 var debug = require('util').debuglog('http');
34 exports.debug = debug;
35
36 exports.CRLF = '\r\n';
37 exports.chunkExpression = /chunk/i;
38 exports.continueExpression = /100-continue/i;
39 exports.methods = HTTPParser.methods;
40
41 var kOnHeaders = HTTPParser.kOnHeaders | 0;
42 var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
43 var kOnBody = HTTPParser.kOnBody | 0;
44 var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
45
46 // Only called in the slow case where slow means
47 // that the request headers were either fragmented
48 // across multiple TCP packets or too large to be
49 // processed in a single run. This method is also
50 // called to process trailing HTTP headers.
51 function parserOnHeaders(headers, url) {
52   // Once we exceeded headers limit - stop collecting them
53   if (this.maxHeaderPairs <= 0 ||
54       this._headers.length < this.maxHeaderPairs) {
55     this._headers = this._headers.concat(headers);
56   }
57   this._url += url;
58 }
59
60 // info.headers and info.url are set only if .onHeaders()
61 // has not been called for this request.
62 //
63 // info.url is not set for response parsers but that's not
64 // applicable here since all our parsers are request parsers.
65 function parserOnHeadersComplete(info) {
66   debug('parserOnHeadersComplete', info);
67   var parser = this;
68   var headers = info.headers;
69   var url = info.url;
70
71   if (!headers) {
72     headers = parser._headers;
73     parser._headers = [];
74   }
75
76   if (!url) {
77     url = parser._url;
78     parser._url = '';
79   }
80
81   parser.incoming = new IncomingMessage(parser.socket);
82   parser.incoming.httpVersionMajor = info.versionMajor;
83   parser.incoming.httpVersionMinor = info.versionMinor;
84   parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
85   parser.incoming.url = url;
86
87   var n = headers.length;
88
89   // If parser.maxHeaderPairs <= 0 - assume that there're no limit
90   if (parser.maxHeaderPairs > 0) {
91     n = Math.min(n, parser.maxHeaderPairs);
92   }
93
94   parser.incoming._addHeaderLines(headers, n);
95
96   if (isNumber(info.method)) {
97     // server only
98     parser.incoming.method = HTTPParser.methods[info.method];
99   } else {
100     // client only
101     parser.incoming.statusCode = info.statusCode;
102     parser.incoming.statusMessage = info.statusMessage;
103   }
104
105   parser.incoming.upgrade = info.upgrade;
106
107   var skipBody = false; // response to HEAD or CONNECT
108
109   if (!info.upgrade) {
110     // For upgraded connections and CONNECT method request,
111     // we'll emit this after parser.execute
112     // so that we can capture the first part of the new protocol
113     skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
114   }
115
116   return skipBody;
117 }
118
119 // XXX This is a mess.
120 // TODO: http.Parser should be a Writable emits request/response events.
121 function parserOnBody(b, start, len) {
122   var parser = this;
123   var stream = parser.incoming;
124
125   // if the stream has already been removed, then drop it.
126   if (!stream)
127     return;
128
129   var socket = stream.socket;
130
131   // pretend this was the result of a stream._read call.
132   if (len > 0 && !stream._dumped) {
133     var slice = b.slice(start, start + len);
134     var ret = stream.push(slice);
135     if (!ret)
136       readStop(socket);
137   }
138 }
139
140 function parserOnMessageComplete() {
141   var parser = this;
142   var stream = parser.incoming;
143
144   if (stream) {
145     stream.complete = true;
146     // Emit any trailing headers.
147     var headers = parser._headers;
148     if (headers) {
149       parser.incoming._addHeaderLines(headers, headers.length);
150       parser._headers = [];
151       parser._url = '';
152     }
153
154     if (!stream.upgrade)
155       // For upgraded connections, also emit this after parser.execute
156       stream.push(null);
157   }
158
159   if (stream && !parser.incoming._pendings.length) {
160     // For emit end event
161     stream.push(null);
162   }
163
164   // force to read the next incoming message
165   readStart(parser.socket);
166 }
167
168
169 var parsers = new FreeList('parsers', 1000, function() {
170   var parser = new HTTPParser(HTTPParser.REQUEST);
171
172   parser._headers = [];
173   parser._url = '';
174
175   // Only called in the slow case where slow means
176   // that the request headers were either fragmented
177   // across multiple TCP packets or too large to be
178   // processed in a single run. This method is also
179   // called to process trailing HTTP headers.
180   parser[kOnHeaders] = parserOnHeaders;
181   parser[kOnHeadersComplete] = parserOnHeadersComplete;
182   parser[kOnBody] = parserOnBody;
183   parser[kOnMessageComplete] = parserOnMessageComplete;
184
185   return parser;
186 });
187 exports.parsers = parsers;
188
189
190 // Free the parser and also break any links that it
191 // might have to any other things.
192 // TODO: All parser data should be attached to a
193 // single object, so that it can be easily cleaned
194 // up by doing `parser.data = {}`, which should
195 // be done in FreeList.free.  `parsers.free(parser)`
196 // should be all that is needed.
197 function freeParser(parser, req, socket) {
198   if (parser) {
199     parser._headers = [];
200     parser.onIncoming = null;
201     if (parser.socket)
202       parser.socket.parser = null;
203     parser.socket = null;
204     parser.incoming = null;
205     if (parsers.free(parser) === false)
206       parser.close();
207     parser = null;
208   }
209   if (req) {
210     req.parser = null;
211   }
212   if (socket) {
213     socket.parser = null;
214   }
215 }
216 exports.freeParser = freeParser;
217
218
219 function ondrain() {
220   if (this._httpMessage) this._httpMessage.emit('drain');
221 }
222
223
224 function httpSocketSetup(socket) {
225   socket.removeListener('drain', ondrain);
226   socket.on('drain', ondrain);
227 }
228 exports.httpSocketSetup = httpSocketSetup;