forked from quic-go/quic-go
http3: don't remove clients on RoundTripOpt context canceled (#4448)
* fix(http3): reuse clients on RoundTripOpt context cancelled Signed-off-by: George MacRorie <me@georgemac.com> * chore(http3): add comment to context canceled check on roundtrip error --------- Signed-off-by: George MacRorie <me@georgemac.com>
This commit is contained in:
@@ -165,7 +165,13 @@ func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.
|
|||||||
defer cl.useCount.Add(-1)
|
defer cl.useCount.Add(-1)
|
||||||
rsp, err := cl.rt.RoundTripOpt(req, opt)
|
rsp, err := cl.rt.RoundTripOpt(req, opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.removeClient(hostname)
|
// non-nil errors on roundtrip are likely due to a problem with the connection
|
||||||
|
// so we remove the client from the cache so that subsequent trips reconnect
|
||||||
|
// context cancelation is excluded as is does not signify a connection error
|
||||||
|
if !errors.Is(err, context.Canceled) {
|
||||||
|
r.removeClient(hostname)
|
||||||
|
}
|
||||||
|
|
||||||
if isReused {
|
if isReused {
|
||||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||||
return r.RoundTripOpt(req, opt)
|
return r.RoundTripOpt(req, opt)
|
||||||
|
|||||||
@@ -296,6 +296,37 @@ var _ = Describe("RoundTripper", func() {
|
|||||||
Expect(count).To(Equal(2))
|
Expect(count).To(Equal(2))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("does not remove a client when a request returns context canceled error", func() {
|
||||||
|
cl1 := NewMockSingleRoundTripper(mockCtrl)
|
||||||
|
clientChan <- cl1
|
||||||
|
cl2 := NewMockSingleRoundTripper(mockCtrl)
|
||||||
|
clientChan <- cl2
|
||||||
|
|
||||||
|
req1, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
req2, err := http.NewRequest("GET", "https://quic-go.net/bar.html", nil)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
conn := mockquic.NewMockEarlyConnection(mockCtrl)
|
||||||
|
var count int
|
||||||
|
rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) {
|
||||||
|
count++
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
testErr := context.Canceled
|
||||||
|
handshakeChan := make(chan struct{})
|
||||||
|
close(handshakeChan)
|
||||||
|
conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2)
|
||||||
|
cl1.EXPECT().RoundTripOpt(req1, gomock.Any()).Return(nil, testErr)
|
||||||
|
cl1.EXPECT().RoundTripOpt(req2, gomock.Any()).Return(&http.Response{Request: req2}, nil)
|
||||||
|
_, err = rt.RoundTrip(req1)
|
||||||
|
Expect(err).To(MatchError(testErr))
|
||||||
|
rsp, err := rt.RoundTrip(req2)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(rsp.Request).To(Equal(req2))
|
||||||
|
Expect(count).To(Equal(1))
|
||||||
|
})
|
||||||
|
|
||||||
It("recreates a client when a request times out", func() {
|
It("recreates a client when a request times out", func() {
|
||||||
var reqCount int
|
var reqCount int
|
||||||
cl1 := NewMockSingleRoundTripper(mockCtrl)
|
cl1 := NewMockSingleRoundTripper(mockCtrl)
|
||||||
|
|||||||
Reference in New Issue
Block a user