1package http2interop 2 3import ( 4 "crypto/tls" 5 "crypto/x509" 6 "fmt" 7 "io" 8 "net" 9 "testing" 10 "time" 11) 12 13const ( 14 Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" 15) 16 17var ( 18 defaultTimeout = 1 * time.Second 19) 20 21type HTTP2InteropCtx struct { 22 // Inputs 23 ServerHost string 24 ServerPort int 25 UseTLS bool 26 UseTestCa bool 27 ServerHostnameOverride string 28 29 T *testing.T 30 31 // Derived 32 serverSpec string 33 authority string 34 rootCAs *x509.CertPool 35} 36 37func parseFrame(r io.Reader) (Frame, error) { 38 fh := FrameHeader{} 39 if err := fh.Parse(r); err != nil { 40 return nil, err 41 } 42 var f Frame 43 switch fh.Type { 44 case PingFrameType: 45 f = &PingFrame{ 46 Header: fh, 47 } 48 case SettingsFrameType: 49 f = &SettingsFrame{ 50 Header: fh, 51 } 52 case HTTP1FrameType: 53 f = &HTTP1Frame{ 54 Header: fh, 55 } 56 default: 57 f = &UnknownFrame{ 58 Header: fh, 59 } 60 } 61 62 if err := f.ParsePayload(r); err != nil { 63 return nil, err 64 } 65 66 return f, nil 67} 68 69func streamFrame(w io.Writer, f Frame) error { 70 raw, err := f.MarshalBinary() 71 if err != nil { 72 return err 73 } 74 if _, err := w.Write(raw); err != nil { 75 return err 76 } 77 return nil 78} 79 80func testClientShortSettings(ctx *HTTP2InteropCtx, length int) error { 81 conn, err := connect(ctx) 82 if err != nil { 83 return err 84 } 85 defer conn.Close() 86 conn.SetDeadline(time.Now().Add(defaultTimeout)) 87 88 if _, err := conn.Write([]byte(Preface)); err != nil { 89 return err 90 } 91 92 // Bad, settings, non multiple of 6 93 sf := &UnknownFrame{ 94 Header: FrameHeader{ 95 Type: SettingsFrameType, 96 }, 97 Data: make([]byte, length), 98 } 99 if err := streamFrame(conn, sf); err != nil { 100 ctx.T.Log("Unable to stream frame", sf) 101 return err 102 } 103 104 if _, err := expectGoAwaySoon(conn); err != nil { 105 return err 106 } 107 108 return nil 109} 110 111func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error { 112 conn, err := connect(ctx) 113 if err != nil { 114 return err 115 } 116 defer conn.Close() 117 conn.SetDeadline(time.Now().Add(defaultTimeout)) 118 119 // Good so far 120 if _, err := conn.Write([]byte(Preface)); err != nil { 121 return err 122 } 123 124 // Bad, settings do not have ids 125 sf := &SettingsFrame{ 126 Header: FrameHeader{ 127 StreamID: 1, 128 }, 129 } 130 if err := streamFrame(conn, sf); err != nil { 131 return err 132 } 133 134 if _, err := expectGoAwaySoon(conn); err != nil { 135 return err 136 } 137 return nil 138} 139 140func testUnknownFrameType(ctx *HTTP2InteropCtx) error { 141 conn, err := connect(ctx) 142 if err != nil { 143 return err 144 } 145 defer conn.Close() 146 conn.SetDeadline(time.Now().Add(defaultTimeout)) 147 148 if err := http2Connect(conn, nil); err != nil { 149 return err 150 } 151 152 // Write a bunch of invalid frame types. 153 // Frame number 11 is the upcoming ALTSVC frame, and should not be tested. 154 for ft := ContinuationFrameType + 2; ft != 0; ft++ { 155 fh := &UnknownFrame{ 156 Header: FrameHeader{ 157 Type: ft, 158 }, 159 } 160 if err := streamFrame(conn, fh); err != nil { 161 ctx.T.Log("Unable to stream frame", fh) 162 return err 163 } 164 } 165 166 pf := &PingFrame{ 167 Data: []byte("01234567"), 168 } 169 if err := streamFrame(conn, pf); err != nil { 170 ctx.T.Log("Unable to stream frame", pf) 171 return err 172 } 173 174 for { 175 frame, err := parseFrame(conn) 176 if err != nil { 177 ctx.T.Log("Unable to parse frame", err) 178 return err 179 } 180 if npf, ok := frame.(*PingFrame); !ok { 181 ctx.T.Log("Got frame", frame.GetHeader().Type) 182 continue 183 } else { 184 if string(npf.Data) != string(pf.Data) || npf.Header.Flags&PING_ACK == 0 { 185 return fmt.Errorf("Bad ping %+v", *npf) 186 } 187 return nil 188 } 189 } 190 191 return nil 192} 193 194func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error { 195 conn, err := connect(ctx) 196 if err != nil { 197 return err 198 } 199 defer conn.Close() 200 conn.SetDeadline(time.Now().Add(defaultTimeout)) 201 202 if _, err := conn.Write([]byte(prefacePrefix)); err != nil { 203 return err 204 } 205 206 if _, err := expectGoAwaySoon(conn); err != nil { 207 return err 208 } 209 210 return nil 211} 212 213func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error { 214 config := buildTlsConfig(ctx) 215 config.MaxVersion = version 216 conn, err := connectWithTls(ctx, config) 217 if err != nil { 218 return err 219 } 220 defer conn.Close() 221 conn.SetDeadline(time.Now().Add(defaultTimeout)) 222 223 if err := http2Connect(conn, nil); err != nil { 224 return err 225 } 226 227 gf, err := expectGoAway(conn) 228 if err != nil { 229 return err 230 } 231 // TODO: make an enum out of this 232 if gf.Code != 0xC { 233 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 234 } 235 return nil 236} 237 238func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error { 239 config := buildTlsConfig(ctx) 240 config.NextProtos = []string{"h2c"} 241 conn, err := connectWithTls(ctx, config) 242 if err != nil { 243 return err 244 } 245 defer conn.Close() 246 conn.SetDeadline(time.Now().Add(defaultTimeout)) 247 248 if err := http2Connect(conn, nil); err != nil { 249 return err 250 } 251 252 gf, err := expectGoAway(conn) 253 if err != nil { 254 return err 255 } 256 // TODO: make an enum out of this 257 if gf.Code != 0xC { 258 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 259 } 260 return nil 261} 262 263func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error { 264 config := buildTlsConfig(ctx) 265 // These are the suites that Go supports, but are forbidden by http2. 266 config.CipherSuites = []uint16{ 267 tls.TLS_RSA_WITH_RC4_128_SHA, 268 tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 269 tls.TLS_RSA_WITH_AES_128_CBC_SHA, 270 tls.TLS_RSA_WITH_AES_256_CBC_SHA, 271 tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 272 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 273 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 274 tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 275 tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 276 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 277 tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 278 } 279 conn, err := connectWithTls(ctx, config) 280 if err != nil { 281 return err 282 } 283 defer conn.Close() 284 conn.SetDeadline(time.Now().Add(defaultTimeout)) 285 286 if err := http2Connect(conn, nil); err != nil { 287 return err 288 } 289 290 gf, err := expectGoAway(conn) 291 if err != nil { 292 return err 293 } 294 // TODO: make an enum out of this 295 if gf.Code != 0xC { 296 return fmt.Errorf("Expected an Inadequate security code: %v", gf) 297 } 298 return nil 299} 300 301func expectGoAway(conn net.Conn) (*GoAwayFrame, error) { 302 f, err := parseFrame(conn) 303 if err != nil { 304 return nil, err 305 } 306 if gf, ok := f.(*GoAwayFrame); !ok { 307 return nil, fmt.Errorf("Expected GoAway Frame %+v", f) 308 } else { 309 return gf, nil 310 } 311} 312 313// expectGoAwaySoon checks that a GOAWAY frame eventually comes. Servers usually send 314// the initial settings frames before any data has actually arrived. This function 315// checks that a go away shows. 316func expectGoAwaySoon(conn net.Conn) (*GoAwayFrame, error) { 317 for { 318 f, err := parseFrame(conn) 319 if err != nil { 320 return nil, err 321 } 322 if gf, ok := f.(*GoAwayFrame); !ok { 323 continue 324 } else { 325 return gf, nil 326 } 327 } 328} 329 330func http2Connect(c net.Conn, sf *SettingsFrame) error { 331 if _, err := c.Write([]byte(Preface)); err != nil { 332 return err 333 } 334 335 if sf == nil { 336 sf = &SettingsFrame{} 337 } 338 if err := streamFrame(c, sf); err != nil { 339 return err 340 } 341 return nil 342} 343 344// CapConn captures connection traffic if Log is non-nil 345type CapConn struct { 346 net.Conn 347 Log func(args ...interface{}) 348} 349 350func (c *CapConn) Write(data []byte) (int, error) { 351 if c.Log != nil { 352 c.Log(" SEND: ", data) 353 } 354 return c.Conn.Write(data) 355} 356 357func (c *CapConn) Read(data []byte) (int, error) { 358 n, err := c.Conn.Read(data) 359 if c.Log != nil { 360 c.Log(" RECV: ", data[:n], err) 361 } 362 return n, err 363} 364 365func connect(ctx *HTTP2InteropCtx) (*CapConn, error) { 366 var conn *CapConn 367 var err error 368 if !ctx.UseTLS { 369 conn, err = connectWithoutTls(ctx) 370 } else { 371 config := buildTlsConfig(ctx) 372 conn, err = connectWithTls(ctx, config) 373 } 374 if err != nil { 375 return nil, err 376 } 377 conn.SetDeadline(time.Now().Add(defaultTimeout)) 378 379 return conn, nil 380} 381 382func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config { 383 return &tls.Config{ 384 RootCAs: ctx.rootCAs, 385 NextProtos: []string{"h2"}, 386 ServerName: ctx.authority, 387 MinVersion: tls.VersionTLS12, 388 } 389} 390 391func connectWithoutTls(ctx *HTTP2InteropCtx) (*CapConn, error) { 392 conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout) 393 if err != nil { 394 return nil, err 395 } 396 return &CapConn{Conn: conn}, nil 397} 398 399func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*CapConn, error) { 400 conn, err := connectWithoutTls(ctx) 401 if err != nil { 402 return nil, err 403 } 404 405 return &CapConn{Conn: tls.Client(conn, config)}, nil 406} 407