You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

201 lines
5.5 KiB

// 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 util = require('util');
var Stream = require('stream');
function readStart(socket) {
if (socket)
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.httpVersion = null;
this.complete = false;
this.headers = {};
this.rawHeaders = [];
this.trailers = {};
this.rawTrailers = [];
this.readable = true;
this._pendings = [];
this._pendingIndex = 0;
// request (server) only
this.url = '';
this.method = null;
// response (client) only
this.statusCode = null;
this.client = this.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);
};
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)
this.push(null);
else
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;
}
raw.push.apply(raw, headers);
for (var i = 0; i < n; i += 2) {
var k = headers[i];
var v = headers[i + 1];
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 (!util.isUndefined(dest[field])) {
dest[field].push(value);
} else {
dest[field] = [value];
}
break;
// Comma separate. Maybe make these arrays?
case 'accept':
case 'accept-charset':
case 'accept-encoding':
case 'accept-language':
case 'connection':
case 'cookie':
case 'pragma':
case 'link':
case 'www-authenticate':
case 'proxy-authenticate':
case 'sec-websocket-extensions':
case 'sec-websocket-protocol':
if (!util.isUndefined(dest[field])) {
dest[field] += ', ' + value;
} else {
dest[field] = value;
}
break;
default:
if (field.slice(0, 2) == 'x-') {
// except for x-
if (!util.isUndefined(dest[field])) {
dest[field] += ', ' + value;
} else {
dest[field] = value;
}
} else {
// drop duplicates
if (util.isUndefined(dest[field])) dest[field] = value;
}
break;
}
};
// 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();
}
};