vendor: revendor for gRPC

This commit is contained in:
Eric Chiang
2016-07-31 23:26:27 -07:00
parent a28d22ff0c
commit 933fc39e63
169 changed files with 38948 additions and 9223 deletions

View File

@@ -52,7 +52,16 @@ const (
noDialOnMiss = false
)
func (p *clientConnPool) getClientConn(_ *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
if isConnectionCloseRequest(req) && dialOnMiss {
// It gets its own connection.
const singleUse = true
cc, err := p.t.dialClientConn(addr, singleUse)
if err != nil {
return nil, err
}
return cc, nil
}
p.mu.Lock()
for _, cc := range p.conns[addr] {
if cc.CanTakeNewRequest() {
@@ -95,7 +104,8 @@ func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
// run in its own goroutine.
func (c *dialCall) dial(addr string) {
c.res, c.err = c.p.t.dialClientConn(addr)
const singleUse = false // shared conn
c.res, c.err = c.p.t.dialClientConn(addr, singleUse)
close(c.done)
c.p.mu.Lock()

View File

@@ -454,7 +454,7 @@ func terminalReadFrameError(err error) bool {
//
// If the frame is larger than previously set with SetMaxReadFrameSize, the
// returned error is ErrFrameTooLarge. Other errors may be of type
// ConnectionError, StreamError, or anything else from from the underlying
// ConnectionError, StreamError, or anything else from the underlying
// reader.
func (fr *Framer) ReadFrame() (Frame, error) {
fr.errDetail = nil

View File

@@ -7,6 +7,7 @@
package http2
import (
"crypto/tls"
"net/http"
"time"
)
@@ -14,3 +15,29 @@ import (
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return t1.ExpectContinueTimeout
}
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
func isBadCipher(cipher uint16) bool {
switch cipher {
case tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
// Reject cipher suites from Appendix A.
// "This list includes those cipher suites that do not
// offer an ephemeral key exchange and those that are
// based on the TLS null, stream or block cipher type"
return true
default:
return false
}
}

View File

@@ -7,6 +7,7 @@
package http2
import (
"crypto/tls"
"net/http"
"time"
)
@@ -17,4 +18,29 @@ func configureTransport(t1 *http.Transport) (*Transport, error) {
func transportExpectContinueTimeout(t1 *http.Transport) time.Duration {
return 0
}
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
func isBadCipher(cipher uint16) bool {
switch cipher {
case tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
// Reject cipher suites from Appendix A.
// "This list includes those cipher suites that do not
// offer an ephemeral key exchange and those that are
// based on the TLS null, stream or block cipher type"
return true
default:
return false
}
}

View File

@@ -339,30 +339,6 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
sc.serve()
}
// isBadCipher reports whether the cipher is blacklisted by the HTTP/2 spec.
func isBadCipher(cipher uint16) bool {
switch cipher {
case tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
// Reject cipher suites from Appendix A.
// "This list includes those cipher suites that do not
// offer an ephemeral key exchange and those that are
// based on the TLS null, stream or block cipher type"
return true
default:
return false
}
}
func (sc *serverConn) rejectConn(err ErrCode, debug string) {
sc.vlogf("http2: server rejecting conn: %v, %s", err, debug)
// ignoring errors. hanging up anyway.
@@ -1200,6 +1176,10 @@ func (sc *serverConn) closeStream(st *stream, err error) {
}
delete(sc.streams, st.id)
if p := st.body; p != nil {
// Return any buffered unread bytes worth of conn-level flow control.
// See golang.org/issue/16481
sc.sendWindowUpdate(nil, p.Len())
p.CloseWithError(err)
}
st.cw.Close() // signals Handler's CloseNotifier, unblocks writes, etc
@@ -1301,6 +1281,8 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
func (sc *serverConn) processData(f *DataFrame) error {
sc.serveG.check()
data := f.Data()
// "If a DATA frame is received whose stream is not in "open"
// or "half closed (local)" state, the recipient MUST respond
// with a stream error (Section 5.4.2) of type STREAM_CLOSED."
@@ -1312,12 +1294,25 @@ func (sc *serverConn) processData(f *DataFrame) error {
// the http.Handler returned, so it's done reading &
// done writing). Try to stop the client from sending
// more DATA.
// But still enforce their connection-level flow control,
// and return any flow control bytes since we're not going
// to consume them.
if int(sc.inflow.available()) < len(data) {
return StreamError{id, ErrCodeFlowControl}
}
// Deduct the flow control from inflow, since we're
// going to immediately add it back in
// sendWindowUpdate, which also schedules sending the
// frames.
sc.inflow.take(int32(len(data)))
sc.sendWindowUpdate(nil, len(data)) // conn-level
return StreamError{id, ErrCodeStreamClosed}
}
if st.body == nil {
panic("internal error: should have a body in this state")
}
data := f.Data()
// Sender sending more than they'd declared?
if st.declBodyBytes != -1 && st.bodyBytes+int64(len(data)) > st.declBodyBytes {

View File

@@ -104,6 +104,8 @@ func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{}
case optQuiet:
quiet = true
}
case func(net.Conn, http.ConnState):
ts.Config.ConnState = v
default:
t.Fatalf("unknown newServerTester option type %T", v)
}
@@ -2165,6 +2167,9 @@ func TestServer_NoCrash_HandlerClose_Then_ClientClose(t *testing.T) {
// it did before.
st.writeData(1, true, []byte("foo"))
// Get our flow control bytes back, since the handler didn't get them.
st.wantWindowUpdate(0, uint32(len("foo")))
// Sent after a peer sends data anyway (admittedly the
// previous RST_STREAM might've still been in-flight),
// but they'll get the more friendly 'cancel' code
@@ -3299,3 +3304,43 @@ func TestExpect100ContinueAfterHandlerWrites(t *testing.T) {
t.Fatalf("second msg = %q; want %q", buf, msg2)
}
}
type funcReader func([]byte) (n int, err error)
func (f funcReader) Read(p []byte) (n int, err error) { return f(p) }
// golang.org/issue/16481 -- return flow control when streams close with unread data.
// (The Server version of the bug. See also TestUnreadFlowControlReturned_Transport)
func TestUnreadFlowControlReturned_Server(t *testing.T) {
unblock := make(chan bool, 1)
defer close(unblock)
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
// Don't read the 16KB request body. Wait until the client's
// done sending it and then return. This should cause the Server
// to then return those 16KB of flow control to the client.
<-unblock
}, optOnlyServer)
defer st.Close()
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()
// This previously hung on the 4th iteration.
for i := 0; i < 6; i++ {
body := io.MultiReader(
io.LimitReader(neverEnding('A'), 16<<10),
funcReader(func([]byte) (n int, err error) {
unblock <- true
return 0, io.EOF
}),
)
req, _ := http.NewRequest("POST", st.ts.URL, body)
res, err := tr.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
res.Body.Close()
}
}

View File

@@ -139,9 +139,10 @@ func (t *Transport) initConnPool() {
// ClientConn is the state of a single HTTP/2 client connection to an
// HTTP/2 server.
type ClientConn struct {
t *Transport
tconn net.Conn // usually *tls.Conn, except specialized impls
tlsState *tls.ConnectionState // nil only for specialized impls
t *Transport
tconn net.Conn // usually *tls.Conn, except specialized impls
tlsState *tls.ConnectionState // nil only for specialized impls
singleUse bool // whether being used for a single http.Request
// readLoop goroutine fields:
readerDone chan struct{} // closed on error
@@ -153,6 +154,7 @@ type ClientConn struct {
inflow flow // peer's conn-level flow control
closed bool
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
goAwayDebug string // goAway frame's debug data, retained as a string
streams map[uint32]*clientStream // client-initiated
nextStreamID uint32
bw *bufio.Writer
@@ -337,7 +339,7 @@ func shouldRetryRequest(req *http.Request, err error) bool {
return err == errClientConnUnusable
}
func (t *Transport) dialClientConn(addr string) (*ClientConn, error) {
func (t *Transport) dialClientConn(addr string, singleUse bool) (*ClientConn, error) {
host, _, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
@@ -346,7 +348,7 @@ func (t *Transport) dialClientConn(addr string) (*ClientConn, error) {
if err != nil {
return nil, err
}
return t.NewClientConn(tconn)
return t.newClientConn(tconn, singleUse)
}
func (t *Transport) newTLSConfig(host string) *tls.Config {
@@ -407,6 +409,10 @@ func (t *Transport) expectContinueTimeout() time.Duration {
}
func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
return t.newClientConn(c, false)
}
func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
if VerboseLogs {
t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr())
}
@@ -424,6 +430,7 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
initialWindowSize: 65535, // spec default
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
streams: make(map[uint32]*clientStream),
singleUse: singleUse,
}
cc.cond = sync.NewCond(&cc.mu)
cc.flow.add(int32(initialWindowSize))
@@ -494,7 +501,17 @@ func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
func (cc *ClientConn) setGoAway(f *GoAwayFrame) {
cc.mu.Lock()
defer cc.mu.Unlock()
old := cc.goAway
cc.goAway = f
// Merge the previous and current GoAway error frames.
if cc.goAwayDebug == "" {
cc.goAwayDebug = string(f.DebugData())
}
if old != nil && old.ErrCode != ErrCodeNo {
cc.goAway.ErrCode = old.ErrCode
}
}
func (cc *ClientConn) CanTakeNewRequest() bool {
@@ -504,6 +521,9 @@ func (cc *ClientConn) CanTakeNewRequest() bool {
}
func (cc *ClientConn) canTakeNewRequestLocked() bool {
if cc.singleUse && cc.nextStreamID > 1 {
return false
}
return cc.goAway == nil && !cc.closed &&
int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) &&
cc.nextStreamID < 2147483647
@@ -732,30 +752,34 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
bodyWritten := false
ctx := reqContext(req)
handleReadLoopResponse := func(re resAndError) (*http.Response, error) {
res := re.res
if re.err != nil || res.StatusCode > 299 {
// On error or status code 3xx, 4xx, 5xx, etc abort any
// ongoing write, assuming that the server doesn't care
// about our request body. If the server replied with 1xx or
// 2xx, however, then assume the server DOES potentially
// want our body (e.g. full-duplex streaming:
// golang.org/issue/13444). If it turns out the server
// doesn't, they'll RST_STREAM us soon enough. This is a
// heuristic to avoid adding knobs to Transport. Hopefully
// we can keep it.
bodyWriter.cancel()
cs.abortRequestBodyWrite(errStopReqBodyWrite)
}
if re.err != nil {
cc.forgetStreamID(cs.ID)
return nil, re.err
}
res.Request = req
res.TLS = cc.tlsState
return res, nil
}
for {
select {
case re := <-readLoopResCh:
res := re.res
if re.err != nil || res.StatusCode > 299 {
// On error or status code 3xx, 4xx, 5xx, etc abort any
// ongoing write, assuming that the server doesn't care
// about our request body. If the server replied with 1xx or
// 2xx, however, then assume the server DOES potentially
// want our body (e.g. full-duplex streaming:
// golang.org/issue/13444). If it turns out the server
// doesn't, they'll RST_STREAM us soon enough. This is a
// heuristic to avoid adding knobs to Transport. Hopefully
// we can keep it.
bodyWriter.cancel()
cs.abortRequestBodyWrite(errStopReqBodyWrite)
}
if re.err != nil {
cc.forgetStreamID(cs.ID)
return nil, re.err
}
res.Request = req
res.TLS = cc.tlsState
return res, nil
return handleReadLoopResponse(re)
case <-respHeaderTimer:
cc.forgetStreamID(cs.ID)
if !hasBody || bodyWritten {
@@ -789,6 +813,12 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
// forgetStreamID.
return nil, cs.resetErr
case err := <-bodyWriter.resc:
// Prefer the read loop's response, if available. Issue 16102.
select {
case re := <-readLoopResCh:
return handleReadLoopResponse(re)
default:
}
if err != nil {
return nil, err
}
@@ -1160,6 +1190,19 @@ func (cc *ClientConn) readLoop() {
}
}
// GoAwayError is returned by the Transport when the server closes the
// TCP connection after sending a GOAWAY frame.
type GoAwayError struct {
LastStreamID uint32
ErrCode ErrCode
DebugData string
}
func (e GoAwayError) Error() string {
return fmt.Sprintf("http2: server sent GOAWAY and closed the connection; LastStreamID=%v, ErrCode=%v, debug=%q",
e.LastStreamID, e.ErrCode, e.DebugData)
}
func (rl *clientConnReadLoop) cleanup() {
cc := rl.cc
defer cc.tconn.Close()
@@ -1170,10 +1213,18 @@ func (rl *clientConnReadLoop) cleanup() {
// TODO: also do this if we've written the headers but not
// gotten a response yet.
err := cc.readerErr
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
cc.mu.Lock()
if err == io.EOF {
if cc.goAway != nil {
err = GoAwayError{
LastStreamID: cc.goAway.LastStreamID,
ErrCode: cc.goAway.ErrCode,
DebugData: cc.goAwayDebug,
}
} else {
err = io.ErrUnexpectedEOF
}
}
for _, cs := range rl.activeRes {
cs.bufPipe.CloseWithError(err)
}
@@ -1191,7 +1242,7 @@ func (rl *clientConnReadLoop) cleanup() {
func (rl *clientConnReadLoop) run() error {
cc := rl.cc
rl.closeWhenIdle = cc.t.disableKeepAlives()
rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse
gotReply := false // ever saw a reply
for {
f, err := cc.fr.ReadFrame()
@@ -1486,10 +1537,27 @@ var errClosedResponseBody = errors.New("http2: response body closed")
func (b transportResponseBody) Close() error {
cs := b.cs
if cs.bufPipe.Err() != io.EOF {
// TODO: write test for this
cs.cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
cc := cs.cc
serverSentStreamEnd := cs.bufPipe.Err() == io.EOF
unread := cs.bufPipe.Len()
if unread > 0 || !serverSentStreamEnd {
cc.mu.Lock()
cc.wmu.Lock()
if !serverSentStreamEnd {
cc.fr.WriteRSTStream(cs.ID, ErrCodeCancel)
}
// Return connection-level flow control.
if unread > 0 {
cc.inflow.add(int32(unread))
cc.fr.WriteWindowUpdate(0, uint32(unread))
}
cc.bw.Flush()
cc.wmu.Unlock()
cc.mu.Unlock()
}
cs.bufPipe.BreakWithError(errClosedResponseBody)
return nil
}
@@ -1497,6 +1565,7 @@ func (b transportResponseBody) Close() error {
func (rl *clientConnReadLoop) processData(f *DataFrame) error {
cc := rl.cc
cs := cc.streamByID(f.StreamID, f.StreamEnded())
data := f.Data()
if cs == nil {
cc.mu.Lock()
neverSent := cc.nextStreamID
@@ -1510,9 +1579,17 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
// TODO: be stricter here? only silently ignore things which
// we canceled, but not things which were closed normally
// by the peer? Tough without accumulating too much state.
// But at least return their flow control:
if len(data) > 0 {
cc.wmu.Lock()
cc.fr.WriteWindowUpdate(0, uint32(len(data)))
cc.bw.Flush()
cc.wmu.Unlock()
}
return nil
}
if data := f.Data(); len(data) > 0 {
if len(data) > 0 {
if cs.bufPipe.b == nil {
// Data frame after it's already closed?
cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
@@ -1557,7 +1634,7 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
}
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)
if cs.req.Close || cs.req.Header.Get("Connection") == "close" {
if isConnectionCloseRequest(cs.req) {
rl.closeWhenIdle = true
}
}
@@ -1679,8 +1756,10 @@ func (rl *clientConnReadLoop) processPushPromise(f *PushPromiseFrame) error {
}
func (cc *ClientConn) writeStreamReset(streamID uint32, code ErrCode, err error) {
// TODO: do something with err? send it as a debug frame to the peer?
// But that's only in GOAWAY. Invent a new frame type? Is there one already?
// TODO: map err to more interesting error codes, once the
// HTTP community comes up with some. But currently for
// RST_STREAM there's no equivalent to GOAWAY frame's debug
// data, and the error codes are all pretty vague ("cancel").
cc.wmu.Lock()
cc.fr.WriteRSTStream(streamID, code)
cc.bw.Flush()
@@ -1830,3 +1909,9 @@ func (s bodyWriterState) scheduleBodyWrite() {
s.timer.Reset(s.delay)
}
}
// isConnectionCloseRequest reports whether req should use its own
// connection for a single request and then close the connection.
func isConnectionCloseRequest(req *http.Request) bool {
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
}

View File

@@ -150,7 +150,9 @@ func TestTransport(t *testing.T) {
func onSameConn(t *testing.T, modReq func(*http.Request)) bool {
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, r.RemoteAddr)
}, optOnlyServer)
}, optOnlyServer, func(c net.Conn, st http.ConnState) {
t.Logf("conn %v is now state %v", c.RemoteAddr(), st)
})
defer st.Close()
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()
@@ -755,18 +757,18 @@ func testTransportReqBodyAfterResponse(t *testing.T, status int) {
if f.StreamEnded() {
return fmt.Errorf("headers contains END_STREAM unexpectedly: %v", f)
}
time.Sleep(50 * time.Millisecond) // let client send body
enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
case *DataFrame:
dataLen := len(f.Data())
dataRecv += int64(dataLen)
if dataLen > 0 {
if dataRecv == 0 {
enc.WriteField(hpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
}
if err := ct.fr.WriteWindowUpdate(0, uint32(dataLen)); err != nil {
return err
}
@@ -774,6 +776,8 @@ func testTransportReqBodyAfterResponse(t *testing.T, status int) {
return err
}
}
dataRecv += int64(dataLen)
if !closed && ((status != 200 && dataRecv > 0) ||
(status == 200 && dataRecv == bodySize)) {
closed = true
@@ -1013,41 +1017,38 @@ func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerTy
if err != nil {
return err
}
endStream := false
send := func(mode headerType) {
hbf := buf.Bytes()
switch mode {
case oneHeader:
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.Header().StreamID,
EndHeaders: true,
EndStream: endStream,
BlockFragment: hbf,
})
case splitHeader:
if len(hbf) < 2 {
panic("too small")
}
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.Header().StreamID,
EndHeaders: false,
EndStream: endStream,
BlockFragment: hbf[:1],
})
ct.fr.WriteContinuation(f.Header().StreamID, true, hbf[1:])
default:
panic("bogus mode")
}
}
switch f := f.(type) {
case *WindowUpdateFrame, *SettingsFrame:
case *DataFrame:
// ignore for now.
case *HeadersFrame:
endStream := false
send := func(mode headerType) {
hbf := buf.Bytes()
switch mode {
case oneHeader:
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: true,
EndStream: endStream,
BlockFragment: hbf,
})
case splitHeader:
if len(hbf) < 2 {
panic("too small")
}
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: f.StreamID,
EndHeaders: false,
EndStream: endStream,
BlockFragment: hbf[:1],
})
ct.fr.WriteContinuation(f.StreamID, true, hbf[1:])
default:
panic("bogus mode")
}
}
if expect100Continue != noHeader {
buf.Reset()
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
send(expect100Continue)
if !f.StreamEnded() {
// No need to send flow control tokens. The test request body is tiny.
continue
}
// Response headers (1+ frames; 1 or 2 in this test, but never 0)
{
@@ -1071,7 +1072,15 @@ func testTransportResPattern(t *testing.T, expect100Continue, resHeader headerTy
enc.WriteField(hpack.HeaderField{Name: "some-trailer", Value: "some-value"})
send(trailers)
}
return nil
if endStream {
return nil
}
case *HeadersFrame:
if expect100Continue != noHeader {
buf.Reset()
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "100"})
send(expect100Continue)
}
}
}
}
@@ -2011,3 +2020,160 @@ func TestTransportFlowControl(t *testing.T) {
time.Sleep(1 * time.Millisecond)
}
}
// golang.org/issue/14627 -- if the server sends a GOAWAY frame, make
// the Transport remember it and return it back to users (via
// RoundTrip or request body reads) if needed (e.g. if the server
// proceeds to close the TCP connection before the client gets its
// response)
func TestTransportUsesGoAwayDebugError_RoundTrip(t *testing.T) {
testTransportUsesGoAwayDebugError(t, false)
}
func TestTransportUsesGoAwayDebugError_Body(t *testing.T) {
testTransportUsesGoAwayDebugError(t, true)
}
func testTransportUsesGoAwayDebugError(t *testing.T, failMidBody bool) {
ct := newClientTester(t)
clientDone := make(chan struct{})
const goAwayErrCode = ErrCodeHTTP11Required // arbitrary
const goAwayDebugData = "some debug data"
ct.client = func() error {
defer close(clientDone)
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
res, err := ct.tr.RoundTrip(req)
if failMidBody {
if err != nil {
return fmt.Errorf("unexpected client RoundTrip error: %v", err)
}
_, err = io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
}
want := GoAwayError{
LastStreamID: 5,
ErrCode: goAwayErrCode,
DebugData: goAwayDebugData,
}
if !reflect.DeepEqual(err, want) {
t.Errorf("RoundTrip error = %T: %#v, want %T (%#T)", err, err, want, want)
}
return nil
}
ct.server = func() error {
ct.greet()
for {
f, err := ct.fr.ReadFrame()
if err != nil {
t.Logf("ReadFrame: %v", err)
return nil
}
hf, ok := f.(*HeadersFrame)
if !ok {
continue
}
if failMidBody {
var buf bytes.Buffer
enc := hpack.NewEncoder(&buf)
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: "123"})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: hf.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
}
// Write two GOAWAY frames, to test that the Transport takes
// the interesting parts of both.
ct.fr.WriteGoAway(5, ErrCodeNo, []byte(goAwayDebugData))
ct.fr.WriteGoAway(5, goAwayErrCode, nil)
ct.sc.Close()
<-clientDone
return nil
}
}
ct.run()
}
// See golang.org/issue/16481
func TestTransportReturnsUnusedFlowControl(t *testing.T) {
ct := newClientTester(t)
clientClosed := make(chan bool, 1)
serverWroteBody := make(chan bool, 1)
ct.client = func() error {
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
res, err := ct.tr.RoundTrip(req)
if err != nil {
return err
}
<-serverWroteBody
if n, err := res.Body.Read(make([]byte, 1)); err != nil || n != 1 {
return fmt.Errorf("body read = %v, %v; want 1, nil", n, err)
}
res.Body.Close() // leaving 4999 bytes unread
clientClosed <- true
return nil
}
ct.server = func() error {
ct.greet()
var hf *HeadersFrame
for {
f, err := ct.fr.ReadFrame()
if err != nil {
return fmt.Errorf("ReadFrame while waiting for Headers: %v", err)
}
switch f.(type) {
case *WindowUpdateFrame, *SettingsFrame:
continue
}
var ok bool
hf, ok = f.(*HeadersFrame)
if !ok {
return fmt.Errorf("Got %T; want HeadersFrame", f)
}
break
}
var buf bytes.Buffer
enc := hpack.NewEncoder(&buf)
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: "5000"})
ct.fr.WriteHeaders(HeadersFrameParam{
StreamID: hf.StreamID,
EndHeaders: true,
EndStream: false,
BlockFragment: buf.Bytes(),
})
ct.fr.WriteData(hf.StreamID, false, make([]byte, 5000)) // without ending stream
serverWroteBody <- true
<-clientClosed
f, err := ct.fr.ReadFrame()
if err != nil {
return fmt.Errorf("ReadFrame while waiting for RSTStreamFrame: %v", err)
}
if rf, ok := f.(*RSTStreamFrame); !ok || rf.ErrCode != ErrCodeCancel {
return fmt.Errorf("Expected a WindowUpdateFrame with code cancel; got %v", summarizeFrame(f))
}
// And wait for our flow control tokens back:
f, err = ct.fr.ReadFrame()
if err != nil {
return fmt.Errorf("ReadFrame while waiting for WindowUpdateFrame: %v", err)
}
if wuf, ok := f.(*WindowUpdateFrame); !ok || wuf.Increment != 4999 {
return fmt.Errorf("Expected WindowUpdateFrame for 4999 bytes; got %v", summarizeFrame(f))
}
return nil
}
ct.run()
}