|
@@ -154,12 +154,21 @@ func (t *Transport) pingTimeout() time.Duration {
|
|
|
|
|
|
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
|
// ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2.
|
|
// It returns an error if t1 has already been HTTP/2-enabled.
|
|
// It returns an error if t1 has already been HTTP/2-enabled.
|
|
|
|
+//
|
|
|
|
+// Use ConfigureTransports instead to configure the HTTP/2 Transport.
|
|
func ConfigureTransport(t1 *http.Transport) error {
|
|
func ConfigureTransport(t1 *http.Transport) error {
|
|
- _, err := configureTransport(t1)
|
|
|
|
|
|
+ _, err := ConfigureTransports(t1)
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
|
|
|
|
-func configureTransport(t1 *http.Transport) (*Transport, error) {
|
|
|
|
|
|
+// ConfigureTransports configures a net/http HTTP/1 Transport to use HTTP/2.
|
|
|
|
+// It returns a new HTTP/2 Transport for further configuration.
|
|
|
|
+// It returns an error if t1 has already been HTTP/2-enabled.
|
|
|
|
+func ConfigureTransports(t1 *http.Transport) (*Transport, error) {
|
|
|
|
+ return configureTransports(t1)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func configureTransports(t1 *http.Transport) (*Transport, error) {
|
|
connPool := new(clientConnPool)
|
|
connPool := new(clientConnPool)
|
|
t2 := &Transport{
|
|
t2 := &Transport{
|
|
ConnPool: noDialClientConnPool{connPool},
|
|
ConnPool: noDialClientConnPool{connPool},
|
|
@@ -689,6 +698,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
|
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
|
|
cc.inflow.add(transportDefaultConnFlow + initialWindowSize)
|
|
cc.bw.Flush()
|
|
cc.bw.Flush()
|
|
if cc.werr != nil {
|
|
if cc.werr != nil {
|
|
|
|
+ cc.Close()
|
|
return nil, cc.werr
|
|
return nil, cc.werr
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1080,6 +1090,15 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
bodyWriter := cc.t.getBodyWriterState(cs, body)
|
|
bodyWriter := cc.t.getBodyWriterState(cs, body)
|
|
cs.on100 = bodyWriter.on100
|
|
cs.on100 = bodyWriter.on100
|
|
|
|
|
|
|
|
+ defer func() {
|
|
|
|
+ cc.wmu.Lock()
|
|
|
|
+ werr := cc.werr
|
|
|
|
+ cc.wmu.Unlock()
|
|
|
|
+ if werr != nil {
|
|
|
|
+ cc.Close()
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+
|
|
cc.wmu.Lock()
|
|
cc.wmu.Lock()
|
|
endStream := !hasBody && !hasTrailers
|
|
endStream := !hasBody && !hasTrailers
|
|
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
|
werr := cc.writeHeaders(cs.ID, endStream, int(cc.maxFrameSize), hdrs)
|
|
@@ -1129,6 +1148,9 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
// we can keep it.
|
|
// we can keep it.
|
|
bodyWriter.cancel()
|
|
bodyWriter.cancel()
|
|
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
|
cs.abortRequestBodyWrite(errStopReqBodyWrite)
|
|
|
|
+ if hasBody && !bodyWritten {
|
|
|
|
+ <-bodyWriter.resc
|
|
|
|
+ }
|
|
}
|
|
}
|
|
if re.err != nil {
|
|
if re.err != nil {
|
|
cc.forgetStreamID(cs.ID)
|
|
cc.forgetStreamID(cs.ID)
|
|
@@ -1149,6 +1171,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
} else {
|
|
} else {
|
|
bodyWriter.cancel()
|
|
bodyWriter.cancel()
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
|
|
+ <-bodyWriter.resc
|
|
}
|
|
}
|
|
cc.forgetStreamID(cs.ID)
|
|
cc.forgetStreamID(cs.ID)
|
|
return nil, cs.getStartedWrite(), errTimeout
|
|
return nil, cs.getStartedWrite(), errTimeout
|
|
@@ -1158,6 +1181,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
} else {
|
|
} else {
|
|
bodyWriter.cancel()
|
|
bodyWriter.cancel()
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
|
|
+ <-bodyWriter.resc
|
|
}
|
|
}
|
|
cc.forgetStreamID(cs.ID)
|
|
cc.forgetStreamID(cs.ID)
|
|
return nil, cs.getStartedWrite(), ctx.Err()
|
|
return nil, cs.getStartedWrite(), ctx.Err()
|
|
@@ -1167,6 +1191,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
} else {
|
|
} else {
|
|
bodyWriter.cancel()
|
|
bodyWriter.cancel()
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
|
|
|
|
+ <-bodyWriter.resc
|
|
}
|
|
}
|
|
cc.forgetStreamID(cs.ID)
|
|
cc.forgetStreamID(cs.ID)
|
|
return nil, cs.getStartedWrite(), errRequestCanceled
|
|
return nil, cs.getStartedWrite(), errRequestCanceled
|
|
@@ -1176,6 +1201,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
// forgetStreamID.
|
|
// forgetStreamID.
|
|
return nil, cs.getStartedWrite(), cs.resetErr
|
|
return nil, cs.getStartedWrite(), cs.resetErr
|
|
case err := <-bodyWriter.resc:
|
|
case err := <-bodyWriter.resc:
|
|
|
|
+ bodyWritten = true
|
|
// Prefer the read loop's response, if available. Issue 16102.
|
|
// Prefer the read loop's response, if available. Issue 16102.
|
|
select {
|
|
select {
|
|
case re := <-readLoopResCh:
|
|
case re := <-readLoopResCh:
|
|
@@ -1186,7 +1212,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
|
|
cc.forgetStreamID(cs.ID)
|
|
cc.forgetStreamID(cs.ID)
|
|
return nil, cs.getStartedWrite(), err
|
|
return nil, cs.getStartedWrite(), err
|
|
}
|
|
}
|
|
- bodyWritten = true
|
|
|
|
if d := cc.responseHeaderTimeout(); d != 0 {
|
|
if d := cc.responseHeaderTimeout(); d != 0 {
|
|
timer := time.NewTimer(d)
|
|
timer := time.NewTimer(d)
|
|
defer timer.Stop()
|
|
defer timer.Stop()
|
|
@@ -2006,8 +2031,8 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
|
|
if !streamEnded || isHead {
|
|
if !streamEnded || isHead {
|
|
res.ContentLength = -1
|
|
res.ContentLength = -1
|
|
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
|
if clens := res.Header["Content-Length"]; len(clens) == 1 {
|
|
- if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil {
|
|
|
|
- res.ContentLength = clen64
|
|
|
|
|
|
+ if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
|
|
|
|
+ res.ContentLength = int64(cl)
|
|
} else {
|
|
} else {
|
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
|
// TODO: care? unlike http/1, it won't mess up our framing, so it's
|
|
// more safe smuggling-wise to ignore.
|
|
// more safe smuggling-wise to ignore.
|
|
@@ -2525,6 +2550,7 @@ func strSliceContains(ss []string, s string) bool {
|
|
|
|
|
|
type erringRoundTripper struct{ err error }
|
|
type erringRoundTripper struct{ err error }
|
|
|
|
|
|
|
|
+func (rt erringRoundTripper) RoundTripErr() error { return rt.err }
|
|
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
|
func (rt erringRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, rt.err }
|
|
|
|
|
|
// gzipReader wraps a response body so it can lazily
|
|
// gzipReader wraps a response body so it can lazily
|
|
@@ -2606,7 +2632,9 @@ func (t *Transport) getBodyWriterState(cs *clientStream, body io.Reader) (s body
|
|
|
|
|
|
func (s bodyWriterState) cancel() {
|
|
func (s bodyWriterState) cancel() {
|
|
if s.timer != nil {
|
|
if s.timer != nil {
|
|
- s.timer.Stop()
|
|
|
|
|
|
+ if s.timer.Stop() {
|
|
|
|
+ s.resc <- nil
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|