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: Several 100-continue #22128

Closed
2at2 opened this issue Oct 4, 2017 · 5 comments

Comments

Projects
None yet
5 participants
@2at2
Copy link

commented Oct 4, 2017

Hi!
I send request to some server and i have a trouble with several 100-continue responses.

What version of Go are you using (go version)?

go version go1.9 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/..."
GORACE=""
GOROOT="/Users/.../go1.9"
GOTOOLDIR="/Users/.../go1.9/pkg/tool/darwin_amd64"
GCCGO="gccgo"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/kz/4rf1vvhj2kb910mwtq44qzvr0000gn/T/go-build703834570=/tmp/go-build -gno-record-gcc-switches -fno-common"
CXX="clang++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"

What did you do?

libcurl example:

curl -i https://*** -H 'Authorization: Basic ***' -H 'Expect: 100-continue' --data 'some content' -v
***
> POST /secure/gateway HTTP/1.1
> Host: ***
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Basic ***
> Expect: 100-continue
> Content-Length: 12
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 100 Continue
HTTP/1.1 100 Continue

* We are completely uploaded and fine
< HTTP/1.1 100 Continue
HTTP/1.1 100 Continue

< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Wed, 04 Oct 2017 10:48:22 GMT
Date: Wed, 04 Oct 2017 10:48:22 GMT
< Content-Type: text/xml
Content-Type: text/xml
< Connection: close
Connection: close
< Transfer-Encoding: chunked
Transfer-Encoding: chunked

In example you see two HTTP/1.1 100 Continue

GO 1.9 example:

func main() {
	content := bytes.NewReader([]byte("my super body"))

	request, err := http.NewRequest("POST", "***", content)

	if err != nil {
		panic(err)
	}

	request.Header = make(http.Header)
	request.Header.Set("Authorization", "Basic ***")
	request.Header.Set("Expect", "100-continue")
	
        client := &http.Client{}
	resp, err := client.Do(request)

	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	_, err = ioutil.ReadAll(resp.Body)

	if err != nil {
		panic(err)
	}

	fmt.Println(resp.StatusCode)
	fmt.Println(resp.Status)
}

Result of this example:

100
100 Continue

What did you expect to see?

200
200 Ok

What did you see instead?

100
100 Continue

I found problem in src/net/http/transport.go:1698

What do you think about:

diff --git a/src/net/http/transport.go b/src/net/http/transport.go
index 6a89392a99..9f85f07700 100644
--- a/src/net/http/transport.go
+++ b/src/net/http/transport.go
@@ -1715,13 +1715,17 @@ func (pc *persistConn) readResponse(rc requestAndChan, trace *httptrace.ClientTr
 			close(rc.continueCh)
 		}
 	}
-	if resp.StatusCode == 100 {
-		pc.readLimit = pc.maxHeaderResponseSize() // reset the limit
-		resp, err = ReadResponse(pc.br, rc.req)
-		if err != nil {
-			return
+
+	for {
+		if resp.StatusCode == 100 {
+			pc.readLimit = pc.maxHeaderResponseSize() // reset the limit
+			resp, err = ReadResponse(pc.br, rc.req)
+			if err != nil {
+				return
+			}
 		}
 	}
+
 	resp.TLS = pc.tlsState
 	return
 }

I know endless cycle it's not good idea =)

UPD: The similar problem https://stackoverflow.com/questions/22818059/several-100-continue-received-from-the-server . Request to an IIS 7.5 server.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2017

@tombergan

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2017

Thanks for the report. As best I can tell, the server and the Go client library are both wrong. The server should not send multiple 100-continues and Go should ignore the extra 100-continues.

https://tools.ietf.org/html/rfc7231#section-6.2

A client MUST be able to parse one or more 1xx responses received
prior to a final response, even if the client does not expect one. A
user agent MAY ignore unexpected 1xx responses.

The second 100-continue response is an unexpected 1xx response that we should ignore. This is definitely a bug we should fix, however, I'm going to close this issue because it's a dup of #17739, which is the exact same problem (Go does not ignore unexpected 1xx responses).

@tombergan tombergan closed this Oct 4, 2017

@fraenkel

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2017

I took your code and created a complete example
https://play.golang.org/p/wRfdbQhViw

I get your expected output.

@tombergan

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2017

@fraenkel That doesn't repro the problem. The server is Go, which should send a single 100-continue. To repro, the server would need to send multiple 100-continue messages. Here's a repro:

https://play.golang.org/p/Czf4GKg2qt [edit: I had a typo in the prior link]

@fraenkel

This comment has been minimized.

Copy link
Contributor

commented Oct 4, 2017

@golang golang locked and limited conversation to collaborators Oct 4, 2018

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
You can’t perform that action at this time.