• 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	"encoding/binary"
19	"fmt"
20	"io"
21)
22
23type FrameHeader struct {
24	Length   int
25	Type     FrameType
26	Flags    byte
27	Reserved Reserved
28	StreamID
29}
30
31type Reserved bool
32
33func (r Reserved) String() string {
34	if r {
35		return "R"
36	}
37	return ""
38}
39
40func (fh *FrameHeader) Parse(r io.Reader) error {
41	buf := make([]byte, 9)
42	if _, err := io.ReadFull(r, buf); err != nil {
43		return err
44	}
45	return fh.UnmarshalBinary(buf)
46}
47
48func (fh *FrameHeader) UnmarshalBinary(b []byte) error {
49	if len(b) != 9 {
50		return fmt.Errorf("Invalid frame header length %d", len(b))
51	}
52	*fh = FrameHeader{
53		Length:   int(b[0])<<16 | int(b[1])<<8 | int(b[2]),
54		Type:     FrameType(b[3]),
55		Flags:    b[4],
56		Reserved: Reserved(b[5]>>7 == 1),
57		StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff),
58	}
59	return nil
60}
61
62func (fh *FrameHeader) MarshalBinary() ([]byte, error) {
63	buf := make([]byte, 9, 9+fh.Length)
64
65	if fh.Length > 0xFFFFFF || fh.Length < 0 {
66		return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length)
67	}
68	if fh.StreamID < 0 {
69		return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID)
70	}
71
72	buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length)
73	buf[3] = byte(fh.Type)
74	buf[4] = fh.Flags
75	var res uint32
76	if fh.Reserved {
77		res = 0x80000000
78	}
79	binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res)
80
81	return buf, nil
82}
83
84type StreamID int32
85
86type FrameType byte
87
88func (ft FrameType) String() string {
89	switch ft {
90	case DataFrameType:
91		return "DATA"
92	case HeadersFrameType:
93		return "HEADERS"
94	case PriorityFrameType:
95		return "PRIORITY"
96	case ResetStreamFrameType:
97		return "RST_STREAM"
98	case SettingsFrameType:
99		return "SETTINGS"
100	case PushPromiseFrameType:
101		return "PUSH_PROMISE"
102	case PingFrameType:
103		return "PING"
104	case GoAwayFrameType:
105		return "GOAWAY"
106	case WindowUpdateFrameType:
107		return "WINDOW_UPDATE"
108	case ContinuationFrameType:
109		return "CONTINUATION"
110	case HTTP1FrameType:
111		return "HTTP/1.? (Bad)"
112	default:
113		return fmt.Sprintf("UNKNOWN(%d)", byte(ft))
114	}
115}
116
117// Types
118const (
119	DataFrameType         FrameType = 0
120	HeadersFrameType      FrameType = 1
121	PriorityFrameType     FrameType = 2
122	ResetStreamFrameType  FrameType = 3
123	SettingsFrameType     FrameType = 4
124	PushPromiseFrameType  FrameType = 5
125	PingFrameType         FrameType = 6
126	GoAwayFrameType       FrameType = 7
127	WindowUpdateFrameType FrameType = 8
128	ContinuationFrameType FrameType = 9
129
130	// HTTP1FrameType is not a real type, but rather a convenient way to check if the response
131	// is an http response.  The type of a frame header is the 4th byte, which in an http1
132	// response will be "HTTP/1.1 200 OK" or something like that.  The character for "P" is 80.
133	HTTP1FrameType FrameType = 80
134)
135