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