func ServerPushTestGroup(ctx *Context) *TestGroup { tg := NewTestGroup("8.2", "Server Push") tg.AddTestCase(NewTestCase( "Sends a PUSH_PROMISE frame", "The endpoint MUST treat the receipt of a PUSH_PROMISE frame as a connection error of type PROTOCOL_ERROR.", func(ctx *Context) (expected []Result, actual Result) { http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := commonHeaderFields(ctx) enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var pp http2.PushPromiseParam pp.StreamID = 1 pp.PromiseID = 3 pp.EndHeaders = true pp.BlockFragment = buf.Bytes() http2Conn.fr.WritePushPromise(pp) actualCodes := []http2.ErrCode{http2.ErrCodeProtocol} return TestConnectionError(ctx, http2Conn, actualCodes) }, )) return tg }
func main() { flag.Usage = usage flag.Parse() if flag.NArg() != 1 { usage() } log.SetFlags(0) host := flag.Arg(0) app := &h2i{ host: host, peerSetting: make(map[http2.SettingID]uint32), } app.henc = hpack.NewEncoder(&app.hbuf) if err := app.Main(); err != nil { if app.term != nil { app.logf("%v\n", err) } else { fmt.Fprintf(os.Stderr, "%v\n", err) } os.Exit(1) } fmt.Fprintf(os.Stdout, "\n") }
func NewConnection(host string, isTLS, sendPreface, sendSettingsInit bool) *Connection { conn := &Connection{ Host: host, IsTLS: isTLS, IsPreface: sendPreface, IsSendSettings: sendSettingsInit, PeerSetting: make(map[http2.SettingID]uint32), } conn.HEnc = hpack.NewEncoder(&conn.HBuf) raw, err := Dial(host, isTLS) if err != nil { conn.handleError(err) return conn } fmt.Println(raw) conn.Raw = raw conn.SetupFramer() if sendPreface { conn.SendPreface() } if sendSettingsInit { conn.SendInitSettings() } go func() { conn.readFrames() }() return conn }
func TestHTTPHeaderFields(ctx *Context) { PrintHeader("8.1.2. HTTP Header Fields", 1) func(ctx *Context) { desc := "Sends a HEADERS frame that contains the header field name in uppercase letters" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("X-TEST", "test"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 1) }(ctx) TestPseudoHeaderFields(ctx) TestConnectionSpecificHeaderFields(ctx) TestRequestPseudoHeaderFields(ctx) TestMalformedRequestsAndResponses(ctx) }
func TestPriority(ctx *Context) { PrintHeader("6.3. PRIORITY", 0) func(ctx *Context) { desc := "Sends a PRIORITY frame with 0x0 stream identifier" msg := "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = false hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) fmt.Fprintf(http2Conn.conn, "\x00\x00\x05\x02\x00\x00\x00\x00\x00") http2Conn.conn.Write(buf.Bytes()) fmt.Fprintf(http2Conn.conn, "\x80\x00\x00\x01\x0a") timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) PrintFooter() }
func TestMalformedRequestsAndResponses(ctx *Context) { PrintHeader("8.1.2.6. Malformed Requests and Responses", 2) // 8.1.2.6. ボディを構成する DATA フレームペイロードの長さの合計が "content-length" ヘッダーフィールドの値と等しくない場合、リクエストやレスポンスは不正な形式になります。 func(ctx *Context) { desc := "Sends a HEADERS frame that contains invalid \"content-length\" header field" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "POST"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("content-length", "1"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = false hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) }
// like encodeHeader, but don't add implicit psuedo headers. func encodeHeaderNoImplicit(t *testing.T, headers ...string) []byte { var buf bytes.Buffer enc := hpack.NewEncoder(&buf) for len(headers) > 0 { k, v := headers[0], headers[1] headers = headers[2:] if err := enc.WriteField(hpack.HeaderField{Name: k, Value: v}); err != nil { t.Fatalf("HPACK encoding error for %q/%q: %v", k, v, err) } } return buf.Bytes() }
func NewConnectionRaw(c net.Conn, tls bool) *Connection { conn := &Connection{ Host: "localhost", IsTLS: tls, Raw: c, } conn.HEnc = hpack.NewEncoder(&conn.HBuf) conn.SetupFramer() conn.SendInitSettings() go func() { conn.readFrames() }() return conn }
// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is // returned if something goes wrong. func newHTTP2Server(conn net.Conn, maxStreams uint32) (_ ServerTransport, err error) { framer := newFramer(conn) // Send initial settings as connection preface to client. // TODO(zhaoq): Have a better way to signal "no limit" because 0 is // permitted in the HTTP2 spec. var settings []http2.Setting // TODO(zhaoq): Have a better way to signal "no limit" because 0 is // permitted in the HTTP2 spec. if maxStreams == 0 { maxStreams = math.MaxUint32 } else { settings = append(settings, http2.Setting{http2.SettingMaxConcurrentStreams, maxStreams}) } if initialWindowSize != defaultWindowSize { settings = append(settings, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)}) } if err := framer.writeSettings(true, settings...); err != nil { return nil, ConnectionErrorf("transport: %v", err) } // Adjust the connection flow control window if needed. if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 { if err := framer.writeWindowUpdate(true, 0, delta); err != nil { return nil, ConnectionErrorf("transport: %v", err) } } var buf bytes.Buffer t := &http2Server{ conn: conn, framer: framer, hBuf: &buf, hEnc: hpack.NewEncoder(&buf), maxStreams: maxStreams, controlBuf: newRecvBuffer(), fc: &inFlow{limit: initialConnWindowSize}, sendQuotaPool: newQuotaPool(defaultWindowSize), state: reachable, writableChan: make(chan int, 1), shutdownChan: make(chan struct{}), activeStreams: make(map[uint32]*Stream), streamSendQuota: defaultWindowSize, } go t.controller() t.writableChan <- 0 return t, nil }
func NewServerConnection(c net.Conn, tls bool) *Connection { conn := &Connection{ Host: "localhost", IsTLS: tls, Raw: c, PeerSetting: make(map[http2.SettingID]uint32), IsSendSettings: true, } conn.HEnc = hpack.NewEncoder(&conn.HBuf) conn.SetupFramer() conn.readPreface() conn.Framer.WriteSettings() conn.Framer.WriteSettingsAck() conn.Framer.WriteSettings() conn.Framer.WriteSettingsAck() go func() { conn.readFrames() }() return conn }
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2 // and starts to receive messages on it. Non-nil error returns if construction // fails. func newHTTP2Client(addr string, authOpts []credentials.Credentials) (_ ClientTransport, err error) { var ( connErr error conn net.Conn ) scheme := "http" // TODO(zhaoq): Use DialTimeout instead. for _, c := range authOpts { if ccreds, ok := c.(credentials.TransportAuthenticator); ok { scheme = "https" // TODO(zhaoq): Now the first TransportAuthenticator is used if there are // multiple ones provided. Revisit this if it is not appropriate. Probably // place the ClientTransport construction into a separate function to make // things clear. conn, connErr = ccreds.Dial(addr) break } } if scheme == "http" { conn, connErr = net.Dial("tcp", addr) } if connErr != nil { return nil, ConnectionErrorf("transport: %v", connErr) } defer func() { if err != nil { conn.Close() } }() // Send connection preface to server. n, err := conn.Write(clientPreface) if err != nil { return nil, ConnectionErrorf("transport: %v", err) } if n != len(clientPreface) { return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface)) } framer := http2.NewFramer(conn, conn) if err := framer.WriteSettings(); err != nil { return nil, ConnectionErrorf("transport: %v", err) } var buf bytes.Buffer t := &http2Client{ target: addr, conn: conn, // The client initiated stream id is odd starting from 1. nextID: 1, writableChan: make(chan int, 1), shutdownChan: make(chan struct{}), errorChan: make(chan struct{}), framer: framer, hBuf: &buf, hEnc: hpack.NewEncoder(&buf), controlBuf: newRecvBuffer(), sendQuotaPool: newQuotaPool(initialWindowSize), scheme: scheme, state: reachable, activeStreams: make(map[uint32]*Stream), maxStreams: math.MaxUint32, authCreds: authOpts, } go t.controller() t.writableChan <- 0 // Start the reader goroutine for incoming message. The threading model // on receiving is that each transport has a dedicated goroutine which // reads HTTP2 frame from network. Then it dispatches the frame to the // corresponding stream entity. go t.reader() return t, nil }
func TestWindowUpdate(ctx *Context) { PrintHeader("6.9. WINDOW_UPDATE", 0) func(ctx *Context) { desc := "Sends a WINDOW_UPDATE frame with an flow control window increment of 0" msg := "the endpoint MUST respond with a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() http2Conn.fr.WriteWindowUpdate(0, 0) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a WINDOW_UPDATE frame with an flow control window increment of 0 on a stream" msg := "the endpoint MUST respond with a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = false hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteWindowUpdate(1, 0) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) TestInitialFlowControlWindowSize(ctx) PrintFooter() }
func CreateHttp2Conn(ctx *Context, sn bool) *Http2Conn { var conn net.Conn var err error if ctx.Tls { conn, err = connectTls(ctx) } else { conn, err = net.Dial("tcp", ctx.Authority()) } if err != nil { fmt.Printf("Unable to connect to the target server: %v\n", err) os.Exit(1) } fmt.Fprintf(conn, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n") fr := http2.NewFramer(conn, conn) settings := map[http2.SettingID]uint32{} if sn { doneCh := make(chan bool, 1) errCh := make(chan error, 1) fr.WriteSettings() go func() { local := false remote := false for { f, err := fr.ReadFrame() if err != nil { errCh <- err return } switch f := f.(type) { case *http2.SettingsFrame: if f.IsAck() { local = true } else { f.ForeachSetting(func(setting http2.Setting) error { settings[setting.ID] = setting.Val return nil }) fr.WriteSettingsAck() remote = true } } if local && remote { doneCh <- true return } } }() select { case <-doneCh: // Nothing to. do case <-errCh: fmt.Println("HTTP/2 settings negotiation failed") os.Exit(1) case <-time.After(ctx.Timeout): fmt.Println("HTTP/2 settings negotiation timeout") os.Exit(1) } } fr.AllowIllegalWrites = true dataCh := make(chan http2.Frame) errCh := make(chan error, 1) http2Conn := &Http2Conn{ conn: conn, fr: fr, dataCh: dataCh, errCh: errCh, Settings: settings, } http2Conn.HpackEncoder = hpack.NewEncoder(&http2Conn.HeaderWriteBuf) return http2Conn }
func TestPseudoHeaderFields(ctx *Context) { PrintHeader("8.1.2.1. Pseudo-Header Fields", 2) func(ctx *Context) { desc := "Sends a HEADERS frame that contains the pseudo-header field defined for response" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair(":status", "200"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame that contains the invalid pseudo-header field" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair(":test", "test"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame that contains a pseudo-header field that appears in a header block after a regular header field" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair("x-test", "test"), pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) }
func HeadersTestGroup(ctx *Context) *TestGroup { tg := NewTestGroup("6.2", "HEADERS") tg.AddTestCase(NewTestCase( "Sends a HEADERS frame followed by any frame other than CONTINUATION", "The endpoint MUST treat the receipt of any other type of frame as a connection error of type PROTOCOL_ERROR.", func(ctx *Context) (expected []Result, actual Result) { http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() hdrs := commonHeaderFields(ctx) var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = false hp.EndHeaders = false hp.BlockFragment = http2Conn.EncodeHeader(hdrs) http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) actualCodes := []http2.ErrCode{http2.ErrCodeProtocol} return TestConnectionError(ctx, http2Conn, actualCodes) }, )) tg.AddTestCase(NewTestCase( "Sends a HEADERS frame followed by a frame on a different stream", "The endpoint MUST treat the receipt of a frame on a different stream as a connection error of type PROTOCOL_ERROR.", func(ctx *Context) (expected []Result, actual Result) { http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() hdrs := commonHeaderFields(ctx) var hp1 http2.HeadersFrameParam hp1.StreamID = 1 hp1.EndStream = false hp1.EndHeaders = false hp1.BlockFragment = http2Conn.EncodeHeader(hdrs) http2Conn.fr.WriteHeaders(hp1) var hp2 http2.HeadersFrameParam hp2.StreamID = 3 hp2.EndStream = true hp2.EndHeaders = true hp2.BlockFragment = http2Conn.EncodeHeader(hdrs) http2Conn.fr.WriteHeaders(hp2) actualCodes := []http2.ErrCode{http2.ErrCodeProtocol} return TestConnectionError(ctx, http2Conn, actualCodes) }, )) tg.AddTestCase(NewTestCase( "Sends a HEADERS frame with 0x0 stream identifier", "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR.", func(ctx *Context) (expected []Result, actual Result) { http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() hdrs := commonHeaderFields(ctx) var hp http2.HeadersFrameParam hp.StreamID = 0 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = http2Conn.EncodeHeader(hdrs) http2Conn.fr.WriteHeaders(hp) actualCodes := []http2.ErrCode{http2.ErrCodeProtocol} return TestConnectionError(ctx, http2Conn, actualCodes) }, )) tg.AddTestCase(NewTestCase( "Sends a HEADERS frame with invalid pad length", "The endpoint MUST treat this as a connection error of type PROTOCOL_ERROR.", func(ctx *Context) (expected []Result, actual Result) { http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := commonHeaderFields(ctx) enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } fmt.Fprintf(http2Conn.conn, "\x00\x00\x0f\x01\x0c\x00\x00\x00\x01") http2Conn.conn.Write(buf.Bytes()) fmt.Fprintf(http2Conn.conn, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") actualCodes := []http2.ErrCode{http2.ErrCodeProtocol} return TestConnectionError(ctx, http2Conn, actualCodes) }, )) return tg }
func TestConnectionSpecificHeaderFields(ctx *Context) { PrintHeader("8.1.2.2. Connection-Specific Header Fields", 2) // 8.1.2.3. CONNECT リクエスト (8.3節) である場合を除き、":method"、":scheme"、そして ":path" 擬似ヘッダーフィールドに有効な値を1つ含まなければなりません (MUST)。これらの擬似ヘッダーフィールドが省略された HTTP リクエストは不正な形式 (8.1.2.6節) です。 // 8.1.2.6. ボディを構成する DATA フレームペイロードの長さの合計が "content-length" ヘッダーフィールドの値と等しくない場合、リクエストやレスポンスは不正な形式になります。 func(ctx *Context) { desc := "Sends a HEADERS frame that contains the connection-specific header field" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("connection", "keep-alive"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame that contains the TE header field that contain any value other than \"trailers\"" msg := "the endpoint MUST respond with a stream error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("trailers", "test"), pair("te", "trailers, deflate"), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: rf, ok := f.(*http2.RSTStreamFrame) if ok { if rf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 2) }(ctx) }
func TestHeaders(ctx *Context) { PrintHeader("6.2. HEADERS", 0) func(ctx *Context) { desc := "Sends a HEADERS frame followed by any frame other than CONTINUATION" msg := "The endpoint MUST treat the receipt of any other type of frame as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = false hp.EndHeaders = false hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame followed by a frame on a different stream" msg := "The endpoint MUST treat the receipt of a frame on a different stream as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp1 http2.HeadersFrameParam hp1.StreamID = 1 hp1.EndStream = false hp1.EndHeaders = false hp1.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp1) var hp2 http2.HeadersFrameParam hp2.StreamID = 3 hp2.EndStream = true hp2.EndHeaders = true hp2.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp2) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame with 0x0 stream identifier" msg := "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 0 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a HEADERS frame with invalid pad length" msg := "The endpoint MUST treat this as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } fmt.Fprintf(http2Conn.conn, "\x00\x00\x0f\x01\x0c\x00\x00\x00\x01") http2Conn.conn.Write(buf.Bytes()) fmt.Fprintf(http2Conn.conn, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) PrintFooter() }
func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) { cfg := &tls.Config{ ServerName: host, NextProtos: []string{http2.NextProtoTLS}, InsecureSkipVerify: t.InsecureTLSDial, } tconn, err := tls.Dial("tcp", host+":"+port, cfg) if err != nil { return nil, err } if err := tconn.Handshake(); err != nil { return nil, err } if !t.InsecureTLSDial { if err := tconn.VerifyHostname(cfg.ServerName); err != nil { return nil, err } } state := tconn.ConnectionState() if p := state.NegotiatedProtocol; p != http2.NextProtoTLS { // TODO(bradfitz): fall back to Fallback // return nil, fmt.Errorf("bad protocol: %v", p) return nil, errBadProtocol } if !state.NegotiatedProtocolIsMutual { return nil, errors.New("could not negotiate protocol mutually") } if _, err := tconn.Write(clientPreface); err != nil { return nil, err } cc := &clientConn{ t: t, tconn: tconn, connKey: []string{key}, // TODO: cert's validated hostnames too tlsState: &state, readerDone: make(chan struct{}), nextStreamID: 1, maxFrameSize: 16 << 10, // spec default initialWindowSize: 65535, // spec default maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough. streams: make(map[uint32]*clientStream), } cc.bw = bufio.NewWriter(stickyErrWriter{tconn, &cc.werr}) cc.br = bufio.NewReader(tconn) cc.fr = http2.NewFramer(cc.bw, cc.br) cc.henc = hpack.NewEncoder(&cc.hbuf) cc.fr.WriteSettings() // TODO: re-send more conn-level flow control tokens when server uses all these. cc.fr.WriteWindowUpdate(0, 1<<30) // um, 0x7fffffff doesn't work to Google? it hangs? cc.bw.Flush() if cc.werr != nil { return nil, cc.werr } // Read the obligatory SETTINGS frame f, err := cc.fr.ReadFrame() if err != nil { return nil, err } sf, ok := f.(*http2.SettingsFrame) if !ok { return nil, fmt.Errorf("expected settings frame, got: %T", f) } cc.fr.WriteSettingsAck() cc.bw.Flush() sf.ForeachSetting(func(s http2.Setting) error { switch s.ID { case http2.SettingMaxFrameSize: cc.maxFrameSize = s.Val case http2.SettingMaxConcurrentStreams: cc.maxConcurrentStreams = s.Val case http2.SettingInitialWindowSize: cc.initialWindowSize = s.Val default: // TODO(bradfitz): handle more log.Printf("Unhandled Setting: %v", s) } return nil }) // TODO: figure out henc size cc.hdec = hpack.NewDecoder(initialHeaderTableSize, cc.onNewHeaderField) go cc.readLoop() return cc, nil }
func custom() { dest := "localhost" port := ":5500" // Create a new client. tlscfg := &tls.Config{ ServerName: dest, NextProtos: []string{http2.NextProtoTLS}, InsecureSkipVerify: true, } conn, err := tls.Dial("tcp", dest+port, tlscfg) if err != nil { panic(err) } defer conn.Close() conn.Handshake() state := conn.ConnectionState() fmt.Printf("Protocol is : %q\n", state.NegotiatedProtocol) if _, err := io.WriteString(conn, http2.ClientPreface); err != nil { fmt.Printf("Preface failed: %s", err) return } var hbuf bytes.Buffer framer := http2.NewFramer(conn, conn) enc := hpack.NewEncoder(&hbuf) writeHeader(enc, ":authority", "localhost") writeHeader(enc, ":method", "GET") writeHeader(enc, ":path", "/ping") writeHeader(enc, ":scheme", "https") writeHeader(enc, "Accept", "*/*") if len(hbuf.Bytes()) > 16<<10 { fmt.Printf("Need CONTINUATION\n") } headers := http2.HeadersFrameParam{ StreamID: 1, EndStream: true, EndHeaders: true, BlockFragment: hbuf.Bytes(), } fmt.Printf("All the stuff: %q\n", headers.BlockFragment) go listen(framer) framer.WriteSettings() framer.WriteWindowUpdate(0, 1<<30) framer.WriteSettingsAck() time.Sleep(time.Second * 2) framer.WriteHeaders(headers) time.Sleep(time.Second * 2) /* A ping HTTP request var payload [8]byte copy(payload[:], "_c0ffee_") framer.WritePing(false, payload) rawpong, err := framer.ReadFrame() if err != nil { panic(err) } pong, ok := rawpong.(*http2.PingFrame) if !ok { fmt.Printf("Instead of a Ping, I got this: %v\n", pong) return } fmt.Printf("Pong: %q\n", pong.Data) */ }
func TestContinuation(ctx *Context) { PrintHeader("6.10. CONTINUATION", 0) func(ctx *Context) { desc := "Sends a CONTINUATION frame" msg := "The endpoint must accept the frame." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("x-dummy1", GetDummyData(10000)), pair("x-dummy2", GetDummyData(10000)), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var blockFragment = buf.Bytes() var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = false hp.BlockFragment = blockFragment[0:16384] http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteContinuation(1, true, blockFragment[16384:]) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: _, ok := f.(*http2.HeadersFrame) if ok { result = true break loop } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends multiple CONTINUATION frames" msg := "The endpoint must accept the frames." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("x-dummy1", GetDummyData(10000)), pair("x-dummy2", GetDummyData(10000)), pair("x-dummy3", GetDummyData(10000)), pair("x-dummy4", GetDummyData(10000)), pair("x-dummy5", GetDummyData(10000)), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var blockFragment = buf.Bytes() var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = false hp.BlockFragment = blockFragment[0:16384] http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteContinuation(1, false, blockFragment[16384:32767]) http2Conn.fr.WriteContinuation(1, true, blockFragment[32767:]) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: _, ok := f.(*http2.HeadersFrame) if ok { result = true break loop } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a CONTINUATION frame followed by any frame other than CONTINUATION" msg := "The endpoint MUST treat as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("x-dummy1", GetDummyData(10000)), pair("x-dummy2", GetDummyData(10000)), pair("x-dummy3", GetDummyData(10000)), pair("x-dummy4", GetDummyData(10000)), pair("x-dummy5", GetDummyData(10000)), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var blockFragment = buf.Bytes() var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = false hp.BlockFragment = blockFragment[0:16384] http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteContinuation(1, false, blockFragment[16384:32767]) http2Conn.fr.WriteData(1, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a CONTINUATION frame followed by a frame on a different stream" msg := "The endpoint MUST treat as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("x-dummy1", GetDummyData(10000)), pair("x-dummy2", GetDummyData(10000)), pair("x-dummy3", GetDummyData(10000)), pair("x-dummy4", GetDummyData(10000)), pair("x-dummy5", GetDummyData(10000)), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var blockFragment = buf.Bytes() var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = false hp.BlockFragment = blockFragment[0:16384] http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteContinuation(1, false, blockFragment[16384:32767]) http2Conn.fr.WriteContinuation(3, true, blockFragment[32767:]) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a CONTINUATION frame with the stream identifier that is 0x0" msg := "The endpoint MUST treat as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), pair("x-dummy1", GetDummyData(10000)), pair("x-dummy2", GetDummyData(10000)), pair("x-dummy3", GetDummyData(10000)), pair("x-dummy4", GetDummyData(10000)), pair("x-dummy5", GetDummyData(10000)), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var blockFragment = buf.Bytes() var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = false hp.BlockFragment = blockFragment[0:16384] http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteContinuation(1, false, blockFragment[16384:32767]) http2Conn.fr.WriteContinuation(0, true, blockFragment[32767:]) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a CONTINUATION frame after the frame other than HEADERS, PUSH_PROMISE or CONTINUATION" msg := "The endpoint MUST treat as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) http2Conn.fr.WriteContinuation(1, true, buf.Bytes()) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true break loop } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) PrintFooter() }
// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2 // and starts to receive messages on it. Non-nil error returns if construction // fails. func newHTTP2Client(addr string, opts *ConnectOptions) (_ ClientTransport, err error) { if opts.Dialer == nil { // Set the default Dialer. opts.Dialer = func(addr string, timeout time.Duration) (net.Conn, error) { return net.DialTimeout("tcp", addr, timeout) } } scheme := "http" startT := time.Now() timeout := opts.Timeout conn, connErr := opts.Dialer(addr, timeout) if connErr != nil { return nil, ConnectionErrorf("transport: %v", connErr) } var authInfo credentials.AuthInfo for _, c := range opts.AuthOptions { if ccreds, ok := c.(credentials.TransportAuthenticator); ok { scheme = "https" // TODO(zhaoq): Now the first TransportAuthenticator is used if there are // multiple ones provided. Revisit this if it is not appropriate. Probably // place the ClientTransport construction into a separate function to make // things clear. if timeout > 0 { timeout -= time.Since(startT) } conn, authInfo, connErr = ccreds.ClientHandshake(addr, conn, timeout) break } } if connErr != nil { return nil, ConnectionErrorf("transport: %v", connErr) } defer func() { if err != nil { conn.Close() } }() // Send connection preface to server. n, err := conn.Write(clientPreface) if err != nil { return nil, ConnectionErrorf("transport: %v", err) } if n != len(clientPreface) { return nil, ConnectionErrorf("transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface)) } framer := newFramer(conn) if initialWindowSize != defaultWindowSize { err = framer.writeSettings(true, http2.Setting{http2.SettingInitialWindowSize, uint32(initialWindowSize)}) } else { err = framer.writeSettings(true) } if err != nil { return nil, ConnectionErrorf("transport: %v", err) } // Adjust the connection flow control window if needed. if delta := uint32(initialConnWindowSize - defaultWindowSize); delta > 0 { if err := framer.writeWindowUpdate(true, 0, delta); err != nil { return nil, ConnectionErrorf("transport: %v", err) } } ua := primaryUA if opts.UserAgent != "" { ua = opts.UserAgent + " " + ua } var buf bytes.Buffer t := &http2Client{ target: addr, userAgent: ua, conn: conn, authInfo: authInfo, // The client initiated stream id is odd starting from 1. nextID: 1, writableChan: make(chan int, 1), shutdownChan: make(chan struct{}), errorChan: make(chan struct{}), framer: framer, hBuf: &buf, hEnc: hpack.NewEncoder(&buf), controlBuf: newRecvBuffer(), fc: &inFlow{limit: initialConnWindowSize}, sendQuotaPool: newQuotaPool(defaultWindowSize), scheme: scheme, state: reachable, activeStreams: make(map[uint32]*Stream), authCreds: opts.AuthOptions, maxStreams: math.MaxInt32, streamSendQuota: defaultWindowSize, } go t.controller() t.writableChan <- 0 // Start the reader goroutine for incoming message. The threading model // on receiving is that each transport has a dedicated goroutine which // reads HTTP2 frame from network. Then it dispatches the frame to the // corresponding stream entity. go t.reader() return t, nil }
func TestData(ctx *Context) { PrintHeader("6.1. DATA", 0) func(ctx *Context) { desc := "Sends a DATA frame with 0x0 stream identifier" msg := "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() http2Conn.fr.WriteData(0, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) func(ctx *Context) { desc := "Sends a DATA frame on the stream that is not opend" msg := "The endpoint MUST respond with a stream error of type STREAM_CLOSED." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeStreamClosed { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) // bradfitz/http2 does not support padding of DATA frame. /* func(ctx *Context) { desc := "Sends a DATA frame with invalid pad length" msg := "The endpoint MUST treat this as a connection error of type PROTOCOL_ERROR." result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 1 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) http2Conn.fr.WriteData(1, true, []byte("test")) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 0) }(ctx) */ PrintFooter() }
func TestStreamIdentifiers(ctx *Context) { PrintHeader("5.1.1. Stream Identifiers", 1) msg := "The endpoint MUST respond with a connection error of type PROTOCOL_ERROR." func(ctx *Context) { desc := "Sends even-numbered stream identifier" result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp http2.HeadersFrameParam hp.StreamID = 2 hp.EndStream = true hp.EndHeaders = true hp.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 1) }(ctx) func(ctx *Context) { desc := "Sends stream identifier that is numerically smaller than previous" result := false http2Conn := CreateHttp2Conn(ctx, true) defer http2Conn.conn.Close() var buf bytes.Buffer hdrs := []hpack.HeaderField{ pair(":method", "GET"), pair(":scheme", "http"), pair(":path", "/"), pair(":authority", ctx.Authority()), } enc := hpack.NewEncoder(&buf) for _, hf := range hdrs { _ = enc.WriteField(hf) } var hp1 http2.HeadersFrameParam hp1.StreamID = 5 hp1.EndStream = true hp1.EndHeaders = true hp1.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp1) var hp2 http2.HeadersFrameParam hp2.StreamID = 3 hp2.EndStream = true hp2.EndHeaders = true hp2.BlockFragment = buf.Bytes() http2Conn.fr.WriteHeaders(hp2) timeCh := time.After(3 * time.Second) loop: for { select { case f := <-http2Conn.dataCh: gf, ok := f.(*http2.GoAwayFrame) if ok { if gf.ErrCode == http2.ErrCodeProtocol { result = true } } case <-http2Conn.errCh: break loop case <-timeCh: break loop } } PrintResult(result, desc, msg, 1) }(ctx) }
// newServerTesterInternal creates test context. If frontendTLS is // true, set up TLS frontend connection. func newServerTesterInternal(args []string, t *testing.T, handler http.Handler, frontendTLS bool, clientConfig *tls.Config) *serverTester { ts := httptest.NewUnstartedServer(handler) backendTLS := false for _, k := range args { switch k { case "--http2-bridge": backendTLS = true } } if backendTLS { nghttp2.ConfigureServer(ts.Config, &nghttp2.Server{}) // According to httptest/server.go, we have to set // NextProtos separately for ts.TLS. NextProtos set // in nghttp2.ConfigureServer is effectively ignored. ts.TLS = new(tls.Config) ts.TLS.NextProtos = append(ts.TLS.NextProtos, "h2-14") ts.StartTLS() args = append(args, "-k") } else { ts.Start() } scheme := "http" if frontendTLS { scheme = "https" args = append(args, testDir+"/server.key", testDir+"/server.crt") } else { args = append(args, "--frontend-no-tls") } backendURL, err := url.Parse(ts.URL) if err != nil { t.Fatalf("Error parsing URL from httptest.Server: %v", err) } // URL.Host looks like "127.0.0.1:8080", but we want // "127.0.0.1,8080" b := "-b" + strings.Replace(backendURL.Host, ":", ",", -1) args = append(args, fmt.Sprintf("-f127.0.0.1,%v", serverPort), b, "--errorlog-file="+testDir+"/log.txt", "-LINFO") authority := fmt.Sprintf("127.0.0.1:%v", serverPort) st := &serverTester{ cmd: exec.Command(serverBin, args...), t: t, ts: ts, url: fmt.Sprintf("%v://%v", scheme, authority), frontendHost: fmt.Sprintf("127.0.0.1:%v", serverPort), backendHost: backendURL.Host, nextStreamID: 1, authority: authority, frCh: make(chan http2.Frame), spdyFrCh: make(chan spdy.Frame), errCh: make(chan error), } if err := st.cmd.Start(); err != nil { st.t.Fatalf("Error starting %v: %v", serverBin, err) } retry := 0 for { var conn net.Conn var err error if frontendTLS { var tlsConfig *tls.Config if clientConfig == nil { tlsConfig = new(tls.Config) } else { tlsConfig = clientConfig } tlsConfig.InsecureSkipVerify = true tlsConfig.NextProtos = []string{"h2-14", "spdy/3.1"} conn, err = tls.Dial("tcp", authority, tlsConfig) } else { conn, err = net.Dial("tcp", authority) } if err != nil { retry += 1 if retry >= 100 { st.Close() st.t.Fatalf("Error server is not responding too long; server command-line arguments may be invalid") } time.Sleep(150 * time.Millisecond) continue } if frontendTLS { tlsConn := conn.(*tls.Conn) cs := tlsConn.ConnectionState() if !cs.NegotiatedProtocolIsMutual { st.Close() st.t.Fatalf("Error negotiated next protocol is not mutual") } } st.conn = conn break } st.fr = http2.NewFramer(st.conn, st.conn) spdyFr, err := spdy.NewFramer(st.conn, st.conn) if err != nil { st.Close() st.t.Fatalf("Error spdy.NewFramer: %v", err) } st.spdyFr = spdyFr st.enc = hpack.NewEncoder(&st.headerBlkBuf) st.dec = hpack.NewDecoder(4096, func(f hpack.HeaderField) { st.header.Add(f.Name, f.Value) }) return st }