'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); 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': case 'retry-after': case 'etag': case 'last-modified': case 'server': case 'age': case 'expires': // drop duplicates if (dest[field] === undefined) dest[field] = value; break; default: // make comma-separated list if (typeof dest[field] === 'string') { 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(); } };