http.ServerRequest.headers are lowercased?

2,589 views
Skip to first unread message

Joran Greef

unread,
Feb 4, 2010, 8:00:29 AM2/4/10
to nodejs
Server:

var sys = require('sys');
var http = require('http');
var server = http.createServer(
function(request, response) {
sys.puts(JSON.stringify(request.headers));
}
);
server.listen(8080);

Receives a request from:

var http = require('http');
var client = http.createClient(8080, "localhost");
var request = client.request("POST", "/test", {
"Host": "localhost",
'Content-Type': 'text/plain',
'Content-Length': '4'
});
request.sendBody('test');
request.finish(function() {});

Such that the http.ServerRequest.headers have been lowercased:

{
"host": "localhost",
"content-type": "text/plain",
"content-length": "4",
"connection": "close"
}

Is this behavior correct?

Felix Geisendörfer

unread,
Feb 4, 2010, 8:10:09 AM2/4/10
to nodejs
> Is this behavior correct?

Yes, HTTP specifies header fields to be case-insensitive [1].

Node has taken the pragmatic approach of lowercasing all incoming
headers to ease accessing them.

--fg

[1]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

Joran Greef

unread,
Feb 4, 2010, 8:16:01 AM2/4/10
to nodejs
Thanks Felix.
Message has been deleted

Joran Greef

unread,
Feb 4, 2010, 9:24:32 AM2/4/10
to nodejs
Just a silly idea in the spirit of http://en.wikipedia.org/wiki/Principle_of_least_surprise

Why not case incoming headers according to the most common
convention,
namely Upper-Dash-Case, e.g.: 'Content-Type', 'Host', 'Content-
Length', etc.

(The Node docs use upper dashcase when referring to the headers
object).

Dean Landolt

unread,
Feb 4, 2010, 9:26:41 AM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 9:24 AM, Joran Greef <joran...@gmail.com> wrote:
Just a silly idea in the spirit of http://en.wikipedia.org/wiki/Principle_of_least_surprise

Why not case incoming headers according to the most common
convention,
namely Upper-Dash-Case, e.g.: 'Content-Type', 'Host', 'Content-
Length', etc.


What do you do with ETag? The X-Headers? To my knowledge there's no sane convention other than just ignoring case entirely.

Joran Greef

unread,
Feb 4, 2010, 9:41:48 AM2/4/10
to nodejs
Principle of least surprise: ETag -> ETag

Just use a lookup hash:

1. Lowercase incoming header key.
2. Use lookup table to find corresponding "most common" key.
3. If not found, resort to upper dash case.

Pretty sane to me.

Avoids a whole lot of code having to be re-written,
http://github.com/guille/node.websocket.js/ for starters.

One could also suggest that a low-level (and fantastic) Http client
that doesn't handle cookies, gzip, etc. (far more useful in my
opinion) should perhaps let the headers just "pass-through". Albeit in
the name of convenience, the second-order effects of lowercasing cause
more inconvenience.

On Feb 4, 4:26 pm, Dean Landolt <d...@deanlandolt.com> wrote:

Felix Geisendörfer

unread,
Feb 4, 2010, 9:50:36 AM2/4/10
to nodejs
> Just use a lookup hash:
>
> 1. Lowercase incoming header key.
> 2. Use lookup table to find corresponding "most common" key.
> 3. If not found, resort to upper dash case.
>
> Pretty sane to me.

And slow.

Sorry if it's causing inconvenience, but I think the current solution
is a good compromise between simplicity & speed. I also think that
explaining your surprise-elimination algorithm, and the exceptions to
the rules, will take longer than just telling people the headers are
lowercased.

--fg

On Feb 4, 3:41 pm, Joran Greef <jorangr...@gmail.com> wrote:
> Principle of least surprise: ETag -> ETag
>
> Just use a lookup hash:
>
> 1. Lowercase incoming header key.
> 2. Use lookup table to find corresponding "most common" key.
> 3. If not found, resort to upper dash case.
>
> Pretty sane to me.
>

> Avoids a whole lot of code having to be re-written,http://github.com/guille/node.websocket.js/for starters.

Joran Greef

unread,
Feb 4, 2010, 9:51:52 AM2/4/10
to nodejs
Exactly. The best thing would be to not try and mess with the headers
at all.

Karl Guertin

unread,
Feb 4, 2010, 10:01:48 AM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 9:51 AM, Joran Greef <joran...@gmail.com> wrote:
> Exactly. The best thing would be to not try and mess with the headers
> at all.

So if I sent 'content-type' and you were expecting 'Content-Type'? The
case conversion has to happen somewhere for reliable header checks.
The rule is pretty simple.

Joran Greef

unread,
Feb 4, 2010, 11:04:43 AM2/4/10
to nodejs
I was excited to see that Node had broken with the legacy of CGI-style
headers. I was disappointed that it had opted to be so helpful in this
regard however (and without mentioning it in the docs).

Almost every piece of literature ever written regarding headers writes
headers case-sensitively. And almost everyone, expects them thus. When
last did you receive a request with a 'conTENT-type' header? Those
that want to cater for that sort of request are already doing /Content-
Type/i. If case conversion has to happen somewhere then the answer is
that it already does. Where it happens is beyond the scope of Node.
Because otherwise, Node will be obliviously lowercasing incoming
headers, only to: 1. break existing code or 2. find that existing code
then does the same thing. The consequences of whether the case
conversion happens or not are beyond the responsibility of Node.

Karl Guertin

unread,
Feb 4, 2010, 11:24:24 AM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 11:04 AM, Joran Greef <joran...@gmail.com> wrote:
> 1. break existing code

What existing code?

I can't figure out why you're arguing against reliable handling of
something the spec defines as case insensitive and going against best
practices by being strict about what you accept.

Felix Geisendörfer

unread,
Feb 4, 2010, 11:36:16 AM2/4/10
to nodejs
The spec is clear, headers are case insensitive. Any proxy along the
way may do with them as he pleases, there is not much point in keeping
the "original" casing.

Besides that, it would become a major PITA to access headers:

var contentType;
for (var header in req.headers) {
if (header.match(/Content-Type/i) {
contentType = req.headers[header];
break;
}
}
puts(contentType);

instead of:

puts(req.headers['content-type']);

Not much fun.

We could also provide a convenience function that abstracts this logic
away from the user, so they could call:

puts(req.header('Content-Type'));

But that is really overkill for something that is not actually a
problem.

That being said, the function approach may be interesting for headers
with multiple occurrences of the same field, but that's another
problem / discussion.

--fg

On Feb 4, 5:24 pm, Karl Guertin <grayr...@gmail.com> wrote:

Dean Landolt

unread,
Feb 4, 2010, 12:02:08 PM2/4/10
to nod...@googlegroups.com


2010/2/4 Felix Geisendörfer <fe...@debuggable.com>

The spec is clear, headers are case insensitive. Any proxy along the
way may do with them as he pleases, there is not much point in keeping
the "original" casing.

Besides that, it would become a major PITA to access headers:

var contentType;
for (var header in req.headers) {
  if (header.match(/Content-Type/i) {
    contentType = req.headers[header];
    break;
  }
}
puts(contentType);

instead of:

puts(req.headers['content-type']);

Not much fun.

We could also provide a convenience function that abstracts this logic
away from the user, so they could call:

puts(req.header('Content-Type'));

But that is really overkill for something that is not actually a
problem.

That being said, the function approach may be interesting for headers
with multiple occurrences of the same field, but that's another
problem / discussion.


I believe Isaac's patch-in-waiting makes that easy too -- the header value would just be a list. It also has the interesting property that if you screw up and concat a string to it it will do the Right Thing and comma-join -- at least, the Right Thing according to spec -- there are real-world cases where this would be the Wrong Thing, but if your dealing with cookies, for instance, just don't screw up and concat to an array and you'll be fine.

Dean Landolt

unread,
Feb 4, 2010, 12:05:37 PM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 11:04 AM, Joran Greef <joran...@gmail.com> wrote:
I was excited to see that Node had broken with the legacy of CGI-style
headers. I was disappointed that it had opted to be so helpful in this
regard however (and without mentioning it in the docs).

Almost every piece of literature ever written regarding headers writes
headers case-sensitively.

Every piece of literature that describes HTTP message headers describes them as case-insensitive -- to me the fact that the examples typically use Mixed-Case only reinforces this point, but I can see how the almost-kinda-usually-consistent casing convention looks more correct. If you feel that way, or you have code that (incorrectly) assumes a specific case, you can certainly rewrite the headers with a proxy. Perhaps it would be a worthwhile feature for node to offer a configurable map for those folks that really want their headers a specific case on the wire but it's not strictly necessary.


Tom Robinson

unread,
Feb 4, 2010, 3:36:54 PM2/4/10
to nod...@googlegroups.com

Then the user has to be aware of what the "most common" (according to who? you?) casing of a particular header name is. This is far worse than just lowercasing everything.

Benjamin Thomas

unread,
Feb 4, 2010, 4:13:05 PM2/4/10
to nod...@googlegroups.com
> Then the user has to be aware of what the "most common" (according to who? you?) casing of a particular header name is. This is far worse than just lowercasing everything.

Yeah, talk about the principle of least surprise. As it works now
everything works in the same exact, easily predictable way. Where if
you go another route then you have no idea if a header is common
enough to have another name. Yuck.

Louis Santillan

unread,
Feb 4, 2010, 6:24:10 PM2/4/10
to nod...@googlegroups.com

Not all of them. The WebSocket draft protocol is strict on header
order, case, and newlines (requires \r\n, see 1.2 of
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75).

Tom Robinson

unread,
Feb 4, 2010, 6:49:56 PM2/4/10
to nod...@googlegroups.com

Matthew Dempsky

unread,
Feb 4, 2010, 7:02:44 PM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 3:24 PM, Louis Santillan <lpsa...@gmail.com> wrote:
> Not all of them.  The WebSocket draft protocol is strict on header
> order, case, and newlines (requires \r\n, see 1.2 of
> http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75).

It's fine for a server to be more lenient in what it receives though,
as long as it's strict in how it formats its response. E.g., Go's
WebSocket implementation uses a regular HTTP handler that listens on
the appropriate URL path, checks that the header fields are correct
(to the best of its ability, given the sanitization already applied by
the generic HTTP parts), and then hijacks the TCP socket and sends the
strict header back itself.

Matthew Dempsky

unread,
Feb 4, 2010, 7:03:53 PM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 4:02 PM, Matthew Dempsky <mat...@dempsky.org> wrote:
> E.g., Go's WebSocket implementation

I suppose I should go ahead and include a link to the relevant source code:

http://golang.org/src/pkg/websocket/server.go

Dean Landolt

unread,
Feb 4, 2010, 7:56:12 PM2/4/10
to nod...@googlegroups.com


Where in this spec does it require (or even suggest) that headers are of a specific case? I haven't read the whole lot but searched for "header" and "case" and read around that. I can say this much -- each of the WebSocket-* fields also appears as "websocket-*". Further, doesn't RFC 2616 also require \r\n? As far as header order, I didn't see anything but I did see that they explicitly disallow multiline headers (probably a smart move!).

Matthew Dempsky

unread,
Feb 4, 2010, 8:07:54 PM2/4/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 4:56 PM, Dean Landolt <de...@deanlandolt.com> wrote:
> Where in this spec does it require (or even suggest) that headers are of a
> specific case?

Not the entire header, but section 1.2 states "[t]he first three lines
in each case are hard-coded (the exact case and order matters)."

Dean Landolt

unread,
Feb 4, 2010, 8:14:52 PM2/4/10
to nod...@googlegroups.com

Wow, I didn't catch that. Considering WebSockets is a transport on top of HTTP I'm going to go out on a limb and suggest this is a spec hole you could drive a truck through (proxies can do crazy things, in theory AND practice).

Still, like Tom said, WebSockets isn't HTTP :)

Louis Santillan

unread,
Feb 5, 2010, 1:05:20 AM2/5/10
to nod...@googlegroups.com
On Thu, Feb 4, 2010 at 3:49 PM, Tom Robinson <tlrob...@gmail.com> wrote:
> On Feb 4, 2010, at 3:24 PM, Louis Santillan wrote:
>> On Thu, Feb 4, 2010 at 9:05 AM, Dean Landolt <de...@deanlandolt.com> wrote:
[SNIP]

>>> Every piece of literature that describes HTTP message headers describes them
>>> as case-insensitive -- to me the fact that the examples typically use
>>> Mixed-Case only reinforces this point, but I can see how
>>> the almost-kinda-usually-consistent casing convention looks more correct. If
>>> you feel that way, or you have code that (incorrectly) assumes a specific
>>> case, you can certainly rewrite the headers with a proxy. Perhaps it would
>>> be a worthwhile feature for node to offer a configurable map for those folks
>>> that really want their headers a specific case on the wire but it's not
>>> strictly necessary.
>>
>> Not all of them.  The WebSocket draft protocol is strict on header
>> order, case, and newlines (requires \r\n, see 1.2 of
>> http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75).
>
> WebSocket != HTTP
>
> http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75#page-7

I didn't claim WebSockets to be HTTP. :) Just pointing out an
application where header, case, and newlines matter in the HTTP
protocol. Thanks Matthew. As for a hole in the spec, I don't know if
I would call it that. I've worked on WireShark protocol analyzers and
the less slack in a spec the better. And I agree, just feels odd when
you're used to trying to cover everything under the sun.

Dean Landolt

unread,
Feb 5, 2010, 10:59:47 AM2/5/10
to nod...@googlegroups.com
On Fri, Feb 5, 2010 at 1:05 AM, Louis Santillan <lpsa...@gmail.com> wrote:
On Thu, Feb 4, 2010 at 3:49 PM, Tom Robinson <tlrob...@gmail.com> wrote:
> On Feb 4, 2010, at 3:24 PM, Louis Santillan wrote:
>> On Thu, Feb 4, 2010 at 9:05 AM, Dean Landolt <de...@deanlandolt.com> wrote:
[SNIP]
>>> Every piece of literature that describes HTTP message headers describes them
>>> as case-insensitive -- to me the fact that the examples typically use
>>> Mixed-Case only reinforces this point, but I can see how
>>> the almost-kinda-usually-consistent casing convention looks more correct. If
>>> you feel that way, or you have code that (incorrectly) assumes a specific
>>> case, you can certainly rewrite the headers with a proxy. Perhaps it would
>>> be a worthwhile feature for node to offer a configurable map for those folks
>>> that really want their headers a specific case on the wire but it's not
>>> strictly necessary.
>>
>> Not all of them.  The WebSocket draft protocol is strict on header
>> order, case, and newlines (requires \r\n, see 1.2 of
>> http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75).
>
> WebSocket != HTTP
>
> http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75#page-7

I didn't claim WebSockets to be HTTP.  :)  Just pointing out an
application where header, case, and newlines matter in the HTTP
protocol.  

There -- that -- you just did it again (conflating WebSockets and HTTP)

The WebSockets protocol uses HTTP as a transport, but it isn't HTTP.
 
Thanks Matthew.  As for a hole in the spec, I don't know if
I would call it that.  I've worked on WireShark protocol analyzers and
the less slack in a spec the better.

Sure but by using HTTP as a transport you have no control what happens to your message on the wire -- so long as its legal within the bounds of HTTP. That's what I'm calling out as a spec hole, but I haven't experimented with WebSockets yet -- I'll look a little closer to see if I'm just being overly paranoid.

Matthew Dempsky

unread,
Feb 5, 2010, 11:07:35 AM2/5/10
to nod...@googlegroups.com
On Fri, Feb 5, 2010 at 7:59 AM, Dean Landolt <de...@deanlandolt.com> wrote:
> There -- that -- you just did it again (conflating WebSockets and HTTP)
> The WebSockets protocol uses HTTP as a transport, but it isn't HTTP.

It doesn't use HTTP as a transport. It just uses a handshake that
looks like HTTP.

> Sure but by using HTTP as a transport you have no control what happens to
> your message on the wire -- so long as its legal within the bounds of HTTP.

Are you talking about HTTP proxy servers? If so, then if they don't
know how to speak all of the WebSocket protocol anyway, then it's
kinda moot whether or not they mangle the HTTP-lookalike handshake,
and possibly even better if they do.

Dean Landolt

unread,
Feb 5, 2010, 11:27:01 AM2/5/10
to nod...@googlegroups.com
On Fri, Feb 5, 2010 at 11:07 AM, Matthew Dempsky <mat...@dempsky.org> wrote:
On Fri, Feb 5, 2010 at 7:59 AM, Dean Landolt <de...@deanlandolt.com> wrote:
> There -- that -- you just did it again (conflating WebSockets and HTTP)
> The WebSockets protocol uses HTTP as a transport, but it isn't HTTP.

It doesn't use HTTP as a transport.  It just uses a handshake that
looks like HTTP.

Excellent -- like I said, I haven't played with it yet but that answers that.
 

> Sure but by using HTTP as a transport you have no control what happens to
> your message on the wire -- so long as its legal within the bounds of HTTP.

Are you talking about HTTP proxy servers?  If so, then if they don't
know how to speak all of the WebSocket protocol anyway, then it's
kinda moot whether or not they mangle the HTTP-lookalike handshake,
and possibly even better if they do.

Fair enough -- apologies for the noise. But I think it's fair to say websockets wouldn't be able to use the http code in node no matter what so it's irrelevant to the discussion of HTTP header casing.

Eran Hammer-Lahav

unread,
Mar 5, 2011, 7:17:08 PM3/5/11
to nod...@googlegroups.com
Changing incoming headers to lowercase makes sense. Once you know it, its faster and consistent.

However, why are outgoing headers changed to lowercase?

That's actually slower. It also makes it impossible to deal with broken servers which expects a certain case. This is not a theoretical problem. The recent change to lowercase outgoing headers broken my code when dealing with a few OAuth providers which look for the 'Authorization' header and fail to find the 'authorization' header.

This change means I can't use those services until they fix their bug which in some case is just not going to happen. Can we change outgoing headers back to whatever case they are assigned to?

EHL
Reply all
Reply to author
Forward
0 new messages