The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#) https://grpc.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

350 lines
6.7 KiB

package http2interop
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net"
"testing"
"time"
)
const (
Preface = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
)
var (
defaultTimeout = 1 * time.Second
)
type HTTP2InteropCtx struct {
// Inputs
ServerHost string
ServerPort int
UseTLS bool
UseTestCa bool
ServerHostnameOverride string
T *testing.T
// Derived
serverSpec string
authority string
rootCAs *x509.CertPool
}
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 testClientShortSettings(ctx *HTTP2InteropCtx, length int) error {
c, err := connect(ctx)
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 {
ctx.T.Log("Unable to stream frame", sf)
return err
}
for {
if _, err := parseFrame(c); err != nil {
ctx.T.Log("Unable to parse frame")
return err
}
}
return nil
}
func testClientPrefaceWithStreamId(ctx *HTTP2InteropCtx) error {
c, err := connect(ctx)
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 {
if _, err := parseFrame(c); err != nil {
return err
}
}
return nil
}
func testUnknownFrameType(ctx *HTTP2InteropCtx) error {
c, err := connect(ctx)
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 {
ctx.T.Log("Unable to stream frame", sf)
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 {
ctx.T.Log("Unable to stream frame", fh)
return err
}
}
pf := &PingFrame{
Data: []byte("01234567"),
}
if err := streamFrame(c, pf); err != nil {
ctx.T.Log("Unable to stream frame", sf)
return err
}
for {
frame, err := parseFrame(c)
if err != nil {
ctx.T.Log("Unable to parse frame")
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(ctx *HTTP2InteropCtx, prefacePrefix string) error {
c, err := connect(ctx)
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(ctx *HTTP2InteropCtx, version uint16) error {
config := buildTlsConfig(ctx)
config.MaxVersion = version
conn, err := connectWithTls(ctx, config)
if err != nil {
return err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
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(ctx *HTTP2InteropCtx) error {
config := buildTlsConfig(ctx)
config.NextProtos = []string{"h2c"}
conn, err := connectWithTls(ctx, config)
if err != nil {
return err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
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 testTLSBadCipherSuites(ctx *HTTP2InteropCtx) error {
config := buildTlsConfig(ctx)
// These are the suites that Go supports, but are forbidden by http2.
config.CipherSuites = []uint16{
tls.TLS_RSA_WITH_RC4_128_SHA,
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
}
conn, err := connectWithTls(ctx, config)
if err != nil {
return err
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(defaultTimeout))
if err := http2Connect(conn, nil); err != nil {
return err
}
for {
f, err := parseFrame(conn)
if err != nil {
return err
}
if gf, ok := f.(*GoAwayFrame); ok {
return fmt.Errorf("Got goaway frame %d", gf.Code)
}
}
return nil
}
func http2Connect(c net.Conn, sf *SettingsFrame) error {
if _, err := c.Write([]byte(Preface)); err != nil {
return err
}
if sf == nil {
sf = &SettingsFrame{}
}
if err := streamFrame(c, sf); err != nil {
return err
}
return nil
}
func connect(ctx *HTTP2InteropCtx) (net.Conn, error) {
var conn net.Conn
var err error
if !ctx.UseTLS {
conn, err = connectWithoutTls(ctx)
} else {
config := buildTlsConfig(ctx)
conn, err = connectWithTls(ctx, config)
}
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(defaultTimeout))
return conn, nil
}
func buildTlsConfig(ctx *HTTP2InteropCtx) *tls.Config {
return &tls.Config{
RootCAs: ctx.rootCAs,
NextProtos: []string{"h2"},
ServerName: ctx.authority,
MinVersion: tls.VersionTLS12,
// TODO(carl-mastrangelo): remove this once all test certificates have been updated.
InsecureSkipVerify: true,
}
}
func connectWithoutTls(ctx *HTTP2InteropCtx) (net.Conn, error) {
conn, err := net.DialTimeout("tcp", ctx.serverSpec, defaultTimeout)
if err != nil {
return nil, err
}
return conn, nil
}
func connectWithTls(ctx *HTTP2InteropCtx, config *tls.Config) (*tls.Conn, error) {
conn, err := connectWithoutTls(ctx)
if err != nil {
return nil, err
}
return tls.Client(conn, config), nil
}