Skip to content

Instantly share code, notes, and snippets.

@FooBarWidget
Last active December 22, 2015 20:19
Show Gist options
  • Save FooBarWidget/6525478 to your computer and use it in GitHub Desktop.
Save FooBarWidget/6525478 to your computer and use it in GitHub Desktop.
const HTTP_HEADERS_WITHOUT_PREFIX = {
'CONTENT_LENGTH': true,
'CONTENT_TYPE': true
};
function cgiKeyToHttpHeader(key) {
if (HTTP_HEADERS_WITHOUT_PREFIX[key]) {
return key.toLowerCase().replace(/_/g, '-');
} else if (key.match(/^HTTP_/)) {
return key.replace(/^HTTP_/, '').toLowerCase().replace(/_/g, '-');
} else {
return undefined;
}
}
function setHttpHeaders(httpHeaders, cgiHeaders) {
for (var i = 0; i < cgiHeaders.keys.length; i++) {
var key = cgiHeaders.keys[i];
var httpHeader = cgiKeyToHttpHeader(key);
if (httpHeader !== undefined) {
httpHeaders[httpHeader] = cgiHeaders[key];
}
}
if (cgiHeaders['HTTPS']) {
httpHeaders['x-forwarded-proto'] = 'https';
}
if (!httpHeaders['x-forwarded-for']) {
httpHeaders['x-forwarded-for'] = cgiHeaders['REMOTE_ADDR'];
}
}
function inferHttpVersion(protocolDescription) {
var match = protocolDescription.match(/^HTTP\/(.+)/);
if (match) {
return match[1];
}
}
function mayHaveRequestBody(headers) {
return headers['REQUEST_METHOD'] != 'GET' || headers['HTTP_UPGRADE'];
}
function createIncomingMessage(headers, socket) {
var message = new http.IncomingMessage(socket);
setHttpHeaders(message.headers, headers);
message.cgiHeaders = headers;
message.httpVersion = inferHttpVersion(headers['SERVER_PROTOCOL']);
message.method = headers['REQUEST_METHOD'];
message.url = headers['REQUEST_URI'];
message.connection.remoteAddress = headers['REMOTE_ADDR'];
message.connection.remotePort = parseInt(headers['REMOTE_PORT']);
function onSocketData(chunk) {
message.emit('data', chunk);
}
function onSocketEnd() {
message.emit('end');
}
socket.on('drain', function() {
message.emit('drain');
});
socket.on('timeout', function() {
message.emit('timeout');
});
/* Node's HTTP parser simulates an 'end' event if it determines that
* the request should not have a request body. Currently (Node 0.10.18),
* it thinks GET requests without an Upgrade header should not have a
* request body, even though technically such GET requests are allowed
* to have a request body. For compatibility reasons we implement the
* same behavior as Node's HTTP parser.
*/
if (mayHaveRequestBody(headers)) {
socket.on('data', onSocketData);
socket.on('end', onSocketEnd);
} else {
process.nextTick(onSocketEnd);
}
return message;
}
function createServerResponse(req) {
var res = new http.ServerResponse(req);
res.assignSocket(req.socket);
res.shouldKeepAlive = false;
res.once('finish', function() {
req.socket.destroySoon();
});
return res;
}
PhusionPassenger.on('request', function(headers, socket) {
var req = createIncomingMessage(headers, socket);
var res = createServerResponse(req);
res.writeHead(200, {'Content-Type': 'text/plain', 'Foo': 'bar'});
res.write('hello world');
res.end();
});
@sbull
Copy link

sbull commented Sep 18, 2013

There are some problems with this, particularly with cgiKeyToHttpHeader(). First, the first occurrence of replace() is missing an argument, so keys get returned like undefinedaccept, undefineduser-agent, etc. Also, it misses some important headers, such as content-length, content-type, etc. (anything that doesn't start with HTTP_).

It might be better to selectively discard headers, instead of only passing along ones that look like HTTP_?

Here's something a bit more useful:

function cgiKeyToHttpHeader(key) {
  if (key.match(/^HTTP_/)) {
    return key.replace(/^HTTP_/,'').toLowerCase().replace(/_/g, '-');
  } else {
    return key.toLowerCase().replace(/_/g, '-');
  }
}

@FooBarWidget
Copy link
Author

I've fixed both problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment