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

http3.RoundTripper unable to process 1xx response #4403

Closed
mchtech opened this issue Apr 2, 2024 · 2 comments · Fixed by #4437
Closed

http3.RoundTripper unable to process 1xx response #4403

mchtech opened this issue Apr 2, 2024 · 2 comments · Fixed by #4437

Comments

@mchtech
Copy link
Contributor

mchtech commented Apr 2, 2024

When website enables HTTP 103 Early Hints, http3.RoundTripper returned "wrong?" status code and client failed to read response body

This code can reproduce this:

  • http2 client returns status 307 and correct body (and httptrace Got1xxResponse was called before roundtrip returned)
  • http3 client returns status 103 and empty body
package xxx

import (
	"context"
	"net/http"
	"net/http/httptrace"
	"net/textproto"
	"testing"

	"github.com/quic-go/quic-go/http3"
	"golang.org/x/net/http2"
)

func TestHttp3(t *testing.T) {
	ctx := context.Background()
	req, _ := http.NewRequestWithContext(
		httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{
			Got1xxResponse: func(code int, header textproto.MIMEHeader) error {
                                 // do something
				t.Log("got 1xx response", code, header) // <-- ✅ http/2 output status 103
				return nil
			},
		}),
		http.MethodGet,
		"https://betterprogramming.pub/object-oriented-programming-the-trillion-dollar-disaster-92a4b666c7c7",
		nil,
	)
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36")

	tr2 := &http2.Transport{}
	resp2, _ := tr2.RoundTrip(req)
	braw2, _ := io.ReadAll(resp2.Body)
	t.Log(resp2.StatusCode, string(braw2)) // <-- ✅ http/2 output status 307 and content `<a href="https: ......`

	rr3 := &http3.RoundTripper{}
	resp3, _ := rr3.RoundTrip(req)
	braw3, _ := io.ReadAll(resp3.Body)
	t.Log(resp3.StatusCode, string(braw3)) // <-- ❓http/3 output status 103 and empty content
}

curl -v https://xxx --http2 output:

> GET /..... HTTP/2
> Host: ......
>
......
< HTTP/2 103
< link: <https://......
<
< HTTP/2 307
< date: ......
< content-type: text/html; charset=utf-8
......
<
<a href="https://......
@mchtech
Copy link
Contributor Author

mchtech commented Apr 2, 2024

golang http.Transport use a loop to read response when status code is 1xx (https://github.com/golang/go/blob/go1.22.1/src/net/http/transport.go#L2333-L2368),

but quic-go http3.client does not do like this (https://github.com/quic-go/quic-go/blob/v0.42.0/http3/client.go#L457-L482)

@marten-seemann
Copy link
Member

You're right, we currently don't support 1xx responses yet.

Regarding 103, how would the resources sent in the Link headers be surfaced to the client? It seems like this might be difficult to achieve with the given Go API.

Would you be interested in contributing a fix? Most likely, you could start by copying what the standard library does, but obviously the code needs to be adapted to the structure of the http3 package.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants