Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net/http: Response.Write does not write Content-Length when 0 #5381

Closed
titanous opened this issue Apr 30, 2013 · 18 comments
Closed

net/http: Response.Write does not write Content-Length when 0 #5381

titanous opened this issue Apr 30, 2013 · 18 comments
Labels
FrozenDueToAge Suggested Issues that may be good for new contributors looking for work to do.
Milestone

Comments

@titanous
Copy link
Member

What steps will reproduce the problem?

http://play.golang.org/p/XsUnTxPk6n

What is the expected output?

"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"

What do you see instead?

"HTTP/1.1 200 OK\r\n\r\n"

Which compiler are you using (5g, 6g, 8g, gccgo)?

6g

Which operating system are you using?

OS X 10.8.2

Which version are you using?  (run 'go version')

go version devel +cdedb129e020 Tue Apr 30 14:01:05 2013 -0700 darwin/amd64
@gopherbot
Copy link

Comment 1 by PieterDroogendijk:

The given example also does not write Content-Length when it is not 0. If the Response
has no Body, it has no Content-Length (though perhaps it should.)
http://play.golang.org/p/SiN2HojWb0 does provide a body, and indeed when ContentLength
== 0 it is not present in the header.

@PieterD
Copy link
Contributor

PieterD commented May 1, 2013

Comment 2:

The given example also does not write Content-Length when it is not 0. If the Response
has no Body, it has no Content-Length (though perhaps it should.)
http://play.golang.org/p/SiN2HojWb0 does provide a body, and indeed when ContentLength
== 0 it is not present in the header.

@titanous
Copy link
Member Author

titanous commented May 1, 2013

Comment 3:

In practice most clients (including curl and Firefox) hang on responses without a
Content-Length header. According to section 4.4 of RFC 2616 no message length would be
determined unless the server closed the connection.

@bradfitz
Copy link
Contributor

Comment 4:

FWIW, the HTTP server code does the right thing.
I assume you're writing your own HTTP server?
One way to fix this is: in Response.Write, see if the ContentLength is zero and the Body
is non-nil. If zero, read a byte, checking for EOF. If it's EOF immediately, write out a
"Content-Length: 0".  If it's not EOF, don't send a Content-Length header.  And re-write
out the byte before copying the rest of resp.Body.
That part isn't contentious.
Where it gets trickier is what ResponseWrite should do if ContentLength is 0 (unset) and
there's a body: chunked encoding or not? Function of which other headers are set? Set a
"Transfer-Encoding: chunked"? But I think that's all getting outside the scope of what
this method should do.
Like I said, the http.Server code does all this correctly already, so you must be
writing your own server, which means you are probably doing lots of this sort of stuff
now, right?  If not, what's the actual problem?

Labels changed: added priority-later, suggested, removed priority-triage.

Status changed to Accepted.

@bradfitz
Copy link
Contributor

Comment 5:

Related: issue #5357

@titanous
Copy link
Member Author

Comment 6:

Yes, this bug came up while working on a server implementation. It's pretty easy to work
around[1], and sort out the chunked encoding behavior, etc.
I think it makes sense to have some basic logic in Response.Write to decide when to send
the Content-Length, but nothing complex. As long as the behavior is documented and makes
sense, then servers using it can easily implement the exact behavior they want.
I propose this behavior:
if res.ContentLength > 0 || res.ContentLength == 0 && res.Body != nil {
    // write Content-Length
}
This stays out of the way of server implementors and doesn't try to do anything magical
or unexpected. No need for the heuristic here, Response.ContentLength is already
explicitly specified as unknown (chunked) if -1.
[1]
https://github.com/fitstar/falcore/blob/a3c31f03fec11a62d3f49d016b041d1924cd997a/server.go#L297-L331

@bradfitz
Copy link
Contributor

Comment 7:

In the case res.ContentLength == 0 && res.Body != nil, you can't just "// write
Content-Length" ... what would you write?
You can't write "Content-Length: -1".
We can't slurp it all in-memory,
We _can_ read one byte to decide between "Content-Length: 0" and ????? where ????? is
either no header (weird?) and "Transfer-Encoding: chunked" (which I don't want to get
into).
So which is it?

@titanous
Copy link
Member Author

Comment 8:

> In the case res.ContentLength == 0 && res.Body != nil, you can't just "// write
Content-Length" ... what would you write?
Content-Length: 0
It should be an error to pass a body with length > 0 and Content-Length == 0.
From the docs:
    // ContentLength records the length of the associated content.  The
    // value -1 indicates that the length is unknown.  Unless Request.Method
    // is "HEAD", values >= 0 indicate that the given number of bytes may
    // be read from Body.
    ContentLength int64
The Content-Length header should never be written when -1 (in most cases this indicates
chunked encoding).

@bradfitz
Copy link
Contributor

Comment 9:

So which header(s) should Response.Write write when resp.ContentLength == -1?

@titanous
Copy link
Member Author

Comment 10:

No Content-Length.
If Response.TransferEncoding is set, the value of it. Two options for handling nil:
1) "chunked"
2) No Transfer-Encoding header.
I like #1, but the docs say otherwise:
    // Contains transfer encodings from outer-most to inner-most. Value is
    // nil, means that "identity" encoding is used.
    TransferEncoding []string

@rsc
Copy link
Contributor

rsc commented Jul 30, 2013

Comment 11:

Labels changed: added go1.2maybe.

@rsc
Copy link
Contributor

rsc commented Sep 10, 2013

Comment 12:

Labels changed: added go1.3, removed go1.2maybe.

@bradfitz
Copy link
Contributor

Comment 13:

Issue #6762 has been merged into this issue.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 14:

Labels changed: added release-go1.3.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 15:

Labels changed: removed go1.3.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 16:

Labels changed: added repo-main.

@gopherbot
Copy link

Comment 17:

CL https://golang.org/cl/85740046 mentions this issue.

@bradfitz
Copy link
Contributor

Comment 18:

This issue was closed by revision a30eaa1.

Status changed to Fixed.

@titanous titanous added fixed Suggested Issues that may be good for new contributors looking for work to do. labels Apr 11, 2014
@rsc rsc added this to the Go1.3 milestone Apr 14, 2015
@rsc rsc removed the release-go1.3 label Apr 14, 2015
@golang golang locked and limited conversation to collaborators Jun 24, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge Suggested Issues that may be good for new contributors looking for work to do.
Projects
None yet
Development

No branches or pull requests

5 participants