• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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