7994cdd98d927774ba7558a1eaca810bbd550bde
[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 var FreeList = require('freelist').FreeList;
23 var HTTPParser = process.binding('http_parser').HTTPParser;
24
25 var incoming = require('_http_incoming');
26 var IncomingMessage = incoming.IncomingMessage;
27 var readStart = incoming.readStart;
28 var readStop = incoming.readStop;
29
30 var isNumber = require('util').isNumber;
31 var debug = require('util').debuglog('http');
32 exports.debug = debug;
33
34 exports.CRLF = '\r\n';
35 exports.chunkExpression = /chunk/i;
36 exports.continueExpression = /100-continue/i;
37 exports.methods = HTTPParser.methods;
38
39 var kOnHeaders = HTTPParser.kOnHeaders | 0;
40 var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
41 var kOnBody = HTTPParser.kOnBody | 0;
42 var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
43
44 // Only called in the slow case where slow means
45 // that the request headers were either fragmented
46 // across multiple TCP packets or too large to be
47 // processed in a single run. This method is also
48 // called to process trailing HTTP headers.
49 function parserOnHeaders(headers, url) {
50   // Once we exceeded headers limit - stop collecting them
51   if (this.maxHeaderPairs <= 0 ||
52       this._headers.length < this.maxHeaderPairs) {
53     this._headers = this._headers.concat(headers);
54   }
55   this._url += url;
56 }
57
58 // info.headers and info.url are set only if .onHeaders()
59 // has not been called for this request.
60 //
61 // info.url is not set for response parsers but that's not
62 // applicable here since all our parsers are request parsers.
63 function parserOnHeadersComplete(info) {
64   debug('parserOnHeadersComplete', info);
65   var parser = this;
66   var headers = info.headers;
67   var url = info.url;
68
69   if (!headers) {
70     headers = parser._headers;
71     parser._headers = [];
72   }
73
74   if (!url) {
75     url = parser._url;
76     parser._url = '';
77   }
78
79   parser.incoming = new IncomingMessage(parser.socket);
80   parser.incoming.httpVersionMajor = info.versionMajor;
81   parser.incoming.httpVersionMinor = info.versionMinor;
82   parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
83   parser.incoming.url = url;
84
85   var n = headers.length;
86
87   // If parser.maxHeaderPairs <= 0 - assume that there're no limit
88   if (parser.maxHeaderPairs > 0) {
89     n = Math.min(n, parser.maxHeaderPairs);
90   }
91
92   parser.incoming._addHeaderLines(headers, n);
93
94   if (isNumber(info.method)) {
95     // server only
96     parser.incoming.method = HTTPParser.methods[info.method];
97   } else {
98     // client only
99     parser.incoming.statusCode = info.statusCode;
100     parser.incoming.statusMessage = info.statusMessage;
101   }
102
103   parser.incoming.upgrade = info.upgrade;
104
105   var skipBody = false; // response to HEAD or CONNECT
106
107   if (!info.upgrade) {
108     // For upgraded connections and CONNECT method request,
109     // we'll emit this after parser.execute
110     // so that we can capture the first part of the new protocol
111     skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
112   }
113
114   return skipBody;
115 }
116
117 // XXX This is a mess.
118 // TODO: http.Parser should be a Writable emits request/response events.
119 function parserOnBody(b, start, len) {
120   var parser = this;
121   var stream = parser.incoming;
122
123   // if the stream has already been removed, then drop it.
124   if (!stream)
125     return;
126
127   var socket = stream.socket;
128
129   // pretend this was the result of a stream._read call.
130   if (len > 0 && !stream._dumped) {
131     var slice = b.slice(start, start + len);
132     var ret = stream.push(slice);
133     if (!ret)
134       readStop(socket);
135   }
136 }
137
138 function parserOnMessageComplete() {
139   var parser = this;
140   var stream = parser.incoming;
141
142   if (stream) {
143     stream.complete = true;
144     // Emit any trailing headers.
145     var headers = parser._headers;
146     if (headers) {
147       parser.incoming._addHeaderLines(headers, headers.length);
148       parser._headers = [];
149       parser._url = '';
150     }
151
152     if (!stream.upgrade)
153       // For upgraded connections, also emit this after parser.execute
154       stream.push(null);
155   }
156
157   if (stream && !parser.incoming._pendings.length) {
158     // For emit end event
159     stream.push(null);
160   }
161
162   // force to read the next incoming message
163   readStart(parser.socket);
164 }
165
166
167 var parsers = new FreeList('parsers', 1000, function() {
168   var parser = new HTTPParser(HTTPParser.REQUEST);
169
170   parser._headers = [];
171   parser._url = '';
172
173   // Only called in the slow case where slow means
174   // that the request headers were either fragmented
175   // across multiple TCP packets or too large to be
176   // processed in a single run. This method is also
177   // called to process trailing HTTP headers.
178   parser[kOnHeaders] = parserOnHeaders;
179   parser[kOnHeadersComplete] = parserOnHeadersComplete;
180   parser[kOnBody] = parserOnBody;
181   parser[kOnMessageComplete] = parserOnMessageComplete;
182
183   return parser;
184 });
185 exports.parsers = parsers;
186
187
188 // Free the parser and also break any links that it
189 // might have to any other things.
190 // TODO: All parser data should be attached to a
191 // single object, so that it can be easily cleaned
192 // up by doing `parser.data = {}`, which should
193 // be done in FreeList.free.  `parsers.free(parser)`
194 // should be all that is needed.
195 function freeParser(parser, req, socket) {
196   if (parser) {
197     parser._headers = [];
198     parser.onIncoming = null;
199     if (parser.socket)
200       parser.socket.parser = null;
201     parser.socket = null;
202     parser.incoming = null;
203     if (parsers.free(parser) === false)
204       parser.close();
205     parser = null;
206   }
207   if (req) {
208     req.parser = null;
209   }
210   if (socket) {
211     socket.parser = null;
212   }
213 }
214 exports.freeParser = freeParser;
215
216
217 function ondrain() {
218   if (this._httpMessage) this._httpMessage.emit('drain');
219 }
220
221
222 function httpSocketSetup(socket) {
223   socket.removeListener('drain', ondrain);
224   socket.on('drain', ondrain);
225 }
226 exports.httpSocketSetup = httpSocketSetup;