|  | // 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 | 
|  | ) |