go: net/http: unexpected 1XX status codes are not handled well
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (go version
)?
go version go1.7.3 darwin/amd64
What operating system and processor architecture are you using (go env
)?
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/cory/Documents/Go"
GORACE=""
GOROOT="/usr/local/Cellar/go/1.7.3/libexec"
GOTOOLDIR="/usr/local/Cellar/go/1.7.3/libexec/pkg/tool/darwin_amd64"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/nc/4r8rg3dx3h9b4t4b0bzcv8x40000gn/T/go-build564596711=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
What did you do?
Running the following Python test server:
import socket
import time
document = b'''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>title</title>
<link rel="stylesheet" href="/other/styles.css">
<script src="/other/action.js"></script>
</head>
<body>
<h1>Hello, world!</h1>
</body>
</html>
'''
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', 8080))
s.listen(5)
while True:
new_socket, _ = s.accept()
data = b''
while not data.endswith(b'\r\n\r\n'):
data += new_socket.recv(8192)
new_socket.sendall(
b'HTTP/1.1 103 Early Hints\r\n'
b'Content-Length: 0\r\n'
b'Server: socketserver/1.0.0\r\n'
b'Link: </other/styles.css>; rel=preload; as=style\r\n'
b'Link: </other/action.js>; rel=preload; as=script\r\n'
b'\r\n'
)
time.sleep(1)
new_socket.sendall(
b'HTTP/1.1 200 OK\r\n'
b'Server: socketserver/1.0.0\r\n'
b'Content-Type: text/html\r\n'
b'Content-Length: %s\r\n'
b'Link: </other/styles.css>; rel=preload; as=style\r\n'
b'Link: </other/action.js>; rel=preload; as=script\r\n'
b'Connection: close\r\n'
b'\r\n' % len(document)
)
new_socket.sendall(document)
new_socket.close()
I ran the following Go test client against it:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"log"
)
func request(client *http.Client) {
resp, err := client.Get("http://localhost:8080/")
if err != nil {
log.Fatal(err);
}
data, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %s", resp.Status)
fmt.Printf("Body: %s", data)
}
func main() {
transport := &http.Transport{}
client := &http.Client{Transport: transport}
request(client)
request(client)
}
What did you expect to see?
I expected to see a 200 status code and the HTML body from the 200 response.
What did you see instead?
Go reported the 103 status code as final with no body, and did not provide access to the 200 response.
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Comments: 20 (12 by maintainers)
Commits related to this issue
- net/http, net/http/httptrace: make Transport support 1xx responses properly Previously the Transport had good support for 100 Continue responses, but other 1xx informational responses were returned a... — committed to golang/go by bradfitz 6 years ago
- http2: ignore unknown 1xx responses like HTTP/1 Updates golang/go#26189 (fixes after vendor into std) Updates golang/go#17739 Change-Id: I076cdbb57841b7dbbaa764d11240913bc3a8b05d Reviewed-on: https:... — committed to golang/net by bradfitz 6 years ago
- http2: fix bug in earlier CL 123615 I both forgot that this list could contain duplicates, and I had forgot to run the net/http tests against CL 123615 before mailing it, which ended up catching this... — committed to golang/net by bradfitz 6 years ago
- net/http: update bundled http2 Updates bundled x/net/http2 to git rev d0887baf81f4 for: http2: ignore unknown 1xx responses like HTTP/1 https://golang.org/cl/123615 http2: fix bug in ea... — committed to golang/go by bradfitz 6 years ago
I think this issue conflates a bigger, easier problem with a smaller, harder problem.
The bigger, easier problem is that the Go HTTP client is completely confused by 1xx responses other than a single 100 (Continue), in clear violation of the protocol, hampering interoperability with RFC 8297 among other things. It can be trivially fixed by replacing
if resp.StatusCode == 100
withfor resp.StatusCode >= 100 && resp.StatusCode <= 199
insrc/net/http/transport.go
.The smaller, harder problem is that the Go HTTP client doesn’t provide an API for 1xx responses, which precludes building a correct HTTP proxy on top of it. But I don’t think this should prevent or delay fixing the bigger, easier problem. As far as I’m aware, the Go distribution doesn’t even include an HTTP proxy (
httputil.ReverseProxy
is not a proxy in the language of RFC 723x — it’s a gateway).Would you like me to submit the above fix to
transport.go
?