Skip to content

Commit

Permalink
http3: make it possible to send HEAD requests in 0-RTT (quic-go#4378)
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-seemann authored Mar 22, 2024
1 parent e14dd2f commit 603e077
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 21 deletions.
18 changes: 13 additions & 5 deletions http3/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,14 @@ import (
"github.com/quic-go/qpack"
)

// MethodGet0RTT allows a GET request to be sent using 0-RTT.
// Note that 0-RTT data doesn't provide replay protection.
const MethodGet0RTT = "GET_0RTT"
const (
// MethodGet0RTT allows a GET request to be sent using 0-RTT.
// Note that 0-RTT doesn't provide replay protection and should only be used for idempotent requests.
MethodGet0RTT = "GET_0RTT"
// MethodHead0RTT allows a HEAD request to be sent using 0-RTT.
// Note that 0-RTT doesn't provide replay protection and should only be used for idempotent requests.
MethodHead0RTT = "HEAD_0RTT"
)

const (
defaultUserAgent = "quic-go HTTP/3"
Expand Down Expand Up @@ -298,9 +303,12 @@ func (c *client) roundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Respon
conn := *c.conn.Load()

// Immediately send out this request, if this is a 0-RTT request.
if req.Method == MethodGet0RTT {
switch req.Method {
case MethodGet0RTT:
req.Method = http.MethodGet
} else {
case MethodHead0RTT:
req.Method = http.MethodHead
default:
// wait for the handshake to complete
select {
case <-conn.HandshakeComplete():
Expand Down
37 changes: 21 additions & 16 deletions http3/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,22 +807,27 @@ var _ = Describe("Client", func() {
Expect(err).To(MatchError(testErr))
})

It("performs a 0-RTT request", func() {
testErr := errors.New("stream open error")
req.Method = MethodGet0RTT
// don't EXPECT any calls to HandshakeComplete()
conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil)
buf := &bytes.Buffer{}
str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
str.EXPECT().Close()
str.EXPECT().CancelWrite(gomock.Any())
str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) {
return 0, testErr
})
_, err := cl.RoundTripOpt(req, RoundTripOpt{})
Expect(err).To(MatchError(testErr))
Expect(decodeHeader(buf)).To(HaveKeyWithValue(":method", "GET"))
})
DescribeTable(
"performs a 0-RTT request",
func(method, serialized string) {
testErr := errors.New("stream open error")
req.Method = method
// don't EXPECT any calls to HandshakeComplete()
conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil)
buf := &bytes.Buffer{}
str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
str.EXPECT().Close()
str.EXPECT().CancelWrite(gomock.Any())
str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) {
return 0, testErr
})
_, err := cl.RoundTripOpt(req, RoundTripOpt{})
Expect(err).To(MatchError(testErr))
Expect(decodeHeader(buf)).To(HaveKeyWithValue(":method", serialized))
},
Entry("GET", MethodGet0RTT, http.MethodGet),
Entry("HEAD", MethodHead0RTT, http.MethodHead),
)

It("returns a response", func() {
rspBuf := bytes.NewBuffer(getResponse(418))
Expand Down

0 comments on commit 603e077

Please sign in to comment.