Skip to content
Snippets Groups Projects
Commit 945836ea authored by Carl Mastrangelo's avatar Carl Mastrangelo
Browse files

Handle goaway and http1 responses

parent 299bcd55
No related branches found
No related tags found
No related merge requests found
...@@ -58,7 +58,11 @@ func (fh *FrameHeader) MarshalBinary() ([]byte, error) { ...@@ -58,7 +58,11 @@ func (fh *FrameHeader) MarshalBinary() ([]byte, error) {
buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length) buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length)
buf[3] = byte(fh.Type) buf[3] = byte(fh.Type)
buf[4] = fh.Flags buf[4] = fh.Flags
binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)) var res uint32
if fh.Reserved {
res = 0x80000000
}
binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res)
return buf, nil return buf, nil
} }
...@@ -89,6 +93,8 @@ func (ft FrameType) String() string { ...@@ -89,6 +93,8 @@ func (ft FrameType) String() string {
return "WINDOW_UPDATE" return "WINDOW_UPDATE"
case ContinuationFrameType: case ContinuationFrameType:
return "CONTINUATION" return "CONTINUATION"
case HTTP1FrameType:
return "HTTP/1.? (Bad)"
default: default:
return fmt.Sprintf("UNKNOWN(%d)", byte(ft)) return fmt.Sprintf("UNKNOWN(%d)", byte(ft))
} }
...@@ -106,4 +112,9 @@ const ( ...@@ -106,4 +112,9 @@ const (
GoAwayFrameType FrameType = 7 GoAwayFrameType FrameType = 7
WindowUpdateFrameType FrameType = 8 WindowUpdateFrameType FrameType = 8
ContinuationFrameType FrameType = 9 ContinuationFrameType FrameType = 9
// HTTP1FrameType is not a real type, but rather a convenient way to check if the response
// is an http response. The type of a frame header is the 4th byte, which in an http1
// response will be "HTTP/1.1 200 OK" or something like that. The character for "P" is 80.
HTTP1FrameType FrameType = 80
) )
package http2interop
import (
"bytes"
"io"
"strings"
)
// HTTP1Frame is not a real frame, but rather a way to represent an http1.x response.
type HTTP1Frame struct {
Header FrameHeader
Data []byte
}
func (f *HTTP1Frame) GetHeader() *FrameHeader {
return &f.Header
}
func (f *HTTP1Frame) ParsePayload(r io.Reader) error {
var buf bytes.Buffer
if _, err := io.Copy(&buf, r); err != nil {
return err
}
f.Data = buf.Bytes()
return nil
}
func (f *HTTP1Frame) MarshalPayload() ([]byte, error) {
return []byte(string(f.Data)), nil
}
func (f *HTTP1Frame) MarshalBinary() ([]byte, error) {
buf, err := f.Header.MarshalBinary()
if err != nil {
return nil, err
}
buf = append(buf, f.Data...)
return buf, nil
}
func (f *HTTP1Frame) String() string {
s := string(f.Data)
parts := strings.SplitN(s, "\n", 2)
headerleft, _ := f.Header.MarshalBinary()
return strings.TrimSpace(string(headerleft) + parts[0])
}
...@@ -49,11 +49,16 @@ func parseFrame(r io.Reader) (Frame, error) { ...@@ -49,11 +49,16 @@ func parseFrame(r io.Reader) (Frame, error) {
f = &SettingsFrame{ f = &SettingsFrame{
Header: fh, Header: fh,
} }
case HTTP1FrameType:
f = &HTTP1Frame{
Header: fh,
}
default: default:
f = &UnknownFrame{ f = &UnknownFrame{
Header: fh, Header: fh,
} }
} }
if err := f.ParsePayload(r); err != nil { if err := f.ParsePayload(r); err != nil {
return nil, err return nil, err
} }
...@@ -73,13 +78,14 @@ func streamFrame(w io.Writer, f Frame) error { ...@@ -73,13 +78,14 @@ func streamFrame(w io.Writer, f Frame) error {
} }
func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error { func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error {
c, err := connect(ctx) conn, err := connect(ctx)
if err != nil { if err != nil {
return err return err
} }
defer c.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
if _, err := c.Write([]byte(Preface)); err != nil { if _, err := conn.Write([]byte(Preface)); err != nil {
return err return err
} }
...@@ -90,30 +96,28 @@ func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error { ...@@ -90,30 +96,28 @@ func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error {
}, },
Data: make([]byte, length), Data: make([]byte, length),
} }
if err := streamFrame(c, sf); err != nil { if err := streamFrame(conn, sf); err != nil {
ctx.T.Log("Unable to stream frame", sf) ctx.T.Log("Unable to stream frame", sf)
return err return err
} }
for { if _, err := expectGoAwaySoon(conn); err != nil {
if _, err := parseFrame(c); err != nil { return err
ctx.T.Log("Unable to parse frame")
return err
}
} }
return nil return nil
} }
func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error { func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error {
c, err := connect(ctx) conn, err := connect(ctx)
if err != nil { if err != nil {
return err return err
} }
defer c.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
// Good so far // Good so far
if _, err := c.Write([]byte(Preface)); err != nil { if _, err := conn.Write([]byte(Preface)); err != nil {
return err return err
} }
...@@ -123,34 +127,25 @@ func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error { ...@@ -123,34 +127,25 @@ func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error {
StreamID: 1, StreamID: 1,
}, },
} }
if err := streamFrame(c, sf); err != nil { if err := streamFrame(conn, sf); err != nil {
return err return err
} }
for { if _, err := expectGoAwaySoon(conn); err != nil {
if _, err := parseFrame(c); err != nil { return err
return err
}
} }
return nil return nil
} }
func testUnknownFrameType(ctx *HTTP2InteropCtx) error { func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
c, err := connect(ctx) conn, err := connect(ctx)
if err != nil { if err != nil {
return err return err
} }
defer c.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
// Send some settings, which are part of the client preface if err := http2Connect(conn, nil); err != nil {
sf := &SettingsFrame{}
if err := streamFrame(c, sf); err != nil {
ctx.T.Log("Unable to stream frame", sf)
return err return err
} }
...@@ -161,7 +156,7 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error { ...@@ -161,7 +156,7 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
Type: ft, Type: ft,
}, },
} }
if err := streamFrame(c, fh); err != nil { if err := streamFrame(conn, fh); err != nil {
ctx.T.Log("Unable to stream frame", fh) ctx.T.Log("Unable to stream frame", fh)
return err return err
} }
...@@ -170,18 +165,19 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error { ...@@ -170,18 +165,19 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
pf := &PingFrame{ pf := &PingFrame{
Data: []byte("01234567"), Data: []byte("01234567"),
} }
if err := streamFrame(c, pf); err != nil { if err := streamFrame(conn, pf); err != nil {
ctx.T.Log("Unable to stream frame", sf) ctx.T.Log("Unable to stream frame", pf)
return err return err
} }
for { for {
frame, err := parseFrame(c) frame, err := parseFrame(conn)
if err != nil { if err != nil {
ctx.T.Log("Unable to parse frame") ctx.T.Log("Unable to parse frame", err)
return err return err
} }
if npf, ok := frame.(*PingFrame); !ok { if npf, ok := frame.(*PingFrame); !ok {
ctx.T.Log("Got frame", frame.GetHeader().Type)
continue continue
} else { } else {
if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 { if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 {
...@@ -195,21 +191,22 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error { ...@@ -195,21 +191,22 @@ func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
} }
func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error { func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error {
c, err := connect(ctx) conn, err := connect(ctx)
if err != nil { if err != nil {
return err return err
} }
defer c.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
if _, err := c.Write([]byte(prefacePrefix)); err != nil { if _, err := conn.Write([]byte(prefacePrefix)); err != nil {
return err return err
} }
buf := make([]byte, 256) if _, err := expectGoAwaySoon(conn); err != nil {
for ; err == nil; _, err = c.Read(buf) { return err
} }
// TODO: maybe check for a GOAWAY?
return err return nil
} }
func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error { func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error {
...@@ -222,13 +219,18 @@ func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error { ...@@ -222,13 +219,18 @@ func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error {
defer conn.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout)) conn.SetDeadline(time.Now().Add(defaultTimeout))
buf := make([]byte, 256) if err := http2Connect(conn, nil); err != nil {
if n, err := conn.Read(buf); err != nil { return err
if n != 0 { }
return fmt.Errorf("Expected no bytes to be read, but was %d", n)
} gf, err := expectGoAway(conn)
if err != nil {
return err return err
} }
// TODO: make an enum out of this
if gf.Code != 0xC {
return fmt.Errorf("Expected an Inadequate security code: %v", gf)
}
return nil return nil
} }
...@@ -242,13 +244,18 @@ func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error { ...@@ -242,13 +244,18 @@ func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error {
defer conn.Close() defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout)) conn.SetDeadline(time.Now().Add(defaultTimeout))
buf := make([]byte, 256) if err := http2Connect(conn, nil); err != nil {
if n, err := conn.Read(buf); err != nil { return err
if n != 0 { }
return fmt.Errorf("Expected no bytes to be read, but was %d", n)
} gf, err := expectGoAway(conn)
if err != nil {
return err return err
} }
// TODO: make an enum out of this
if gf.Code != 0xC {
return fmt.Errorf("Expected an Inadequate security code: %v", gf)
}
return nil return nil
} }
...@@ -279,16 +286,44 @@ func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error { ...@@ -279,16 +286,44 @@ func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error {
return err return err
} }
gf, err := expectGoAway(conn)
if err != nil {
return err
}
// TODO: make an enum out of this
if gf.Code != 0xC {
return fmt.Errorf("Expected an Inadequate security code: %v", gf)
}
return nil
}
func expectGoAway(conn net.Conn) (*GoAwayFrame, error) {
f, err := parseFrame(conn)
if err != nil {
return nil, err
}
if gf, ok := f.(*GoAwayFrame); !ok {
return nil, fmt.Errorf("Expected GoAway Frame %+v", f)
} else {
return gf, nil
}
}
// expectGoAwaySoon checks that a GOAWAY frame eventually comes. Servers usually send
// the initial settings frames before any data has actually arrived. This function
// checks that a go away shows.
func expectGoAwaySoon(conn net.Conn) (*GoAwayFrame, error) {
for { for {
f, err := parseFrame(conn) f, err := parseFrame(conn)
if err != nil { if err != nil {
return err return nil, err
} }
if gf, ok := f.(*GoAwayFrame); ok { if gf, ok := f.(*GoAwayFrame); !ok {
return fmt.Errorf("Got goaway frame %d", gf.Code) continue
} else {
return gf, nil
} }
} }
return nil
} }
func http2Connect(c net.Conn, sf *SettingsFrame) error { func http2Connect(c net.Conn, sf *SettingsFrame) error {
...@@ -304,8 +339,29 @@ func http2Connect(c net.Conn, sf *SettingsFrame) error { ...@@ -304,8 +339,29 @@ func http2Connect(c net.Conn, sf *SettingsFrame) error {
return nil return nil
} }
func connect(ctx *HTTP2InteropCtx) (net.Conn, error) { // CapConn captures connection traffic if Log is non-nil
var conn net.Conn type CapConn struct {
net.Conn
Log func(args ...interface{})
}
func (c *CapConn) Write(data []byte) (int, error) {
if c.Log != nil {
c.Log(" SEND: ", data)
}
return c.Conn.Write(data)
}
func (c *CapConn) Read(data []byte) (int, error) {
n, err := c.Conn.Read(data)
if c.Log != nil {
c.Log(" RECV: ", data[:n], err)
}
return n, err
}
func connect(ctx *HTTP2InteropCtx) (*CapConn, error) {
var conn *CapConn
var err error var err error
if !ctx.UseTLS { if !ctx.UseTLS {
conn, err = connectWithoutTls(ctx) conn, err = connectWithoutTls(ctx)
...@@ -327,24 +383,22 @@ func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config { ...@@ -327,24 +383,22 @@ func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config {
NextProtos: []string{"h2"}, NextProtos: []string{"h2"},
ServerName: ctx.authority, ServerName: ctx.authority,
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
// TODO(carl-mastrangelo): remove this once all test certificates have been updated.
InsecureSkipVerify: true,
} }
} }
func connectWithoutTls(ctx *HTTP2InteropCtx) (net.Conn, error) { func connectWithoutTls(ctx *HTTP2InteropCtx) (*CapConn, error) {
conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout) conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return conn, nil return &CapConn{Conn: conn}, nil
} }
func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*tls.Conn, error) { func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*CapConn, error) {
conn, err := connectWithoutTls(ctx) conn, err := connectWithoutTls(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return tls.Client(conn, config), nil return &CapConn{Conn: tls.Client(conn, config)}, nil
} }
...@@ -5,7 +5,6 @@ import ( ...@@ -5,7 +5,6 @@ import (
"crypto/x509" "crypto/x509"
"flag" "flag"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"strconv" "strconv"
...@@ -68,15 +67,25 @@ func (ctx *HTTP2InteropCtx) Close() error { ...@@ -68,15 +67,25 @@ func (ctx *HTTP2InteropCtx) Close() error {
return nil return nil
} }
func TestClientShortSettings(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t)
for i := 1; i <= 5; i++ {
err := testClientShortSettings(ctx, i)
matchError(t, err, "EOF")
}
}
func TestShortPreface(t *testing.T) { func TestShortPreface(t *testing.T) {
if *testCase != "framing" { if *testCase != "framing" {
t.SkipNow() t.SkipNow()
} }
ctx := InteropCtx(t) ctx := InteropCtx(t)
for i := 0; i < len(Preface)-1; i++ { for i := 0; i < len(Preface)-1; i++ {
if err := testShortPreface(ctx, Preface[:i]+"X"); err != io.EOF { err := testShortPreface(ctx, Preface[:i]+"X")
t.Error("Expected an EOF but was", err) matchError(t, err, "EOF")
}
} }
} }
...@@ -90,13 +99,22 @@ func TestUnknownFrameType(t *testing.T) { ...@@ -90,13 +99,22 @@ func TestUnknownFrameType(t *testing.T) {
} }
} }
func TestClientPrefaceWithStreamId(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t)
err := testClientPrefaceWithStreamId(ctx)
matchError(t, err, "EOF")
}
func TestTLSApplicationProtocol(t *testing.T) { func TestTLSApplicationProtocol(t *testing.T) {
if *testCase != "tls" { if *testCase != "tls" {
t.SkipNow() t.SkipNow()
} }
ctx := InteropCtx(t) ctx := InteropCtx(t)
err := testTLSApplicationProtocol(ctx) err := testTLSApplicationProtocol(ctx)
matchError(t, err, "EOF") matchError(t, err, "EOF", "broken pipe")
} }
func TestTLSMaxVersion(t *testing.T) { func TestTLSMaxVersion(t *testing.T) {
...@@ -119,15 +137,6 @@ func TestTLSBadCipherSuites(t *testing.T) { ...@@ -119,15 +137,6 @@ func TestTLSBadCipherSuites(t *testing.T) {
matchError(t, err, "EOF", "Got goaway frame") matchError(t, err, "EOF", "Got goaway frame")
} }
func TestClientPrefaceWithStreamId(t *testing.T) {
if *testCase != "framing" {
t.SkipNow()
}
ctx := InteropCtx(t)
err := testClientPrefaceWithStreamId(ctx)
matchError(t, err, "EOF")
}
func matchError(t *testing.T, err error, matches ...string) { func matchError(t *testing.T, err error, matches ...string) {
if err == nil { if err == nil {
t.Fatal("Expected an error") t.Fatal("Expected an error")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment