| 'use strict'; |
| |
| const util = require('util'); |
| const Stream = require('stream'); |
| |
| function readStart(socket) { |
| if (socket && !socket._paused && socket.readable) |
| socket.resume(); |
| } |
| exports.readStart = readStart; |
| |
| function readStop(socket) { |
| if (socket) |
| socket.pause(); |
| } |
| exports.readStop = readStop; |
| |
| |
| /* Abstract base class for ServerRequest and ClientResponse. */ |
| function IncomingMessage(socket) { |
| Stream.Readable.call(this); |
| |
| // XXX This implementation is kind of all over the place |
| // When the parser emits body chunks, they go in this list. |
| // _read() pulls them out, and when it finds EOF, it ends. |
| |
| this.socket = socket; |
| this.connection = socket; |
| |
| this.httpVersionMajor = null; |
| this.httpVersionMinor = null; |
| this.httpVersion = null; |
| this.complete = false; |
| this.headers = {}; |
| this.rawHeaders = []; |
| this.trailers = {}; |
| this.rawTrailers = []; |
| |
| this.readable = true; |
| |
| this.upgrade = null; |
| |
| // request (server) only |
| this.url = ''; |
| this.method = null; |
| |
| // response (client) only |
| this.statusCode = null; |
| this.statusMessage = null; |
| this.client = socket; |
| |
| // flag for backwards compatibility grossness. |
| this._consuming = false; |
| |
| // flag for when we decide that this message cannot possibly be |
| // read by the user, so there's no point continuing to handle it. |
| this._dumped = false; |
| } |
| util.inherits(IncomingMessage, Stream.Readable); |
| |
| |
| exports.IncomingMessage = IncomingMessage; |
| |
| |
| IncomingMessage.prototype.setTimeout = function(msecs, callback) { |
| if (callback) |
| this.on('timeout', callback); |
| this.socket.setTimeout(msecs); |
| return this; |
| }; |
| |
| |
| IncomingMessage.prototype.read = function(n) { |
| this._consuming = true; |
| this.read = Stream.Readable.prototype.read; |
| return this.read(n); |
| }; |
| |
| |
| IncomingMessage.prototype._read = function(n) { |
| // We actually do almost nothing here, because the parserOnBody |
| // function fills up our internal buffer directly. However, we |
| // do need to unpause the underlying socket so that it flows. |
| if (this.socket.readable) |
| readStart(this.socket); |
| }; |
| |
| |
| // It's possible that the socket will be destroyed, and removed from |
| // any messages, before ever calling this. In that case, just skip |
| // it, since something else is destroying this connection anyway. |
| IncomingMessage.prototype.destroy = function(error) { |
| if (this.socket) |
| this.socket.destroy(error); |
| }; |
| |
| |
| IncomingMessage.prototype._addHeaderLines = function(headers, n) { |
| if (headers && headers.length) { |
| var raw, dest; |
| if (this.complete) { |
| raw = this.rawTrailers; |
| dest = this.trailers; |
| } else { |
| raw = this.rawHeaders; |
| dest = this.headers; |
| } |
| |
| for (var i = 0; i < n; i += 2) { |
| var k = headers[i]; |
| var v = headers[i + 1]; |
| raw.push(k); |
| raw.push(v); |
| this._addHeaderLine(k, v, dest); |
| } |
| } |
| }; |
| |
| |
| // Add the given (field, value) pair to the message |
| // |
| // Per RFC2616, section 4.2 it is acceptable to join multiple instances of the |
| // same header with a ', ' if the header in question supports specification of |
| // multiple values this way. If not, we declare the first instance the winner |
| // and drop the second. Extended header fields (those beginning with 'x-') are |
| // always joined. |
| IncomingMessage.prototype._addHeaderLine = function(field, value, dest) { |
| field = field.toLowerCase(); |
| switch (field) { |
| // Array headers: |
| case 'set-cookie': |
| if (dest[field] !== undefined) { |
| dest[field].push(value); |
| } else { |
| dest[field] = [value]; |
| } |
| break; |
| |
| /* eslint-disable max-len */ |
| // list is taken from: |
| // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp |
| /* eslint-enable max-len */ |
| case 'content-type': |
| case 'content-length': |
| case 'user-agent': |
| case 'referer': |
| case 'host': |
| case 'authorization': |
| case 'proxy-authorization': |
| case 'if-modified-since': |
| case 'if-unmodified-since': |
| case 'from': |
| case 'location': |
| case 'max-forwards': |
| // drop duplicates |
| if (dest[field] === undefined) |
| dest[field] = value; |
| break; |
| |
| default: |
| // make comma-separated list |
| if (dest[field] !== undefined) { |
| dest[field] += ', ' + value; |
| } else { |
| dest[field] = value; |
| } |
| } |
| }; |
| |
| |
| // Call this instead of resume() if we want to just |
| // dump all the data to /dev/null |
| IncomingMessage.prototype._dump = function() { |
| if (!this._dumped) { |
| this._dumped = true; |
| this.resume(); |
| } |
| }; |