You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When implementing Tranfer-Encoding (transfer-coding, TE) other than chunked in an http.Server, Go produces an invalid combination of HTTP response headers — it provides both Content-Length and Transfer-Encoding. It should not be writing a Content-Length header.
If a Content-Length header field (section 14.13) is present, its decimal value in OCTETs represents both the entity-length and the transfer-length. The Content-Length header field MUST NOT be sent if these two lengths are different (i.e., if a Transfer-Encoding header field is present). If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.
I am using go version go1.4.1 linux/amd64.
To reproduce the problem, compile and run an HTTP server that implements gzip Transfer-Encoding:
package main
import"bytes"import"compress/gzip"import"log"import"net/http"import"strings"funcgz(input []byte) []byte {
varbuf bytes.Bufferw:=gzip.NewWriter(&buf)
w.Write(input)
w.Close()
returnbuf.Bytes()
}
varresponse= []byte("Hello, world!\n")
funcmain() {
http.HandleFunc("/", func(w http.ResponseWriter, req*http.Request) {
// This is not strictly correct, but close enough for demoingifstrings.Contains(strings.ToLower(req.Header.Get("TE")), "gzip") {
// Go automatically wraps this in chunked as required by specw.Header().Set("Transfer-Encoding", "gzip")
// Uncomment this to print an error, but produce a valid response//w.Header().Set("Content-Length", "-1")w.Write(gz(response))
} else {
w.Write(response)
}
})
log.Fatal(http.ListenAndServe(":8080", nil))
}
Then use curl to query the server. curl is one of the few clients that implement Transfer-Encoding, and it knows how to handle the Transfer-Encoding header in response to this request with the TE request header.
http.Server logs an error saying that the Content-Length was set, but it was actually calculated in http.Server's chunkWriter.writeHeader().
http: WriteHeader called with both Transfer-Encoding of "gzip" and a Content-Length of 38
Setting the Content-Length to an invalid value (such as -1, by uncommenting line 26 of the source), produces correct output, but also logs an error message.
http: invalid Content-Length of "-1"
What did you expect to see?
A Transfer-Encoding header (either "gzip, chunked" or two separate headers, "gzip" and "chunked") and no Content-Length header.
What did you see instead?
Both a Content-Length & Transfer-Encoding header.
The problem appears to be where the contentLength is calculated in src/net/http/server.go near line 795:
This is causing the Content-Length header to be set.
Then around line 860 the presence of the Transfer-Encoding header is determined:
te := header.get("Transfer-Encoding")
hasTE := te != ""
if hasCL && hasTE && te != "identity" {
// TODO: return an error if WriteHeader gets a return parameter
// For now just ignore the Content-Length.
w.conn.server.logf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d", te, w.contentLength)
delHeader("Content-Length")
hasCL = false
}
But I think the fact that w.contentLength is still set to something other than -1 means it is unaffected by the presence of "Content-Length" in excludeHeader (via delHeader()) and the Content-Length is included in the output headers.
A couple of options that probably fix this:
Move the calculation of the Content-Length & the definition of hasCL to occur afterhasTE is defined, and only if !hasTE. (Neither the calculated content length nor hasCL is used before the hasCL && hasTE condition.); or
Add && header.get("Transfer-Encoding") == "" to the condtion near line 795 to prevent the Content-Length header being produced.
(In my real world implementation I set the Content-Type explicitly, so I'm not concerned about the Content-Type being guessed incorrectly as it is in this example, where it should be "text/plain". Maybe it shouldn't be detected if the user set a Transfer-Encoding?)
The text was updated successfully, but these errors were encountered:
bradfitz
changed the title
http.Server sets both Content-Length & Transfer-Encoding: chunked
net/http: Server sets both Content-Length & Transfer-Encoding: chunked
Feb 24, 2015
When implementing Tranfer-Encoding (transfer-coding, TE) other than chunked in an http.Server, Go produces an invalid combination of HTTP response headers — it provides both Content-Length and Transfer-Encoding. It should not be writing a Content-Length header.
RFC 2616 Section 4.4:
I am using go version go1.4.1 linux/amd64.
To reproduce the problem, compile and run an HTTP server that implements gzip Transfer-Encoding:
Then use curl to query the server. curl is one of the few clients that implement Transfer-Encoding, and it knows how to handle the Transfer-Encoding header in response to this request with the
TE
request header.http.Server logs an error saying that the Content-Length was set, but it was actually calculated in http.Server's
chunkWriter.writeHeader()
.Setting the Content-Length to an invalid value (such as -1, by uncommenting line 26 of the source), produces correct output, but also logs an error message.
A Transfer-Encoding header (either "gzip, chunked" or two separate headers, "gzip" and "chunked") and no Content-Length header.
Both a Content-Length & Transfer-Encoding header.
The problem appears to be where the contentLength is calculated in src/net/http/server.go near line 795:
This is causing the Content-Length header to be set.
Then around line 860 the presence of the Transfer-Encoding header is determined:
But I think the fact that w.contentLength is still set to something other than -1 means it is unaffected by the presence of "Content-Length" in excludeHeader (via
delHeader()
) and the Content-Length is included in the output headers.A couple of options that probably fix this:
hasCL
to occur afterhasTE
is defined, and onlyif !hasTE
. (Neither the calculated content length norhasCL
is used before thehasCL && hasTE
condition.); or&& header.get("Transfer-Encoding") == ""
to the condtion near line 795 to prevent the Content-Length header being produced.(In my real world implementation I set the Content-Type explicitly, so I'm not concerned about the Content-Type being guessed incorrectly as it is in this example, where it should be "text/plain". Maybe it shouldn't be detected if the user set a Transfer-Encoding?)
The text was updated successfully, but these errors were encountered: