aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/http2_interop/http2interop.go
diff options
context:
space:
mode:
Diffstat (limited to 'tools/http2_interop/http2interop.go')
-rw-r--r--tools/http2_interop/http2interop.go245
1 files changed, 245 insertions, 0 deletions
diff --git a/tools/http2_interop/http2interop.go b/tools/http2_interop/http2interop.go
new file mode 100644
index 0000000000..f1bca7fe13
--- /dev/null
+++ b/tools/http2_interop/http2interop.go
@@ -0,0 +1,245 @@
+package http2interop
+
+import (
+ "crypto/tls"
+ "fmt"
+ "io"
+ "log"
+)
+
+const (
+ Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+)
+
+func parseFrame(r io.Reader) (Frame, error) {
+ fh := FrameHeader{}
+ if err := fh.Parse(r); err != nil {
+ return nil, err
+ }
+ var f Frame
+ switch fh.Type {
+ case PingFrameType:
+ f = &PingFrame{
+ Header: fh,
+ }
+ case SettingsFrameType:
+ f = &SettingsFrame{
+ Header: fh,
+ }
+ default:
+ f = &UnknownFrame{
+ Header: fh,
+ }
+ }
+ if err := f.ParsePayload(r); err != nil {
+ return nil, err
+ }
+
+ return f, nil
+}
+
+func streamFrame(w io.Writer, f Frame) error {
+ raw, err := f.MarshalBinary()
+ if err != nil {
+ return err
+ }
+ if _, err := w.Write(raw); err != nil {
+ return err
+ }
+ return nil
+}
+
+func getHttp2Conn(addr string) (*tls.Conn, error) {
+ config := &tls.Config{
+ InsecureSkipVerify: true,
+ NextProtos: []string{"h2"},
+ }
+
+ conn, err := tls.Dial("tcp", addr, config)
+ if err != nil {
+ return nil, err
+ }
+
+ return conn, nil
+}
+
+func testClientShortSettings(addr string, length int) error {
+ c, err := getHttp2Conn(addr)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ if _, err := c.Write([]byte(Preface)); err != nil {
+ return err
+ }
+
+ // Bad, settings, non multiple of 6
+ sf := &UnknownFrame{
+ Header: FrameHeader{
+ Type: SettingsFrameType,
+ },
+ Data: make([]byte, length),
+ }
+ if err := streamFrame(c, sf); err != nil {
+ return err
+ }
+
+ for {
+ frame, err := parseFrame(c)
+ if err != nil {
+ return err
+ }
+ log.Println(frame)
+ }
+
+ return nil
+}
+
+func testClientPrefaceWithStreamId(addr string) error {
+ c, err := getHttp2Conn(addr)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ // Good so far
+ if _, err := c.Write([]byte(Preface)); err != nil {
+ return err
+ }
+
+ // Bad, settings do not have ids
+ sf := &SettingsFrame{
+ Header: FrameHeader{
+ StreamID: 1,
+ },
+ }
+ if err := streamFrame(c, sf); err != nil {
+ return err
+ }
+
+ for {
+ frame, err := parseFrame(c)
+ if err != nil {
+ return err
+ }
+ log.Println(frame)
+ }
+
+ return nil
+}
+
+func testUnknownFrameType(addr string) error {
+ c, err := getHttp2Conn(addr)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ if _, err := c.Write([]byte(Preface)); err != nil {
+ return err
+ }
+
+ // Send some settings, which are part of the client preface
+ sf := &SettingsFrame{}
+ if err := streamFrame(c, sf); err != nil {
+ return err
+ }
+
+ // Write a bunch of invalid frame types.
+ for ft := ContinuationFrameType + 1; ft != 0; ft++ {
+ fh := &UnknownFrame{
+ Header: FrameHeader{
+ Type: ft,
+ },
+ }
+ if err := streamFrame(c, fh); err != nil {
+ return err
+ }
+ }
+
+ pf := &PingFrame{
+ Data: []byte("01234567"),
+ }
+ if err := streamFrame(c, pf); err != nil {
+ return err
+ }
+
+ for {
+ frame, err := parseFrame(c)
+ if err != nil {
+ return err
+ }
+ if npf, ok := frame.(*PingFrame); !ok {
+ continue
+ } else {
+ if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 {
+ return fmt.Errorf("Bad ping %+v", *npf)
+ }
+ return nil
+ }
+ }
+
+ return nil
+}
+
+func testShortPreface(addr string, prefacePrefix string) error {
+ c, err := getHttp2Conn(addr)
+ if err != nil {
+ return err
+ }
+ defer c.Close()
+
+ if _, err := c.Write([]byte(prefacePrefix)); err != nil {
+ return err
+ }
+
+ buf := make([]byte, 256)
+ for ; err == nil; _, err = c.Read(buf) {
+ }
+ // TODO: maybe check for a GOAWAY?
+ return err
+}
+
+func testTLSMaxVersion(addr string, version uint16) error {
+ config := &tls.Config{
+ InsecureSkipVerify: true,
+ NextProtos: []string{"h2"},
+ MaxVersion: version,
+ }
+ conn, err := tls.Dial("tcp", addr, config)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ buf := make([]byte, 256)
+ if n, err := conn.Read(buf); err != nil {
+ if n != 0 {
+ return fmt.Errorf("Expected no bytes to be read, but was %d", n)
+ }
+ return err
+ }
+ return nil
+}
+
+func testTLSApplicationProtocol(addr string) error {
+ config := &tls.Config{
+ InsecureSkipVerify: true,
+ NextProtos: []string{"h2c"},
+ }
+ conn, err := tls.Dial("tcp", addr, config)
+ if err != nil {
+ return err
+ }
+ defer conn.Close()
+
+ buf := make([]byte, 256)
+ if n, err := conn.Read(buf); err != nil {
+ if n != 0 {
+ return fmt.Errorf("Expected no bytes to be read, but was %d", n)
+ }
+ return err
+ }
+ return nil
+}