Possible dissonance between response.sendHeader() and HTTP spec

58 views
Skip to first unread message

Avi Flax

unread,
Nov 30, 2009, 1:27:07 AM11/30/09
to nodejs
My apologies if this has come up before, I searched but didn't find
it.

I find the name of this method a little disconcerting. Maybe it's
because I generally work with HTTP at a higher level than node is
intended to operate, but I think of a HTTP response as having multiple
headers, not just one. And in fact, as I read it, that's what RFC 2616
says [1]. Also, the spec does not refer to the Status Code as being
part of a larger "header" section; rather it's part of "Status-Line"
which is part of "start-line".

I would think that we'd be better off with two methods:

* sendStatus(code, text) — would have the same rules as the current
setHeader() — must be called before sendBody() and finish()

* sendHeaders(headers) — could be called 0-N times, but only after
sendStatus() and before sendBody() and finish()

I hope this makes sense. I've hardly fired up node yet; I'm just
excited about it, and want to (a) understand it better; (b) help
improve it.

Thanks,
Avi

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

P.S. Ryan, node is very impressive!! Thanks for contributing this to
the world!!

--
Avi Flax » Partner » Arc90 » http://arc90.com
➙ Have you tried Kindling‽ http://kindlingapp.com

Ryan Dahl

unread,
Nov 30, 2009, 3:49:08 AM11/30/09
to nodejs
On Mon, Nov 30, 2009 at 7:27 AM, Avi Flax <a...@arc90.com> wrote:
> My apologies if this has come up before, I searched but didn't find
> it.
>
> I find the name of this method a little disconcerting. Maybe it's
> because I generally work with HTTP at a higher level than node is
> intended to operate, but I think of a HTTP response as having multiple
> headers, not just one. And in fact, as I read it, that's what RFC 2616
> says [1]. Also, the spec does not refer to the Status Code as being
> part of a larger "header" section; rather it's part of "Status-Line"
> which is part of "start-line".
>
> I would think that we'd be better off with two methods:
>
> * sendStatus(code, text) — would have the same rules as the current
> setHeader() — must be called before sendBody() and finish()
>
> * sendHeaders(headers) — could be called 0-N times, but only after
> sendStatus() and before sendBody() and finish()
>
> I hope this makes sense. I've hardly fired up node yet; I'm just
> excited about it, and want to (a) understand it better; (b) help
> improve it.

This is a good suggestion. At the moment you can send multiple header
lines (and order them) like this

res.sendHeader(200, [["Set-Cookie", x], ["Set-Cookie", y]])

But that clashes with the normal API. I'd like the current API to work
as it does, that is

res.sendHeader(200, {"Content-Type": "text/plain" });

should work, but having and additional

res.sendExtraHeader("Set-Cookie", y)

would be okay. This would also allow for trailing headers at the end
of chunked messages.

Any objections / suggestions / corrections before I do it?

Jed Schmidt

unread,
Nov 30, 2009, 4:02:15 AM11/30/09
to nod...@googlegroups.com
Ryan,

How about just accepting arrays as the values of the header object?

res.sendStatus(200);
res.sendHeader({
"Content-Type": "text/html",
"Set-Cookie": [ "key1=val1", "key2=val2" ]
});
res.sendBody("<html><body>Multiple cookies set</body></html>");
res.finish();

Jed Schmidt
> --
>
> You received this message because you are subscribed to the Google Groups "nodejs" group.
> To post to this group, send email to nod...@googlegroups.com.
> To unsubscribe from this group, send email to nodejs+un...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/nodejs?hl=en.
>
>
>

Bluebie

unread,
Nov 30, 2009, 5:26:40 AM11/30/09
to nodejs
I'm a fan of the array technique, as this seems to be an edge case
which so far as i'm aware, only really affects Set-Cookie. Nice and
simple. :)

Joost

unread,
Nov 30, 2009, 7:42:51 AM11/30/09
to nodejs
My vote. Readable and concise.

Dean Landolt

unread,
Nov 30, 2009, 8:32:14 AM11/30/09
to nod...@googlegroups.com
On Mon, Nov 30, 2009 at 7:42 AM, Joost <schut...@gmail.com> wrote:
My vote. Readable and concise.

It's what JSGI does. It has the added benefit that when you're working with header values as array and you accidentally += ", somevalue" it does the Right Thing according to the HTTP spec and joins on comma. (Other than for cookies, perhaps the most common case, which totally disregard the spec and don't use comma as the split char).
 

Joe Developer

unread,
Nov 30, 2009, 8:43:47 AM11/30/09
to nod...@googlegroups.com
How about supporting something like:
var cookies = { "key1":"value1", "key2":"value2"};
res.sendHeader({
   "Content-Type": "text/html",
   "Set-Cookie": cookies});

and in the simple case: 
res.sendHeader({
   "Content-Type": "text/html",
   "Set-Cookie": { "key1":"value1"} });

wouldn't that reflect usage better than 'userland' string concatenation with = ?

Jed Schmidt

unread,
Nov 30, 2009, 10:51:46 AM11/30/09
to nod...@googlegroups.com
Joe,

Unfortunately, you'd still need concatenation to set any cookie attributes, such as the domain or path.
Since it's not too much of a big deal, I don't mind handling this in userland.

Jed Schmidt


Avi Flax

unread,
Nov 30, 2009, 3:27:57 PM11/30/09
to nodejs
On Nov 30, 3:49 am, Ryan Dahl <coldredle...@gmail.com> wrote:

> This is a good suggestion.

Thanks!

> I'd like the current API to work
> as it does, that is
>
>   res.sendHeader(200, {"Content-Type": "text/plain" });
>
> should work, but having and additional
>
>   res.sendExtraHeader("Set-Cookie", y)
>
> would be okay. This would also allow for trailing headers at the end
> of chunked messages.
>
> Any objections / suggestions / corrections before I do it?

I'm a little confused… my focus in my suggestion was separating the
sending of the Status line from the sending of HTTP headers. I'm not
sure what that has to do with sending "extra" headers.

So, to be clear: what do you think of splitting sendHeader() into
sendStatus() and sendHeaders()?

As far as the "extra" use cases, I'm also not sure I understand them.
What's an "extra" header? Are you talking about sending the same
header multiple times? If so, why would that need special support? Why
can't we just do it?

Also, I may be completely wrong, but as I understand HTTP, it doesn't
seem possible to send trailing headers at the end of chunked messages.
But I don't know much about chunking… so I'll go read up on that.

Also, I'm not sure, but I think the order of HTTP headers may be
unimportant.

Cheers!

Avi

Joe Developer

unread,
Nov 30, 2009, 4:04:58 PM11/30/09
to nod...@googlegroups.com
Hi Jed, 

Thanks for pointing that out.
I suspect that consensus will be that such sugar is best left out of 'core' node.js 

Ryan Dahl

unread,
Nov 30, 2009, 11:19:53 PM11/30/09
to nodejs
On Mon, Nov 30, 2009 at 9:27 PM, Avi Flax <a...@arc90.com> wrote:
> I'm a little confused… my focus in my suggestion was separating the
> sending of the Status line from the sending of HTTP headers. I'm not
> sure what that has to do with sending "extra" headers.
>
> So, to be clear: what do you think of splitting sendHeader() into
> sendStatus() and sendHeaders()?

Function calls are overhead (although, probably, trivially small
overhead). There is no sense in streaming response headers to the
client, so I don't see the need for calling a function for each header
pair - although it is one way to introduce both ordering and multiple
headers with the same field... Yeah. Maybe it's not a bad idea, the
way you suggested it.

Well, one thing I'm sure of, I don't think people should need (or even
be allowed) to specify the "human readable" version of the status
code.

> Also, I may be completely wrong, but as I understand HTTP, it doesn't
> seem possible to send trailing headers at the end of chunked messages.
> But I don't know much about chunking… so I'll go read up on that.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.40

> Also, I'm not sure, but I think the order of HTTP headers may be
> unimportant.

Not technically true, but practically, yes.

Avi Flax

unread,
Dec 1, 2009, 12:06:55 AM12/1/09
to nod...@googlegroups.com
On Mon, Nov 30, 2009 at 23:19, Ryan Dahl <coldre...@gmail.com> wrote:

> Function calls are overhead (although, probably, trivially small
> overhead). There is no sense in streaming response headers to the
> client, so I don't see the need for calling a function for each header
> pair - although it is one way to introduce both ordering and multiple
> headers with the same field... Yeah. Maybe it's not a bad idea, the
> way you suggested it.

Thanks! But actually, I wasn't thinking that users would call a
function for each header pair. I just want the semantics of the API to
match the semantics of HTTP. And in HTTP, as I understand it, there's
no such thing as a Response "header" which includes both the
status-line and the header-lines. Rather, the term "header"
specifically excludes the status-line, and a Response can include
multiple Headers.

What I had in mind is something like:

response.sendStatus(200);
response.sendHeaders({"Content-Type": "text/plain", "Content-Length": "42"});

If you want to allow sendHeaders() to be called only once, to
discourage the overhead of multiple function calls, that seems fine to
me.

If you really want to stick with a single method, to absolutely
minimize the multiple-function-calls overhead, I'd be cool with that
too; I'd just like to see the name be adjusted. I'd suggest something
like "sendPrologue()" or even "sendStatusAndHeaders()".

> Well, one thing I'm sure of, I don't think people should need (or even
> be allowed) to specify the "human readable" version of the status
> code.

I disagree with this. RFC-2616 says:

"The Reason-Phrase is intended to give a short textual description of
the Status-Code. The Status-Code is intended for use by automata and
the Reason-Phrase is intended for the human user. The client is not
required to examine or display the Reason-Phrase."

http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1.1

So a server can send any value at all for Reason-Phrase, as long as
it's intended to make sense to human users. I regularly use HTTP
libraries which send unusual values for Reason-Phrase.

I do think that good defaults, conventions, and common practices are
important. The default should be to use the common values for
Reason-Phrase.

Therefore, I'd suggest that response.sendStatus() have an optional
second parameter for Reason-Phrase. When not specified, the client
would use the common Reason-Phrase for the specified Status Code.

For example:

response.sendStatus(404); // sends 404 Not Found

response.sendStatus(404, "No Dice!") // sends 404 No Dice!

I'm not a JS expert so I'm a little fuzzy on whether or how well this
is supported by JS… sorry if I'm missing something stupid/obvious.

>> Also, I'm not sure, but I think the order of HTTP headers may be
>> unimportant.
>
> Not technically true, but practically, yes.

Hmm, well, RFC-2616 says:

'The order in which header fields with differing field names are
received is not significant. However, it is "good practice" to send
general-header fields first, followed by request-header or response-
header fields, and ending with the entity-header fields.'

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

So isn't it technically true to say that the order of headers is unimportant?

Thanks,
Avi

Benjamin Thomas

unread,
Dec 1, 2009, 11:42:21 AM12/1/09
to nod...@googlegroups.com
On Mon, Nov 30, 2009 at 10:06 PM, Avi Flax <a...@arc90.com> wrote:
> If you really want to stick with a single method, to absolutely
> minimize the multiple-function-calls overhead, I'd be cool with that
> too; I'd just like to see the name be adjusted. I'd suggest something
> like "sendPrologue()" or even "sendStatusAndHeaders()".

I think the real question comes down to if you are ever going to want
to send the status and the
headers separately. And since they must come next to each other in
the document, this doesn't seem very likely (or did we decide this
wasn't the case?).

So, I am in favor of renaming sendHeader to sendPrologue (or the more
wordy sendStatusAndHeaders)

But

I think it might be useful to allow the first argument of sendHeader
(or sendPrologue, etc) to take either a number or a string. If it is
a number, node would treat it like it does now, and if it is a string,
send the string as the first line of the request.

Avi Flax

unread,
Dec 1, 2009, 11:59:04 AM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 11:42, Benjamin Thomas <bam.t...@gmail.com> wrote:

> I think the real question comes down to if you are ever going to want
> to send the status and the
> headers separately.  And since they must come next to each other in
> the document, this doesn't seem very likely (or did we decide this
> wasn't the case?).

While the status-line and headers are indeed adjacent in the response,
they're very different things. That's kinda my whole point here. So I
don't think that's the real question. As I see it, this discussion
isn't about sending the status and headers together or separately, but
about semantics — what *are* the status and headers, and should they
be grouped together. And if they are sent together with a single
function, it shouldn't be named "sendHeader", because it's a confusing
name.

> So, I am in favor of renaming sendHeader to sendPrologue (or the more
> wordy sendStatusAndHeaders)

Yup, that'd make sense to me. As I wrote earlier, I think either way
would be totally fine.

>
> But
>
> I think it might be useful to allow the first argument of sendHeader
> (or sendPrologue, etc) to take either a number or a string.  If it is
> a number, node would treat it like it does now, and if it is a string,
> send the string as the first line of the request.

I'm guessing you meant "…as the first line of the response". And by
"first line" I assume you mean the status-line.

I don't know about that… I think a user should be required to supply a
status code as an integer. It's possible to assume/derive a
Reason-Phrase (AKA Status Text) from a Status-Code, but not the other
way around.

I do agree that it should be possible to supply a value for
Reason-Phrase. That's another reason I think it would be good to split
sendHeader into sendStatus and sendHeaders; that way sendStatus could
have an optional argument for Reason-Phrase.

Avi

Benjamin Thomas

unread,
Dec 1, 2009, 12:27:50 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 9:59 AM, Avi Flax <a...@arc90.com> wrote:
> I'm guessing you meant "…as the first line of the response". And by
> "first line" I assume you mean the status-line.

I did! Thanks!

> I don't know about that… I think a user should be required to supply a
> status code as an integer. It's possible to assume/derive a
> Reason-Phrase (AKA Status Text) from a Status-Code, but not the other
> way around.

I more meant that if you are supplying a string for the status line,
that you would supply the whole thing:

so you could do this:

resp.sendHeader(200, {...});

Which would result in a status line that looks like: HTTP/1.1 200 OK

or you could do this:

resp.sendHeader("200 OKAY", {...});

Which would result in a status line that looks like: HTTP/1.1 200 OKAY

This gives you full control over the Status, but also keeps the Node
API simple and in one method.

Dean Landolt

unread,
Dec 1, 2009, 12:39:33 PM12/1/09
to nod...@googlegroups.com


That's pretty elegant. And I would agree that for something like node the ability to send a reason phrase may be useful (if not technically important). With JSGI we don't allow this as part of the interface, but nothing stops JSGI servers from exposing some kind of hook for this functionality to those people that really care. It's just like header casing -- technically irrelevant but may matter to some naive code in the wild.

Christopher Lenz

unread,
Dec 1, 2009, 12:42:24 PM12/1/09
to nod...@googlegroups.com
On 01.12.2009, at 17:59, Avi Flax wrote:
> On Tue, Dec 1, 2009 at 11:42, Benjamin Thomas <bam.t...@gmail.com> wrote:
>> I think the real question comes down to if you are ever going to want
>> to send the status and the
>> headers separately. And since they must come next to each other in
>> the document, this doesn't seem very likely (or did we decide this
>> wasn't the case?).
>
> While the status-line and headers are indeed adjacent in the response,
> they're very different things. That's kinda my whole point here. So I
> don't think that's the real question. As I see it, this discussion
> isn't about sending the status and headers together or separately, but
> about semantics — what *are* the status and headers, and should they
> be grouped together. And if they are sent together with a single
> function, it shouldn't be named "sendHeader", because it's a confusing
> name.

FWIW, status has historically often been treated as a special kind of header, for example in CGI where you'd print "Status: 200" to set the response status. :)

I agree it's confusing, but what confused me was the singular form.

>> So, I am in favor of renaming sendHeader to sendPrologue (or the more
>> wordy sendStatusAndHeaders)
>
> Yup, that'd make sense to me. As I wrote earlier, I think either way
> would be totally fine.

I would vote against splitting the method. I don't like the name sendPrologue() as it is not familiar terminology in the context of HTTP afaik. And sendStatusAndHeaders() is way too verbose for my taste.

"sendHeaders(status, [reason], headers)" (note the plural and the optional reason parameter) would be fine with me, personally.

WSGI in the Python world uses the name start_response(status, headers) for this same thing. Maybe "response.begin(status, [reason], headers)" would work for node.js? Would be nicely symmetrical to response.finish([trailers?]).

Cheers,
--
Christopher Lenz
cml...@gmail.com
http://www.cmlenz.net/

Avi Flax

unread,
Dec 1, 2009, 12:54:51 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 12:27, Benjamin Thomas <bam.t...@gmail.com> wrote:

> I more meant that if you are supplying a string for the status line,
> that you would supply the whole thing:



> or you could do this:
>
> resp.sendHeader("200 OKAY", {...});
>
> Which would result in a status line that looks like:  HTTP/1.1 200 OKAY
>
> This gives you full control over the Status, but also keeps the Node
> API simple and in one method.

That's clever, and would achieve the desired flexibility, but it's
also not very strict… could lead to someone inadvertently sending a
malformed response. And as Ryan said as jsconf.eu: "The way that you
should be doing things should be easy, and the way you shouldn't be
doing things should be difficult.".

So I don't think the API should support supplying the entire
status-line as a single string.

Avi

Avi Flax

unread,
Dec 1, 2009, 1:01:12 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 12:42, Christopher Lenz <cml...@gmail.com> wrote:

> FWIW, status has historically often been treated as a special kind of header, for example in CGI where you'd print "Status: 200" to set the response status. :)

That's interesting, but I don't think it's nearly as significant as
the HTTP spec.

> I would vote against splitting the method. I don't like the name sendPrologue() as it is not familiar terminology in the context of HTTP afaik. And sendStatusAndHeaders() is way too verbose for my taste.
>
> "sendHeaders(status, [reason], headers)" (note the plural and the optional reason parameter) would be fine with me, personally.

I think that would still be at odds with the semantics of HTTP. The
status-line, status-code, and reason-phrase are not headers, or a
header, or part of a header or headers, or related to headers, in any
way!

> WSGI in the Python world uses the name start_response(status, headers) for this same thing. Maybe "response.begin(status, [reason], headers)" would work for node.js? Would be nicely symmetrical to response.finish([trailers?]).

*Great* idea. I like "begin" very much. It's succinct, clear,
consistent with "finish", and is semantically compatible with HTTP.

BTW I think response.finish() accepts a callback, not "trailers". Not
sure what "trailers" would be anyway.

Thanks,
Avi

Christopher Lenz

unread,
Dec 1, 2009, 1:12:18 PM12/1/09
to nod...@googlegroups.com
It doesn't take a callback, the sig is simply finish() without params. Trailers are like headers sent after the response body [1], but you can only do that when using chunked encoding, and when the client declares that it supports trailers via the TE header [2]. I only included that as Ryan mentioned trailers in his first message to this thread.

[1] http://tools.ietf.org/html/rfc2616#section-14.40
[2] http://tools.ietf.org/html/rfc2616#section-14.39

Avi Flax

unread,
Dec 1, 2009, 2:14:01 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 13:12, Christopher Lenz <cml...@gmail.com> wrote:

> It doesn't take a callback, the sig is simply finish() without params.

Right, got it, sorry. I really should check the docs before opening my
big… fingers…

> Trailers are like headers sent after the response body [1], but you can only do that when using chunked encoding, and when the client declares that it supports trailers via the TE header [2]. I only included that as Ryan mentioned trailers in his first message to this thread.

Interesting, thanks for the info!

Can you mention some uses for trailers? In what cases are they useful?

Thanks,
Avi

Micheil Smith

unread,
Dec 1, 2009, 5:05:00 PM12/1/09
to nod...@googlegroups.com
I can think of one very good case where you may wish to send headers by
themselves: When doing session management, so you can keep the
Set-Cookie code out of the main bulk of code, and don't need to remember
that you need to put that header in. Another case could be toggling
debug data like what FirePHP does.

I'm moreso in favour of having:

resp.sendStatus(...);
resp.setHeader(...);
resp.send(...);

- Micheil.

Avi Flax

unread,
Dec 1, 2009, 5:26:29 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 17:05, Micheil Smith <mic...@brandedcode.com> wrote:
 
I can think of one very good case where you may wish to send headers by
themselves: When doing session management, so you can keep the
Set-Cookie code out of the main bulk of code, and don't need to remember
that you need to put that header in. Another case could be toggling
debug data like what FirePHP does.

I'm moreso in favour of having:

resp.sendStatus(...);
resp.setHeader(...);
resp.send(...);

This could be accomplished with a single call to a response.begin(statusCode, headers), like so:

var headers = {};

headers["Content-Type"] = "text/html";

if (something) {
    headers["Set-Cookie"] = "chocolate chip";
}

response.begin(200, headers);

Richard Boulton

unread,
Dec 1, 2009, 6:04:09 PM12/1/09
to nod...@googlegroups.com
2009/12/1 Avi Flax <a...@arc90.com>:
>> resp.sendStatus(...);
>> resp.setHeader(...);
>> resp.send(...);
>
> This could be accomplished with a single call to a
> response.begin(statusCode, headers), like so:
> var headers = {};
> headers["Content-Type"] = "text/html";
> if (something) {
>     headers["Set-Cookie"] = "chocolate chip";
> }
> response.begin(200, headers);

Could be, but oh-so-ugly compared to having the calls separate to
start with. Especially if the Set-Cookie bit was in a separate
function, or as part of a middleware framework: you'd have to make the
headers object and pass it around together with the response object.
Eww.

--
Richard

Micheil Smith

unread,
Dec 1, 2009, 10:21:07 PM12/1/09
to nod...@googlegroups.com
Well the session manager I'm working on is a piece of middleware in many
respects, so you can see where I'm coming from. It's far easier to just
pass about the resp object and set it's properties directly.

Avi Flax

unread,
Dec 1, 2009, 10:29:55 PM12/1/09
to nod...@googlegroups.com
On Tue, Dec 1, 2009 at 18:04, Richard Boulton <ric...@tartarus.org> wrote:

> Could be, but oh-so-ugly compared to having the calls separate to
> start with.  Especially if the Set-Cookie bit was in a separate
> function, or as part of a middleware framework: you'd have to make the
> headers object and pass it around together with the response object.
> Eww.

OK, well, no matter what *something* would need to be passed around,
either the headers object or the response object — but yeah, it'd be a
pain to have to pass both around.

I think it'd be totally fine to allow sendHeaders() to be called 0–n
times before sendBody() is called.

Avi

Ryan Dahl

unread,
Dec 2, 2009, 12:07:26 AM12/2/09
to nodejs
On Tue, Dec 1, 2009 at 6:42 PM, Christopher Lenz <cml...@gmail.com> wrote:
> I would vote against splitting the method. I don't like the name sendPrologue()
> as it is not familiar terminology in the context of HTTP afaik. And
> sendStatusAndHeaders() is way too verbose for my taste.

Me too.

> "sendHeaders(status, [reason], headers)" (note the plural and the optional
> reason parameter) would be fine with me, personally.
>
> WSGI in the Python world uses the name start_response(status, headers)
> for this same thing. Maybe "response.begin(status, [reason], headers)" would
> work for node.js? Would be nicely symmetrical to response.finish([trailers?]).

I like the begin() suggestion - sounds cute.



I don't care about reason phrases. If someone wants to patch it to
allow for some optional parameter to specify a reason phrase, I will
accept it. Let's address the real concerns here:



On Wed, Dec 2, 2009 at 4:21 AM, Micheil Smith <mic...@brandedcode.com> wrote:
> Well the session manager I'm working on is a piece of middleware in many
> respects, so you can see where I'm coming from. It's far easier to just
> pass about the resp object and set it's properties directly.

What does your API look like? How do you imagine this working? Do I
just pass the response object to the Session module, and it adds it's
set-cookie headers? I can see the need for this.

Christopher Lenz

unread,
Dec 2, 2009, 4:31:14 AM12/2/09
to nod...@googlegroups.com
On 02.12.2009, at 04:21, Micheil Smith wrote:
> Well the session manager I'm working on is a piece of middleware in many
> respects, so you can see where I'm coming from. It's far easier to just
> pass about the resp object and set it's properties directly.

But you're not setting properties, you're sending bytes over the wire. While allowing multiple sendHeader/s() invocations may kind of work for a piece of middleware that just wants to add more cookies, the API is insufficient for most other pieces of middleware. Those need to be able to send a completely different response, i.e. change the status code, remove/update headers, or even transform the body.

Such tasks are better addressed either by a higher level API (think "web framework"), which would buffer the response status and headers, and optionally even chunks of the response body. Or you'd use a pattern like this:

function GZipCompressor(response) {
this.response = response;
}
GZipCompressor.prototype.begin(status, headers) { ... }
GZipCompressor.prototype.sendBody(data, encoding) { ... }
GZipCompressor.prototype.finish() { ... }

http.createServer(function(request, response) {
response = new GZipCompressor(response);
response.begin(200, {"Content-Type": "text/plain"});
response.sendBody("Hello");
response.finish();
}

I.e. the middleware component simply wraps the request or response object, offering the same API to the outside. You can just comment out the line that creates the GZipCompressor to turn off the compression. This pattern could be made more convenient using some API sugar, for example:

var middleware = require("middleware");

http.createServer(function(request, response) {
response = middleware.wrapResponse(response, {
begin: function(status, headers) {
this.response.begin(status, process.mixin({}, headers, {
"Set-Cookie": "foo=bar;path=/"
});
}
});
response.begin(200, {"Content-Type": "text/plain"});
response.sendBody("Hello");
response.finish();
}

To summarize, I don't think it'd be wise to complicate the ServerResponse API for a rather limited use case. If we want to consider middleware, let's consider more types of desirable middleware components and their requirements. Also, we need to keep in mind that ServerResponse is a low-level API that's pretty close the wire. Higher-level concerns should be layered on top of this API, not mixed into it.

Benjamin Thomas

unread,
Dec 4, 2009, 10:20:37 AM12/4/09
to nod...@googlegroups.com
I wonder if maybe the Node header handling should be purposely kept
low level, and a library for managing headers in a more versatile way
should be developed.

I think the question that should be asked is whether or not the
current way Node handles headers is capable of expressing every
conforming request. If it is then it seems to me like it is doing its
job.

Then people can add whatever functionality they need for manipulating
headers. And Ryan gets his simplicity:

> Function calls are overhead (although, probably, trivially small
overhead). There is no sense in streaming response headers to the
client, so I don't see the need for calling a function for each header
pair.

But if it doesn't cover every possible scenario then some changes need
to be done. And I'm not familiar enough with the HTTP spec to have any
sort of opinion on that.

Edwin Martin

unread,
Dec 4, 2009, 7:17:57 PM12/4/09
to nod...@googlegroups.com
Benjamin Thomas schreef:

> I wonder if maybe the Node header handling should be purposely kept
> low level, and a library for managing headers in a more versatile way
> should be developed.

I agree. Cookies and Basic authentication are better left out of the
core and into a seperate library.

Probably even things like etags, 304 not modified etc. should be left to
a seperate lib. This means http.createServer of node.js is probably not
a conforming http 1.1 server.

Maybe the build-in server should just be a http 1.0 server. That's much
easier to build. And add http 1.1-support via a seperate, "real"
webserver library.

I just looked, and node.js outputs http/1.x, so it hasn't made up its
mind yet :-)

http/1.x is not valid though, so a decision has to be made.

See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1

> I think the question that should be asked is whether or not the
> current way Node handles headers is capable of expressing every
> conforming request. If it is then it seems to me like it is doing its
> job.

You might look at how Java has implemented this. Most Java API's are
well thought out.

http://java.sun.com/products/servlet/2.2/javadoc/javax/servlet/http/HttpServletResponse.html

Edwin Martin


Ryan Dahl

unread,
Dec 5, 2009, 2:53:10 AM12/5/09
to nod...@googlegroups.com
On Sat, Dec 5, 2009 at 1:17 AM, Edwin Martin <edwinj...@gmail.com> wrote:
>
> Maybe the build-in server should just be a http 1.0 server. That's much
> easier to build. And add http 1.1-support via a seperate, "real"
> webserver library.

No, the server is and will be 1.1. Ideally the client will be too, but
I haven't done it yet.

> I just looked, and node.js outputs http/1.x, so it hasn't made up its
> mind yet :-)

?

Edwin Martin

unread,
Dec 5, 2009, 7:38:53 AM12/5/09
to nod...@googlegroups.com
Ryan,

> No, the server is and will be 1.1. Ideally the client will be too, but
> I haven't done it yet.

Okay.

>> I just looked, and node.js outputs http/1.x, so it hasn't made up its
>> mind yet :-)
>
> ?

Hmm, other sites also return "http/1.x" :-|

I guess the Firefox addon I use just can't determine the version and
shows "1.x". Confusing.

Edwin Martin

Avi Flax

unread,
Dec 6, 2009, 6:42:35 PM12/6/09
to nod...@googlegroups.com
On Wed, Dec 2, 2009 at 00:07, Ryan Dahl <coldre...@gmail.com> wrote:

> I like the begin() suggestion - sounds cute.

Ryan, do you think you might get around to making this change? If not,
would you accept a patch from me? (I have very little C experience but
I'd get some of my co-workers to look it over before submitting it.)

> I don't care about reason phrases. If someone wants to patch it to
> allow for some optional parameter to specify a reason phrase, I will
> accept it.

I might do this. Maybe it's a little anal, but I like reason phrases.

Avi

Ryan Dahl

unread,
Dec 7, 2009, 3:58:14 AM12/7/09
to nod...@googlegroups.com
On Mon, Dec 7, 2009 at 12:42 AM, Avi Flax <a...@arc90.com> wrote:
> On Wed, Dec 2, 2009 at 00:07, Ryan Dahl <coldre...@gmail.com> wrote:
>
>> I like the begin() suggestion - sounds cute.
>
> Ryan, do you think you might get around to making this change? If not,
> would you accept a patch from me? (I have very little C experience but
> I'd get some of my co-workers to look it over before submitting it.)

I don't know - I kind of like the setHeaders() name...

>> I don't care about reason phrases. If someone wants to patch it to
>> allow for some optional parameter to specify a reason phrase, I will
>> accept it.
>
> I might do this. Maybe it's a little anal, but I like reason phrases.

Sure, a patch for this I would accept.

Avi Flax

unread,
Dec 7, 2009, 12:10:59 PM12/7/09
to nod...@googlegroups.com
On Mon, Dec 7, 2009 at 03:58, Ryan Dahl <coldre...@gmail.com>

> I don't know - I kind of like the setHeaders() name…

Oh, really? Cool, I like it too — as long as it's paired with
setStatus(). My whole point with this thread has been that status !=
header(s).

Also, what's up with "set" versus "send"? I kinda like "send", it
seems in line with Node's low-level I/O oriented role.

So I've love to see sendHeader() split into sendStatus() and
sendHeaders() — or setStatus() and setHeaders().

What do you wanna do? And would you like me to try to put together a patch?

>>> I don't care about reason phrases. If someone wants to patch it to
>>> allow for some optional parameter to specify a reason phrase, I will
>>> accept it.
>>
>> I might do this. Maybe it's a little anal, but I like reason phrases.
>
> Sure, a patch for this I would accept.

Cool, I'll see if I can do this. It'll have to wait until the above
questions are resolved.

Ryan Dahl

unread,
Dec 7, 2009, 12:21:27 PM12/7/09
to nod...@googlegroups.com
On Mon, Dec 7, 2009 at 6:10 PM, Avi Flax <a...@arc90.com> wrote:
> On Mon, Dec 7, 2009 at 03:58, Ryan Dahl <coldre...@gmail.com>
>
>> I don't know - I kind of like the setHeaders() name…
>
> Oh, really? Cool, I like it too — as long as it's paired with
> setStatus(). My whole point with this thread has been that status !=
> header(s).
>
> Also, what's up with "set" versus "send"? I kinda like "send", it
> seems in line with Node's low-level I/O oriented role.

Err, typo. It's "sendHeader" which I think is a fine name. You send
the header of the http message - the first line of which includes the
status code... The nomenclature is: The "http message" usually has two
parts, a "header" and a "body". The "header" consists of a "request
line" or a "status line" and several "header lines".

Avi Flax

unread,
Dec 27, 2009, 9:27:10 PM12/27/09
to nod...@googlegroups.com
On Mon, Dec 7, 2009 at 12:21, Ryan Dahl <coldre...@gmail.com> wrote:

> Err, typo. It's "sendHeader" which I think is a fine name. You send
> the header of the http message - the first line of which includes the
> status code... The nomenclature is: The "http message" usually has two
> parts, a "header" and a "body". The "header" consists of a "request
> line" or a "status line" and several "header lines".

Hi Ryan, sorry to take so long to respond; been swamped at work. I
hope it's not too late to resurrect this thread!

So, the point I've been trying to make in this thread is that in a
HTTP library, using the terminology "header" in a different way from
how it's used in HTTP is confusing and causes dissonance.

HTTP has something called a "header", and it's not the thing you
describe above. In fact, HTTP doesn't have the thing you describe
above at all. You said the HTTP message "usually has two parts", but
actually a HTTP message is comprised of 3 possible parts: the
start-line, the headers, and the body. [1]

Anyway, I don't want any of us to spend any more time debating how to
read the HTTP spec. I personally believe that Node's HTTP library
would be better if ServerResponse.sendHeader() had a different name;
for now let's call it begin(). So I've made the changes in my fork:
http://github.com/aviflax/node and I'd be happy if you'd consider
accepting them, or some version of them. And of course if you choose a
different name that'd be great too, as long as it didn't include the
word "header".

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

Thanks,
Avi

Tautologistics

unread,
Dec 27, 2009, 10:58:34 PM12/27/09
to nodejs
Even after all this time writing sendHeader() still grinds on me every
time =)

The HTTP spec provides a little guidance here. It specifies a method
for requesting only the status line + headers be sent: HEAD. It's what
I've always called status + headers and what I've always heard it
referred to as. My vote is for sendHead().

Avi Flax

unread,
Dec 27, 2009, 11:19:22 PM12/27/09
to nod...@googlegroups.com
On Sun, Dec 27, 2009 at 22:58, Tautologistics <cpt.o...@gmail.com> wrote:

> The HTTP spec provides a little guidance here. It specifies a method
> for requesting only the status line + headers be sent: HEAD. It's what
> I've always called status + headers and what I've always heard it
> referred to as. My vote is for sendHead().

I could live with that!

Although Christopher Lenz's suggestion of begin() as nicely
symmetrical to finish() is pretty nice. OTOH, I do see how using
"send" is clearer and more concrete about what's actually going to
happen.

Ryan Dahl

unread,
Dec 28, 2009, 1:12:37 AM12/28/09
to nod...@googlegroups.com
On Sun, Dec 27, 2009 at 6:27 PM, Avi Flax <a...@arc90.com> wrote:
> So, the point I've been trying to make in this thread is that in a
> HTTP library, using the terminology "header" in a different way from
> how it's used in HTTP is confusing and causes dissonance.
>
> HTTP has something called a "header", and it's not the thing you
> describe above. In fact, HTTP doesn't have the thing you describe
> above at all. You said the HTTP message "usually has two parts", but
> actually a HTTP message is comprised of 3 possible parts: the
> start-line, the headers, and the body. [1]

Meh. I think the term "header" is an appropriate name for the data
before the message body. However, I don't want to grind at the
sensibilities of users, so I will rename the method to something less
controversial, soon.

Elijah Insua

unread,
Dec 28, 2009, 11:39:45 AM12/28/09
to nod...@googlegroups.com
I vote for baking it into begin/start

-- Elijah

Avi Flax

unread,
Dec 28, 2009, 12:33:24 PM12/28/09
to nod...@googlegroups.com
On Mon, Dec 28, 2009 at 01:12, Ryan Dahl <coldre...@gmail.com> wrote:
 
I will rename the method to something less
controversial, soon.

Great!

Even if you don't use "begin", I hope you'll check out my fork, because I also added the ability to specify the Reason Phrase, and made the method a little more flexible in general. Check it out:


Basically, it can be called 4 different ways:

1) Status code only:
response.begin(204);

2) Status code and headers:
response.begin(201, headers);

3) Status code and Reason phrase:
response.begin(409, "What the hell?");

4) Status code, Reason phrase, headers:
response.begin(403, "Go away", headers);

And I tried to optimize it for case 2, which will probably be the most common.

Chris Winberry

unread,
Dec 28, 2009, 1:35:08 PM12/28/09
to nod...@googlegroups.com
Customizable status messages would be most excellent. In NodeMachine there are many reasons for a 500 status code and having access to the status message would help clarify the error both in development of an app and in production for RESTful consumers.

Reply all
Reply to author
Forward
0 new messages