// Copyright 2019 The gRPC Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package http2interop import ( "encoding/binary" "fmt" "io" ) type FrameHeader struct { Length int Type FrameType Flags byte Reserved Reserved StreamID } type Reserved bool func (r Reserved) String() string { if r { return "R" } return "" } func (fh *FrameHeader) Parse(r io.Reader) error { buf := make([]byte, 9) if _, err := io.ReadFull(r, buf); err != nil { return err } return fh.UnmarshalBinary(buf) } func (fh *FrameHeader) UnmarshalBinary(b []byte) error { if len(b) != 9 { return fmt.Errorf("Invalid frame header length %d", len(b)) } *fh = FrameHeader{ Length: int(b[0])<<16 | int(b[1])<<8 | int(b[2]), Type: FrameType(b[3]), Flags: b[4], Reserved: Reserved(b[5]>>7 == 1), StreamID: StreamID(binary.BigEndian.Uint32(b[5:9]) & 0x7fffffff), } return nil } func (fh *FrameHeader) MarshalBinary() ([]byte, error) { buf := make([]byte, 9, 9+fh.Length) if fh.Length > 0xFFFFFF || fh.Length < 0 { return nil, fmt.Errorf("Invalid frame header length: %d", fh.Length) } if fh.StreamID < 0 { return nil, fmt.Errorf("Invalid Stream ID: %v", fh.StreamID) } buf[0], buf[1], buf[2] = byte(fh.Length>>16), byte(fh.Length>>8), byte(fh.Length) buf[3] = byte(fh.Type) buf[4] = fh.Flags var res uint32 if fh.Reserved { res = 0x80000000 } binary.BigEndian.PutUint32(buf[5:], uint32(fh.StreamID)|res) return buf, nil } type StreamID int32 type FrameType byte func (ft FrameType) String() string { switch ft { case DataFrameType: return "DATA" case HeadersFrameType: return "HEADERS" case PriorityFrameType: return "PRIORITY" case ResetStreamFrameType: return "RST_STREAM" case SettingsFrameType: return "SETTINGS" case PushPromiseFrameType: return "PUSH_PROMISE" case PingFrameType: return "PING" case GoAwayFrameType: return "GOAWAY" case WindowUpdateFrameType: return "WINDOW_UPDATE" case ContinuationFrameType: return "CONTINUATION" case HTTP1FrameType: return "HTTP/1.? (Bad)" default: return fmt.Sprintf("UNKNOWN(%d)", byte(ft)) } } // Types const ( DataFrameType FrameType = 0 HeadersFrameType FrameType = 1 PriorityFrameType FrameType = 2 ResetStreamFrameType FrameType = 3 SettingsFrameType FrameType = 4 PushPromiseFrameType FrameType = 5 PingFrameType FrameType = 6 GoAwayFrameType FrameType = 7 WindowUpdateFrameType FrameType = 8 ContinuationFrameType FrameType = 9 // HTTP1FrameType is not a real type, but rather a convenient way to check if the response // is an http response. The type of a frame header is the 4th byte, which in an http1 // response will be "HTTP/1.1 200 OK" or something like that. The character for "P" is 80. HTTP1FrameType FrameType = 80 )