OLD | NEW |
(Empty) | |
| 1 package http2interop |
| 2 |
| 3 import ( |
| 4 "crypto/tls" |
| 5 "crypto/x509" |
| 6 "fmt" |
| 7 "io" |
| 8 "net" |
| 9 "testing" |
| 10 "time" |
| 11 ) |
| 12 |
| 13 const ( |
| 14 Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n" |
| 15 ) |
| 16 |
| 17 var ( |
| 18 defaultTimeout = 1 * time.Second |
| 19 ) |
| 20 |
| 21 type 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 |
| 37 func 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 |
| 69 func 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 |
| 80 func 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 |
| 111 func 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 |
| 140 func 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 for ft := ContinuationFrameType + 1; ft != 0; ft++ { |
| 154 fh := &UnknownFrame{ |
| 155 Header: FrameHeader{ |
| 156 Type: ft, |
| 157 }, |
| 158 } |
| 159 if err := streamFrame(conn, fh); err != nil { |
| 160 ctx.T.Log("Unable to stream frame", fh) |
| 161 return err |
| 162 } |
| 163 } |
| 164 |
| 165 pf := &PingFrame{ |
| 166 Data: []byte("01234567"), |
| 167 } |
| 168 if err := streamFrame(conn, pf); err != nil { |
| 169 ctx.T.Log("Unable to stream frame", pf) |
| 170 return err |
| 171 } |
| 172 |
| 173 for { |
| 174 frame, err := parseFrame(conn) |
| 175 if err != nil { |
| 176 ctx.T.Log("Unable to parse frame", err) |
| 177 return err |
| 178 } |
| 179 if npf, ok := frame.(*PingFrame); !ok { |
| 180 ctx.T.Log("Got frame", frame.GetHeader().Type) |
| 181 continue |
| 182 } else { |
| 183 if string(npf.Data) != string(pf.Data) || npf.Header.Fla
gs&PING_ACK == 0 { |
| 184 return fmt.Errorf("Bad ping %+v", *npf) |
| 185 } |
| 186 return nil |
| 187 } |
| 188 } |
| 189 |
| 190 return nil |
| 191 } |
| 192 |
| 193 func testShortPreface(ctx *HTTP2InteropCtx, prefacePrefix string) error { |
| 194 conn, err := connect(ctx) |
| 195 if err != nil { |
| 196 return err |
| 197 } |
| 198 defer conn.Close() |
| 199 conn.SetDeadline(time.Now().Add(defaultTimeout)) |
| 200 |
| 201 if _, err := conn.Write([]byte(prefacePrefix)); err != nil { |
| 202 return err |
| 203 } |
| 204 |
| 205 if _, err := expectGoAwaySoon(conn); err != nil { |
| 206 return err |
| 207 } |
| 208 |
| 209 return nil |
| 210 } |
| 211 |
| 212 func testTLSMaxVersion(ctx *HTTP2InteropCtx, version uint16) error { |
| 213 config := buildTlsConfig(ctx) |
| 214 config.MaxVersion = version |
| 215 conn, err := connectWithTls(ctx, config) |
| 216 if err != nil { |
| 217 return err |
| 218 } |
| 219 defer conn.Close() |
| 220 conn.SetDeadline(time.Now().Add(defaultTimeout)) |
| 221 |
| 222 if err := http2Connect(conn, nil); err != nil { |
| 223 return err |
| 224 } |
| 225 |
| 226 gf, err := expectGoAway(conn) |
| 227 if err != nil { |
| 228 return err |
| 229 } |
| 230 // TODO: make an enum out of this |
| 231 if gf.Code != 0xC { |
| 232 return fmt.Errorf("Expected an Inadequate security code: %v", gf
) |
| 233 } |
| 234 return nil |
| 235 } |
| 236 |
| 237 func testTLSApplicationProtocol(ctx *HTTP2InteropCtx) error { |
| 238 config := buildTlsConfig(ctx) |
| 239 config.NextProtos = []string{"h2c"} |
| 240 conn, err := connectWithTls(ctx, config) |
| 241 if err != nil { |
| 242 return err |
| 243 } |
| 244 defer conn.Close() |
| 245 conn.SetDeadline(time.Now().Add(defaultTimeout)) |
| 246 |
| 247 if err := http2Connect(conn, nil); err != nil { |
| 248 return err |
| 249 } |
| 250 |
| 251 gf, err := expectGoAway(conn) |
| 252 if err != nil { |
| 253 return err |
| 254 } |
| 255 // TODO: make an enum out of this |
| 256 if gf.Code != 0xC { |
| 257 return fmt.Errorf("Expected an Inadequate security code: %v", gf
) |
| 258 } |
| 259 return nil |
| 260 } |
| 261 |
| 262 func testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error { |
| 263 config := buildTlsConfig(ctx) |
| 264 // These are the suites that Go supports, but are forbidden by http2. |
| 265 config.CipherSuites = []uint16{ |
| 266 tls.TLS_RSA_WITH_RC4_128_SHA, |
| 267 tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, |
| 268 tls.TLS_RSA_WITH_AES_128_CBC_SHA, |
| 269 tls.TLS_RSA_WITH_AES_256_CBC_SHA, |
| 270 tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, |
| 271 tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, |
| 272 tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, |
| 273 tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, |
| 274 tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, |
| 275 tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, |
| 276 tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, |
| 277 } |
| 278 conn, err := connectWithTls(ctx, config) |
| 279 if err != nil { |
| 280 return err |
| 281 } |
| 282 defer conn.Close() |
| 283 conn.SetDeadline(time.Now().Add(defaultTimeout)) |
| 284 |
| 285 if err := http2Connect(conn, nil); err != nil { |
| 286 return err |
| 287 } |
| 288 |
| 289 gf, err := expectGoAway(conn) |
| 290 if err != nil { |
| 291 return err |
| 292 } |
| 293 // TODO: make an enum out of this |
| 294 if gf.Code != 0xC { |
| 295 return fmt.Errorf("Expected an Inadequate security code: %v", gf
) |
| 296 } |
| 297 return nil |
| 298 } |
| 299 |
| 300 func expectGoAway(conn net.Conn) (*GoAwayFrame, error) { |
| 301 f, err := parseFrame(conn) |
| 302 if err != nil { |
| 303 return nil, err |
| 304 } |
| 305 if gf, ok := f.(*GoAwayFrame); !ok { |
| 306 return nil, fmt.Errorf("Expected GoAway Frame %+v", f) |
| 307 } else { |
| 308 return gf, nil |
| 309 } |
| 310 } |
| 311 |
| 312 // expectGoAwaySoon checks that a GOAWAY frame eventually comes. Servers usuall
y send |
| 313 // the initial settings frames before any data has actually arrived. This funct
ion |
| 314 // checks that a go away shows. |
| 315 func expectGoAwaySoon(conn net.Conn) (*GoAwayFrame, error) { |
| 316 for { |
| 317 f, err := parseFrame(conn) |
| 318 if err != nil { |
| 319 return nil, err |
| 320 } |
| 321 if gf, ok := f.(*GoAwayFrame); !ok { |
| 322 continue |
| 323 } else { |
| 324 return gf, nil |
| 325 } |
| 326 } |
| 327 } |
| 328 |
| 329 func http2Connect(c net.Conn, sf *SettingsFrame) error { |
| 330 if _, err := c.Write([]byte(Preface)); err != nil { |
| 331 return err |
| 332 } |
| 333 |
| 334 if sf == nil { |
| 335 sf = &SettingsFrame{} |
| 336 } |
| 337 if err := streamFrame(c, sf); err != nil { |
| 338 return err |
| 339 } |
| 340 return nil |
| 341 } |
| 342 |
| 343 // CapConn captures connection traffic if Log is non-nil |
| 344 type CapConn struct { |
| 345 net.Conn |
| 346 Log func(args ...interface{}) |
| 347 } |
| 348 |
| 349 func (c *CapConn) Write(data []byte) (int, error) { |
| 350 if c.Log != nil { |
| 351 c.Log(" SEND: ", data) |
| 352 } |
| 353 return c.Conn.Write(data) |
| 354 } |
| 355 |
| 356 func (c *CapConn) Read(data []byte) (int, error) { |
| 357 n, err := c.Conn.Read(data) |
| 358 if c.Log != nil { |
| 359 c.Log(" RECV: ", data[:n], err) |
| 360 } |
| 361 return n, err |
| 362 } |
| 363 |
| 364 func connect(ctx *HTTP2InteropCtx) (*CapConn, error) { |
| 365 var conn *CapConn |
| 366 var err error |
| 367 if !ctx.UseTLS { |
| 368 conn, err = connectWithoutTls(ctx) |
| 369 } else { |
| 370 config := buildTlsConfig(ctx) |
| 371 conn, err = connectWithTls(ctx, config) |
| 372 } |
| 373 if err != nil { |
| 374 return nil, err |
| 375 } |
| 376 conn.SetDeadline(time.Now().Add(defaultTimeout)) |
| 377 |
| 378 return conn, nil |
| 379 } |
| 380 |
| 381 func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config { |
| 382 return &tls.Config{ |
| 383 RootCAs: ctx.rootCAs, |
| 384 NextProtos: []string{"h2"}, |
| 385 ServerName: ctx.authority, |
| 386 MinVersion: tls.VersionTLS12, |
| 387 } |
| 388 } |
| 389 |
| 390 func connectWithoutTls(ctx *HTTP2InteropCtx) (*CapConn, error) { |
| 391 conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout) |
| 392 if err != nil { |
| 393 return nil, err |
| 394 } |
| 395 return &CapConn{Conn: conn}, nil |
| 396 } |
| 397 |
| 398 func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*CapConn, error)
{ |
| 399 conn, err := connectWithoutTls(ctx) |
| 400 if err != nil { |
| 401 return nil, err |
| 402 } |
| 403 |
| 404 return &CapConn{Conn: tls.Client(conn, config)}, nil |
| 405 } |
OLD | NEW |