diff options
author | Timothy J Fontaine <tjfontaine@gmail.com> | 2013-04-11 15:15:41 -0700 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2013-04-17 00:08:28 +0200 |
commit | bb56489f21da9d668c279edb45d6a90d03517a30 (patch) | |
tree | 75ba40840ef03fb020179a62c7f3df5bf394c4ea /lib/_http_common.js | |
parent | 5909a9c9bddddaab91693d18a8840d8b300bbc28 (diff) | |
download | node-new-bb56489f21da9d668c279edb45d6a90d03517a30.tar.gz |
http: move parsers into _http_common.js
Diffstat (limited to 'lib/_http_common.js')
-rw-r--r-- | lib/_http_common.js | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/lib/_http_common.js b/lib/_http_common.js new file mode 100644 index 0000000000..2c6b7d5d3a --- /dev/null +++ b/lib/_http_common.js @@ -0,0 +1,209 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +var FreeList = require('freelist').FreeList; +var HTTPParser = process.binding('http_parser').HTTPParser; + +var incoming = require('_http_incoming'); +var IncomingMessage = incoming.IncomingMessage; +var readStart = incoming.readStart; +var readStop = incoming.readStop; + +// Only called in the slow case where slow means +// that the request headers were either fragmented +// across multiple TCP packets or too large to be +// processed in a single run. This method is also +// called to process trailing HTTP headers. +function parserOnHeaders(headers, url) { + // Once we exceeded headers limit - stop collecting them + if (this.maxHeaderPairs <= 0 || + this._headers.length < this.maxHeaderPairs) { + this._headers = this._headers.concat(headers); + } + this._url += url; +} + +// info.headers and info.url are set only if .onHeaders() +// has not been called for this request. +// +// info.url is not set for response parsers but that's not +// applicable here since all our parsers are request parsers. +function parserOnHeadersComplete(info) { + var parser = this; + var headers = info.headers; + var url = info.url; + + if (!headers) { + headers = parser._headers; + parser._headers = []; + } + + if (!url) { + url = parser._url; + parser._url = ''; + } + + parser.incoming = new IncomingMessage(parser.socket); + parser.incoming.httpVersionMajor = info.versionMajor; + parser.incoming.httpVersionMinor = info.versionMinor; + parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor; + parser.incoming.url = url; + + var n = headers.length; + + // If parser.maxHeaderPairs <= 0 - assume that there're no limit + if (parser.maxHeaderPairs > 0) { + n = Math.min(n, parser.maxHeaderPairs); + } + + for (var i = 0; i < n; i += 2) { + var k = headers[i]; + var v = headers[i + 1]; + parser.incoming._addHeaderLine(k, v); + } + + + if (info.method) { + // server only + parser.incoming.method = info.method; + } else { + // client only + parser.incoming.statusCode = info.statusCode; + // CHECKME dead code? we're always a request parser + } + + parser.incoming.upgrade = info.upgrade; + + var skipBody = false; // response to HEAD or CONNECT + + if (!info.upgrade) { + // For upgraded connections and CONNECT method request, + // we'll emit this after parser.execute + // so that we can capture the first part of the new protocol + skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive); + } + + return skipBody; +} + +// XXX This is a mess. +// TODO: http.Parser should be a Writable emits request/response events. +function parserOnBody(b, start, len) { + var parser = this; + var stream = parser.incoming; + + // if the stream has already been removed, then drop it. + if (!stream) + return; + + var socket = stream.socket; + + // pretend this was the result of a stream._read call. + if (len > 0 && !stream._dumped) { + var slice = b.slice(start, start + len); + var ret = stream.push(slice); + if (!ret) + readStop(socket); + } +} + +function parserOnMessageComplete() { + var parser = this; + var stream = parser.incoming; + + if (stream) { + stream.complete = true; + // Emit any trailing headers. + var headers = parser._headers; + if (headers) { + for (var i = 0, n = headers.length; i < n; i += 2) { + var k = headers[i]; + var v = headers[i + 1]; + parser.incoming._addHeaderLine(k, v); + } + parser._headers = []; + parser._url = ''; + } + + if (!stream.upgrade) + // For upgraded connections, also emit this after parser.execute + stream.push(null); + } + + if (stream && !parser.incoming._pendings.length) { + // For emit end event + stream.push(null); + } + + if (parser.socket.readable) { + // force to read the next incoming message + readStart(parser.socket); + } +} + + +var parsers = new FreeList('parsers', 1000, function() { + var parser = new HTTPParser(HTTPParser.REQUEST); + + parser._headers = []; + parser._url = ''; + + // Only called in the slow case where slow means + // that the request headers were either fragmented + // across multiple TCP packets or too large to be + // processed in a single run. This method is also + // called to process trailing HTTP headers. + parser.onHeaders = parserOnHeaders; + parser.onHeadersComplete = parserOnHeadersComplete; + parser.onBody = parserOnBody; + parser.onMessageComplete = parserOnMessageComplete; + + return parser; +}); +exports.parsers = parsers; + + +// Free the parser and also break any links that it +// might have to any other things. +// TODO: All parser data should be attached to a +// single object, so that it can be easily cleaned +// up by doing `parser.data = {}`, which should +// be done in FreeList.free. `parsers.free(parser)` +// should be all that is needed. +function freeParser(parser, req) { + if (parser) { + parser._headers = []; + parser.onIncoming = null; + if (parser.socket) { + parser.socket.onend = null; + parser.socket.ondata = null; + parser.socket.parser = null; + } + parser.socket = null; + parser.incoming = null; + parsers.free(parser); + parser = null; + } + if (req) { + req.parser = null; + } +} +exports.freeParser = freeParser; |