blob: 84f6fa5c55822277ff51d100679ab0a07042f5a6 [file] [log] [blame]
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
)