1 // Copyright Joyent, Inc. and other Node contributors.
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:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
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.
22 var FreeList = require('freelist').FreeList;
23 var HTTPParser = process.binding('http_parser').HTTPParser;
25 var incoming = require('_http_incoming');
26 var IncomingMessage = incoming.IncomingMessage;
27 var readStart = incoming.readStart;
28 var readStop = incoming.readStop;
30 var isNumber = require('util').isNumber;
31 var debug = require('util').debuglog('http');
32 exports.debug = debug;
34 exports.CRLF = '\r\n';
35 exports.chunkExpression = /chunk/i;
36 exports.continueExpression = /100-continue/i;
37 exports.methods = HTTPParser.methods;
39 var kOnHeaders = HTTPParser.kOnHeaders | 0;
40 var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
41 var kOnBody = HTTPParser.kOnBody | 0;
42 var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
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);
58 // info.headers and info.url are set only if .onHeaders()
59 // has not been called for this request.
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);
66 var headers = info.headers;
70 headers = parser._headers;
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;
85 var n = headers.length;
87 // If parser.maxHeaderPairs <= 0 - assume that there're no limit
88 if (parser.maxHeaderPairs > 0) {
89 n = Math.min(n, parser.maxHeaderPairs);
92 parser.incoming._addHeaderLines(headers, n);
94 if (isNumber(info.method)) {
96 parser.incoming.method = HTTPParser.methods[info.method];
99 parser.incoming.statusCode = info.statusCode;
100 parser.incoming.statusMessage = info.statusMessage;
103 parser.incoming.upgrade = info.upgrade;
105 var skipBody = false; // response to HEAD or CONNECT
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);
117 // XXX This is a mess.
118 // TODO: http.Parser should be a Writable emits request/response events.
119 function parserOnBody(b, start, len) {
121 var stream = parser.incoming;
123 // if the stream has already been removed, then drop it.
127 var socket = stream.socket;
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);
138 function parserOnMessageComplete() {
140 var stream = parser.incoming;
143 stream.complete = true;
144 // Emit any trailing headers.
145 var headers = parser._headers;
147 parser.incoming._addHeaderLines(headers, headers.length);
148 parser._headers = [];
153 // For upgraded connections, also emit this after parser.execute
157 if (stream && !parser.incoming._pendings.length) {
158 // For emit end event
162 // force to read the next incoming message
163 readStart(parser.socket);
167 var parsers = new FreeList('parsers', 1000, function() {
168 var parser = new HTTPParser(HTTPParser.REQUEST);
170 parser._headers = [];
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;
185 exports.parsers = parsers;
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) {
197 parser._headers = [];
198 parser.onIncoming = null;
200 parser.socket.parser = null;
201 parser.socket = null;
202 parser.incoming = null;
203 if (parsers.free(parser) === false)
211 socket.parser = null;
214 exports.freeParser = freeParser;
218 if (this._httpMessage) this._httpMessage.emit('drain');
222 function httpSocketSetup(socket) {
223 socket.removeListener('drain', ondrain);
224 socket.on('drain', ondrain);
226 exports.httpSocketSetup = httpSocketSetup;