Skip to content

Commit

Permalink
HTTP: handling of invalid incoming headers.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gábor Molnár committed Aug 15, 2013
1 parent f1bc612 commit c87c0af
Showing 1 changed file with 87 additions and 15 deletions.
102 changes: 87 additions & 15 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ Server.prototype._start = function _start(socket) {
};

Server.prototype._onStream = function _onStream(stream) {
var request = new IncomingMessage(stream);
var request = new IncomingMessage(stream, 'REQUEST');
var response = new ServerResponse(stream);

request.once('ready', this.emit.bind(this, 'request', request, response));
Expand Down Expand Up @@ -178,32 +178,104 @@ function Agent(options) {
// Common IncomingMessage class
// ----------------------------

function IncomingMessage(stream) {
// Constructor
function IncomingMessage(stream, role) {
// * This is basically a read-only wrapper for the [Stream](stream.html) class.
PassThrough.call(this);

stream.pipe(this);
this._stream = stream;

// * HTTP/2.0 does not define a way to carry the version identifier that is included in the
// HTTP/1.1 request/status line. Version is always 2.0.
this.httpVersion = '2.0';
this.httpVersionMajor = 2;
this.httpVersionMinor = 0;

stream.pipe(this);
stream.once('headers', this._onHeaders.bind(this));
// * Other metadata is filled in when the headers arrive.
var onHeaders = (role === 'REQUEST') ? this._onRequestHeaders
: this._onResponseHeaders;
stream.once('headers', onHeaders.bind(this));
}
IncomingMessage.prototype = Object.create(PassThrough.prototype, { constructor: { value: IncomingMessage } });

IncomingMessage.prototype._onHeaders = function _onHeaders(headers) {
this.statusCode = headers[':status'];
this.method = headers[':method'];
this.url = headers[':path'];
// [Request Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-05#section-8.1.2.1)
IncomingMessage.prototype._onRequestHeaders = function _onRequestHeaders(headers) {
// * HTTP/2.0 request and response header fields carry information as a series of key-value pairs.
// This includes the target URI for the request, the status code for the response, as well as
// HTTP header fields.
this.headers = headers;

// * The ":method" header field includes the HTTP method
// * The ":scheme" header field includes the scheme portion of the target URI
// * The ":host" header field includes the authority portion of the target URI
// * The ":path" header field includes the path and query parts of the target URI.
// This field MUST NOT be empty; URIs that do not contain a path component MUST include a value
// of '/', unless the request is an OPTIONS request for '*', in which case the ":path" header
// field MUST include '*'.
// * All HTTP/2.0 requests MUST include exactly one valid value for all of these header fields. A
// server MUST treat the absence of any of these header fields, presence of multiple values, or
// an invalid value as a stream error of type PROTOCOL_ERROR.
var mapping = {
method: ':method',
scheme: ':scheme',
host: ':host',
url: ':path'
};
for (var key in mapping) {
var value = headers[mapping[key]];
if ((typeof value !== 'string') || (value.length === 0)) {
this._stream.emit('error', 'PROTOCOL_ERROR');
return;
}
this[key] = value;
delete headers[mapping[key]];
}

// * An HTTP/2.0 request MUST NOT include any of the following header fields: Connection, Host,
// Keep-Alive, Proxy-Connection, TE, Transfer-Encoding, and Upgrade. A server MUST treat the
// presence of any of these header fields as a stream error of type PROTOCOL_ERROR.
if (
('connection' in headers) ||
('host' in headers) ||
('keep-alive' in headers) ||
('proxy-connection' in headers) ||
('te' in headers) ||
('transfer-encoding' in headers) ||
('upgrade' in headers)
) {
this._stream.emit('error', 'PROTOCOL_ERROR');
return;
}

// * Host header is included in the headers object for backwards compatibility.
headers.host = this.host;

// * Signaling that the header arrived.
this.emit('ready');
};

// [Response Header Fields](http://tools.ietf.org/html/draft-ietf-httpbis-http2-05#section-8.1.2.2)
IncomingMessage.prototype._onResponseHeaders = function _onResponseHeaders(headers) {
// * HTTP/2.0 request and response header fields carry information as a series of key-value pairs.
// This includes the target URI for the request, the status code for the response, as well as
// HTTP header fields.
this.headers = headers;
headers.host = headers[':host'];
delete headers[':scheme'];
delete headers[':method'];
delete headers[':host'];
delete headers[':path'];

// * A single ":status" header field is defined that carries the HTTP status code field. This
// header field MUST be included in all responses.
// * A client MUST treat the absence of the ":status" header field, the presence of multiple
// values, or an invalid value as a stream error of type PROTOCOL_ERROR.
// * HTTP/2.0 does not define a way to carry the reason phrase that is included in an HTTP/1.1
// status line.
var statusCode = headers[':status'];
if ((typeof statusCode !== 'string') || (statusCode.length === 0)) {
this._stream.emit('error', 'PROTOCOL_ERROR');
return;
}
this.statusCode = statusCode;
delete headers[':status'];

// * Signaling that the header arrived.
this.emit('ready');
};

Expand Down Expand Up @@ -257,7 +329,7 @@ ClientRequest.prototype._start = function _start(stream, options) {
stream.headers(headers);
this.pipe(stream);

var response = new IncomingMessage(stream);
var response = new IncomingMessage(stream, 'RESPONSE');
response.once('ready', this.emit.bind(this, 'response', response));
};

Expand Down

0 comments on commit c87c0af

Please sign in to comment.