package http2
import (
)
const frameHeaderLen = 9
var padZeros = make([]byte, 255)
type FrameType uint8
const (
FrameData FrameType = 0x0
FrameHeaders FrameType = 0x1
FramePriority FrameType = 0x2
FrameRSTStream FrameType = 0x3
FrameSettings FrameType = 0x4
FramePushPromise FrameType = 0x5
FramePing FrameType = 0x6
FrameGoAway FrameType = 0x7
FrameWindowUpdate FrameType = 0x8
FrameContinuation FrameType = 0x9
)
var frameName = map[FrameType]string{
FrameData: "DATA",
FrameHeaders: "HEADERS",
FramePriority: "PRIORITY",
FrameRSTStream: "RST_STREAM",
FrameSettings: "SETTINGS",
FramePushPromise: "PUSH_PROMISE",
FramePing: "PING",
FrameGoAway: "GOAWAY",
FrameWindowUpdate: "WINDOW_UPDATE",
FrameContinuation: "CONTINUATION",
}
func ( FrameType) () string {
if , := frameName[]; {
return
}
return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", uint8())
}
type Flags uint8
func ( Flags) ( Flags) bool {
return ( & ) ==
}
const (
FlagDataEndStream Flags = 0x1
FlagDataPadded Flags = 0x8
FlagHeadersEndStream Flags = 0x1
FlagHeadersEndHeaders Flags = 0x4
FlagHeadersPadded Flags = 0x8
FlagHeadersPriority Flags = 0x20
FlagSettingsAck Flags = 0x1
FlagPingAck Flags = 0x1
FlagContinuationEndHeaders Flags = 0x4
FlagPushPromiseEndHeaders Flags = 0x4
FlagPushPromisePadded Flags = 0x8
)
var flagName = map[FrameType]map[Flags]string{
FrameData: {
FlagDataEndStream: "END_STREAM",
FlagDataPadded: "PADDED",
},
FrameHeaders: {
FlagHeadersEndStream: "END_STREAM",
FlagHeadersEndHeaders: "END_HEADERS",
FlagHeadersPadded: "PADDED",
FlagHeadersPriority: "PRIORITY",
},
FrameSettings: {
FlagSettingsAck: "ACK",
},
FramePing: {
FlagPingAck: "ACK",
},
FrameContinuation: {
FlagContinuationEndHeaders: "END_HEADERS",
},
FramePushPromise: {
FlagPushPromiseEndHeaders: "END_HEADERS",
FlagPushPromisePadded: "PADDED",
},
}
type frameParser func(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error)
var frameParsers = map[FrameType]frameParser{
FrameData: parseDataFrame,
FrameHeaders: parseHeadersFrame,
FramePriority: parsePriorityFrame,
FrameRSTStream: parseRSTStreamFrame,
FrameSettings: parseSettingsFrame,
FramePushPromise: parsePushPromise,
FramePing: parsePingFrame,
FrameGoAway: parseGoAwayFrame,
FrameWindowUpdate: parseWindowUpdateFrame,
FrameContinuation: parseContinuationFrame,
}
func ( FrameType) frameParser {
if := frameParsers[]; != nil {
return
}
return parseUnknownFrame
}
type FrameHeader struct {
valid bool
Type FrameType
Flags Flags
Length uint32
StreamID uint32
}
func ( FrameHeader) () FrameHeader { return }
func ( FrameHeader) () string {
var bytes.Buffer
.WriteString("[FrameHeader ")
.writeDebug(&)
.WriteByte(']')
return .String()
}
func ( FrameHeader) ( *bytes.Buffer) {
.WriteString(.Type.String())
if .Flags != 0 {
.WriteString(" flags=")
:= 0
for := uint8(0); < 8; ++ {
if .Flags&(1<<) == 0 {
continue
}
++
if > 1 {
.WriteByte('|')
}
:= flagName[.Type][Flags(1<<)]
if != "" {
.WriteString()
} else {
fmt.Fprintf(, "0x%x", 1<<)
}
}
}
if .StreamID != 0 {
fmt.Fprintf(, " stream=%d", .StreamID)
}
fmt.Fprintf(, " len=%d", .Length)
}
func ( *FrameHeader) () {
if !.valid {
panic("Frame accessor called on non-owned Frame")
}
}
func ( *FrameHeader) () { .valid = false }
var fhBytes = sync.Pool{
New: func() interface{} {
:= make([]byte, frameHeaderLen)
return &
},
}
func ( io.Reader) (FrameHeader, error) {
:= fhBytes.Get().(*[]byte)
defer fhBytes.Put()
return readFrameHeader(*, )
}
func ( []byte, io.Reader) (FrameHeader, error) {
, := io.ReadFull(, [:frameHeaderLen])
if != nil {
return FrameHeader{},
}
return FrameHeader{
Length: (uint32([0])<<16 | uint32([1])<<8 | uint32([2])),
Type: FrameType([3]),
Flags: Flags([4]),
StreamID: binary.BigEndian.Uint32([5:]) & (1<<31 - 1),
valid: true,
}, nil
}
type Frame interface {
Header() FrameHeader
invalidate()
}
type Framer struct {
r io.Reader
lastFrame Frame
errDetail error
countError func(errToken string)
lastHeaderStream uint32
maxReadSize uint32
headerBuf [frameHeaderLen]byte
getReadBuf func(size uint32) []byte
readBuf []byte
maxWriteSize uint32
w io.Writer
wbuf []byte
AllowIllegalWrites bool
AllowIllegalReads bool
ReadMetaHeaders *hpack.Decoder
MaxHeaderListSize uint32
logReads, logWrites bool
debugFramer *Framer
debugFramerBuf *bytes.Buffer
debugReadLoggerf func(string, ...interface{})
debugWriteLoggerf func(string, ...interface{})
frameCache *frameCache
}
func ( *Framer) () uint32 {
if .MaxHeaderListSize == 0 {
return 16 << 20
}
return .MaxHeaderListSize
}
func ( *Framer) ( FrameType, Flags, uint32) {
.wbuf = append(.wbuf[:0],
0,
0,
0,
byte(),
byte(),
byte(>>24),
byte(>>16),
byte(>>8),
byte())
}
func ( *Framer) () error {
:= len(.wbuf) - frameHeaderLen
if >= (1 << 24) {
return ErrFrameTooLarge
}
_ = append(.wbuf[:0],
byte(>>16),
byte(>>8),
byte())
if .logWrites {
.logWrite()
}
, := .w.Write(.wbuf)
if == nil && != len(.wbuf) {
= io.ErrShortWrite
}
return
}
func ( *Framer) () {
if .debugFramer == nil {
.debugFramerBuf = new(bytes.Buffer)
.debugFramer = NewFramer(nil, .debugFramerBuf)
.debugFramer.logReads = false
.debugFramer.AllowIllegalReads = true
}
.debugFramerBuf.Write(.wbuf)
, := .debugFramer.ReadFrame()
if != nil {
.debugWriteLoggerf("http2: Framer %p: failed to decode just-written frame", )
return
}
.debugWriteLoggerf("http2: Framer %p: wrote %v", , summarizeFrame())
}
func ( *Framer) ( byte) { .wbuf = append(.wbuf, ) }
func ( *Framer) ( []byte) { .wbuf = append(.wbuf, ...) }
func ( *Framer) ( uint16) { .wbuf = append(.wbuf, byte(>>8), byte()) }
func ( *Framer) ( uint32) {
.wbuf = append(.wbuf, byte(>>24), byte(>>16), byte(>>8), byte())
}
const (
minMaxFrameSize = 1 << 14
maxFrameSize = 1<<24 - 1
)
func ( *Framer) () {
if .frameCache != nil {
return
}
.frameCache = &frameCache{}
}
type frameCache struct {
dataFrame DataFrame
}
func ( *frameCache) () *DataFrame {
if == nil {
return &DataFrame{}
}
return &.dataFrame
}
func ( io.Writer, io.Reader) *Framer {
:= &Framer{
w: ,
r: ,
countError: func(string) {},
logReads: logFrameReads,
logWrites: logFrameWrites,
debugReadLoggerf: log.Printf,
debugWriteLoggerf: log.Printf,
}
.getReadBuf = func( uint32) []byte {
if cap(.readBuf) >= int() {
return .readBuf[:]
}
.readBuf = make([]byte, )
return .readBuf
}
.SetMaxReadFrameSize(maxFrameSize)
return
}
func ( *Framer) ( uint32) {
if > maxFrameSize {
= maxFrameSize
}
.maxReadSize =
}
func ( *Framer) () error {
return .errDetail
}
var ErrFrameTooLarge = errors.New("http2: frame too large")
func ( error) bool {
if , := .(StreamError); {
return false
}
return != nil
}
func ( *Framer) () (Frame, error) {
.errDetail = nil
if .lastFrame != nil {
.lastFrame.invalidate()
}
, := readFrameHeader(.headerBuf[:], .r)
if != nil {
return nil,
}
if .Length > .maxReadSize {
return nil, ErrFrameTooLarge
}
:= .getReadBuf(.Length)
if , := io.ReadFull(.r, ); != nil {
return nil,
}
, := typeFrameParser(.Type)(.frameCache, , .countError, )
if != nil {
if , := .(connError); {
return nil, .connError(.Code, .Reason)
}
return nil,
}
if := .checkFrameOrder(); != nil {
return nil,
}
if .logReads {
.debugReadLoggerf("http2: Framer %p: read %v", , summarizeFrame())
}
if .Type == FrameHeaders && .ReadMetaHeaders != nil {
return .readMetaFrame(.(*HeadersFrame))
}
return , nil
}
func ( *Framer) ( ErrCode, string) error {
.errDetail = errors.New()
return ConnectionError()
}
func ( *Framer) ( Frame) error {
:= .lastFrame
.lastFrame =
if .AllowIllegalReads {
return nil
}
:= .Header()
if .lastHeaderStream != 0 {
if .Type != FrameContinuation {
return .connError(ErrCodeProtocol,
fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d",
.Type, .StreamID,
.Header().Type, .lastHeaderStream))
}
if .StreamID != .lastHeaderStream {
return .connError(ErrCodeProtocol,
fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d",
.StreamID, .lastHeaderStream))
}
} else if .Type == FrameContinuation {
return .connError(ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", .StreamID))
}
switch .Type {
case FrameHeaders, FrameContinuation:
if .Flags.Has(FlagHeadersEndHeaders) {
.lastHeaderStream = 0
} else {
.lastHeaderStream = .StreamID
}
}
return nil
}
type DataFrame struct {
FrameHeader
data []byte
}
func ( *DataFrame) () bool {
return .FrameHeader.Flags.Has(FlagDataEndStream)
}
func ( *DataFrame) () []byte {
.checkValid()
return .data
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if .StreamID == 0 {
("frame_data_stream_0")
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
}
:= .getDataFrame()
.FrameHeader =
var byte
if .Flags.Has(FlagDataPadded) {
var error
, , = readByte()
if != nil {
("frame_data_pad_byte_short")
return nil,
}
}
if int() > len() {
("frame_data_pad_too_big")
return nil, connError{ErrCodeProtocol, "pad size larger than data payload"}
}
.data = [:len()-int()]
return , nil
}
var (
errStreamID = errors.New("invalid stream ID")
errDepStreamID = errors.New("invalid dependent stream ID")
errPadLength = errors.New("pad length too large")
errPadBytes = errors.New("padding bytes must all be zeros unless AllowIllegalWrites is enabled")
)
func ( uint32) bool {
return &(1<<31) == 0
}
func ( uint32) bool {
return != 0 && &(1<<31) == 0
}
func ( *Framer) ( uint32, bool, []byte) error {
return .WriteDataPadded(, , , nil)
}
func ( *Framer) ( uint32, bool, , []byte) error {
if := .startWriteDataPadded(, , , ); != nil {
return
}
return .endWrite()
}
func ( *Framer) ( uint32, bool, , []byte) error {
if !validStreamID() && !.AllowIllegalWrites {
return errStreamID
}
if len() > 0 {
if len() > 255 {
return errPadLength
}
if !.AllowIllegalWrites {
for , := range {
if != 0 {
return errPadBytes
}
}
}
}
var Flags
if {
|= FlagDataEndStream
}
if != nil {
|= FlagDataPadded
}
.startWrite(FrameData, , )
if != nil {
.wbuf = append(.wbuf, byte(len()))
}
.wbuf = append(.wbuf, ...)
.wbuf = append(.wbuf, ...)
return nil
}
type SettingsFrame struct {
FrameHeader
p []byte
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if .Flags.Has(FlagSettingsAck) && .Length > 0 {
("frame_settings_ack_with_length")
return nil, ConnectionError(ErrCodeFrameSize)
}
if .StreamID != 0 {
("frame_settings_has_stream")
return nil, ConnectionError(ErrCodeProtocol)
}
if len()%6 != 0 {
("frame_settings_mod_6")
return nil, ConnectionError(ErrCodeFrameSize)
}
:= &SettingsFrame{FrameHeader: , p: }
if , := .Value(SettingInitialWindowSize); && > (1<<31)-1 {
("frame_settings_window_size_too_big")
return nil, ConnectionError(ErrCodeFlowControl)
}
return , nil
}
func ( *SettingsFrame) () bool {
return .FrameHeader.Flags.Has(FlagSettingsAck)
}
func ( *SettingsFrame) ( SettingID) ( uint32, bool) {
.checkValid()
for := 0; < .NumSettings(); ++ {
if := .Setting(); .ID == {
return .Val, true
}
}
return 0, false
}
func ( *SettingsFrame) ( int) Setting {
:= .p
return Setting{
ID: SettingID(binary.BigEndian.Uint16([*6 : *6+2])),
Val: binary.BigEndian.Uint32([*6+2 : *6+6]),
}
}
func ( *SettingsFrame) () int { return len(.p) / 6 }
func ( *SettingsFrame) () bool {
:= .NumSettings()
if == 0 {
return false
}
if < 10 {
for := 0; < ; ++ {
:= .Setting().ID
for := + 1; < ; ++ {
:= .Setting().ID
if == {
return true
}
}
}
return false
}
:= map[SettingID]bool{}
for := 0; < ; ++ {
:= .Setting().ID
if [] {
return true
}
[] = true
}
return false
}
func ( *SettingsFrame) ( func(Setting) error) error {
.checkValid()
for := 0; < .NumSettings(); ++ {
if := (.Setting()); != nil {
return
}
}
return nil
}
func ( *Framer) ( ...Setting) error {
.startWrite(FrameSettings, 0, 0)
for , := range {
.writeUint16(uint16(.ID))
.writeUint32(.Val)
}
return .endWrite()
}
func ( *Framer) () error {
.startWrite(FrameSettings, FlagSettingsAck, 0)
return .endWrite()
}
type PingFrame struct {
FrameHeader
Data [8]byte
}
func ( *PingFrame) () bool { return .Flags.Has(FlagPingAck) }
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if len() != 8 {
("frame_ping_length")
return nil, ConnectionError(ErrCodeFrameSize)
}
if .StreamID != 0 {
("frame_ping_has_stream")
return nil, ConnectionError(ErrCodeProtocol)
}
:= &PingFrame{FrameHeader: }
copy(.Data[:], )
return , nil
}
func ( *Framer) ( bool, [8]byte) error {
var Flags
if {
= FlagPingAck
}
.startWrite(FramePing, , 0)
.writeBytes([:])
return .endWrite()
}
type GoAwayFrame struct {
FrameHeader
LastStreamID uint32
ErrCode ErrCode
debugData []byte
}
func ( *GoAwayFrame) () []byte {
.checkValid()
return .debugData
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if .StreamID != 0 {
("frame_goaway_has_stream")
return nil, ConnectionError(ErrCodeProtocol)
}
if len() < 8 {
("frame_goaway_short")
return nil, ConnectionError(ErrCodeFrameSize)
}
return &GoAwayFrame{
FrameHeader: ,
LastStreamID: binary.BigEndian.Uint32([:4]) & (1<<31 - 1),
ErrCode: ErrCode(binary.BigEndian.Uint32([4:8])),
debugData: [8:],
}, nil
}
func ( *Framer) ( uint32, ErrCode, []byte) error {
.startWrite(FrameGoAway, 0, 0)
.writeUint32( & (1<<31 - 1))
.writeUint32(uint32())
.writeBytes()
return .endWrite()
}
type UnknownFrame struct {
FrameHeader
p []byte
}
func ( *UnknownFrame) () []byte {
.checkValid()
return .p
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
return &UnknownFrame{, }, nil
}
type WindowUpdateFrame struct {
FrameHeader
Increment uint32
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if len() != 4 {
("frame_windowupdate_bad_len")
return nil, ConnectionError(ErrCodeFrameSize)
}
:= binary.BigEndian.Uint32([:4]) & 0x7fffffff
if == 0 {
if .StreamID == 0 {
("frame_windowupdate_zero_inc_conn")
return nil, ConnectionError(ErrCodeProtocol)
}
("frame_windowupdate_zero_inc_stream")
return nil, streamError(.StreamID, ErrCodeProtocol)
}
return &WindowUpdateFrame{
FrameHeader: ,
Increment: ,
}, nil
}
func ( *Framer) (, uint32) error {
if ( < 1 || > 2147483647) && !.AllowIllegalWrites {
return errors.New("illegal window increment value")
}
.startWrite(FrameWindowUpdate, 0, )
.writeUint32()
return .endWrite()
}
type HeadersFrame struct {
FrameHeader
Priority PriorityParam
headerFragBuf []byte
}
func ( *HeadersFrame) () []byte {
.checkValid()
return .headerFragBuf
}
func ( *HeadersFrame) () bool {
return .FrameHeader.Flags.Has(FlagHeadersEndHeaders)
}
func ( *HeadersFrame) () bool {
return .FrameHeader.Flags.Has(FlagHeadersEndStream)
}
func ( *HeadersFrame) () bool {
return .FrameHeader.Flags.Has(FlagHeadersPriority)
}
func ( *frameCache, FrameHeader, func(string), []byte) ( Frame, error) {
:= &HeadersFrame{
FrameHeader: ,
}
if .StreamID == 0 {
("frame_headers_zero_stream")
return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"}
}
var uint8
if .Flags.Has(FlagHeadersPadded) {
if , , = readByte(); != nil {
("frame_headers_pad_short")
return
}
}
if .Flags.Has(FlagHeadersPriority) {
var uint32
, , = readUint32()
if != nil {
("frame_headers_prio_short")
return nil,
}
.Priority.StreamDep = & 0x7fffffff
.Priority.Exclusive = ( != .Priority.StreamDep)
, .Priority.Weight, = readByte()
if != nil {
("frame_headers_prio_weight_short")
return nil,
}
}
if len()-int() < 0 {
("frame_headers_pad_too_big")
return nil, streamError(.StreamID, ErrCodeProtocol)
}
.headerFragBuf = [:len()-int()]
return , nil
}
type HeadersFrameParam struct {
StreamID uint32
BlockFragment []byte
EndStream bool
EndHeaders bool
PadLength uint8
Priority PriorityParam
}
func ( *Framer) ( HeadersFrameParam) error {
if !validStreamID(.StreamID) && !.AllowIllegalWrites {
return errStreamID
}
var Flags
if .PadLength != 0 {
|= FlagHeadersPadded
}
if .EndStream {
|= FlagHeadersEndStream
}
if .EndHeaders {
|= FlagHeadersEndHeaders
}
if !.Priority.IsZero() {
|= FlagHeadersPriority
}
.startWrite(FrameHeaders, , .StreamID)
if .PadLength != 0 {
.writeByte(.PadLength)
}
if !.Priority.IsZero() {
:= .Priority.StreamDep
if !validStreamIDOrZero() && !.AllowIllegalWrites {
return errDepStreamID
}
if .Priority.Exclusive {
|= 1 << 31
}
.writeUint32()
.writeByte(.Priority.Weight)
}
.wbuf = append(.wbuf, .BlockFragment...)
.wbuf = append(.wbuf, padZeros[:.PadLength]...)
return .endWrite()
}
type PriorityFrame struct {
FrameHeader
PriorityParam
}
type PriorityParam struct {
StreamDep uint32
Exclusive bool
Weight uint8
}
func ( PriorityParam) () bool {
return == PriorityParam{}
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if .StreamID == 0 {
("frame_priority_zero_stream")
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
}
if len() != 5 {
("frame_priority_bad_length")
return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len())}
}
:= binary.BigEndian.Uint32([:4])
:= & 0x7fffffff
return &PriorityFrame{
FrameHeader: ,
PriorityParam: PriorityParam{
Weight: [4],
StreamDep: ,
Exclusive: != ,
},
}, nil
}
func ( *Framer) ( uint32, PriorityParam) error {
if !validStreamID() && !.AllowIllegalWrites {
return errStreamID
}
if !validStreamIDOrZero(.StreamDep) {
return errDepStreamID
}
.startWrite(FramePriority, 0, )
:= .StreamDep
if .Exclusive {
|= 1 << 31
}
.writeUint32()
.writeByte(.Weight)
return .endWrite()
}
type RSTStreamFrame struct {
FrameHeader
ErrCode ErrCode
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if len() != 4 {
("frame_rststream_bad_len")
return nil, ConnectionError(ErrCodeFrameSize)
}
if .StreamID == 0 {
("frame_rststream_zero_stream")
return nil, ConnectionError(ErrCodeProtocol)
}
return &RSTStreamFrame{, ErrCode(binary.BigEndian.Uint32([:4]))}, nil
}
func ( *Framer) ( uint32, ErrCode) error {
if !validStreamID() && !.AllowIllegalWrites {
return errStreamID
}
.startWrite(FrameRSTStream, 0, )
.writeUint32(uint32())
return .endWrite()
}
type ContinuationFrame struct {
FrameHeader
headerFragBuf []byte
}
func ( *frameCache, FrameHeader, func(string), []byte) (Frame, error) {
if .StreamID == 0 {
("frame_continuation_zero_stream")
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
}
return &ContinuationFrame{, }, nil
}
func ( *ContinuationFrame) () []byte {
.checkValid()
return .headerFragBuf
}
func ( *ContinuationFrame) () bool {
return .FrameHeader.Flags.Has(FlagContinuationEndHeaders)
}
func ( *Framer) ( uint32, bool, []byte) error {
if !validStreamID() && !.AllowIllegalWrites {
return errStreamID
}
var Flags
if {
|= FlagContinuationEndHeaders
}
.startWrite(FrameContinuation, , )
.wbuf = append(.wbuf, ...)
return .endWrite()
}
type PushPromiseFrame struct {
FrameHeader
PromiseID uint32
headerFragBuf []byte
}
func ( *PushPromiseFrame) () []byte {
.checkValid()
return .headerFragBuf
}
func ( *PushPromiseFrame) () bool {
return .FrameHeader.Flags.Has(FlagPushPromiseEndHeaders)
}
func ( *frameCache, FrameHeader, func(string), []byte) ( Frame, error) {
:= &PushPromiseFrame{
FrameHeader: ,
}
if .StreamID == 0 {
("frame_pushpromise_zero_stream")
return nil, ConnectionError(ErrCodeProtocol)
}
var uint8
if .Flags.Has(FlagPushPromisePadded) {
if , , = readByte(); != nil {
("frame_pushpromise_pad_short")
return
}
}
, .PromiseID, = readUint32()
if != nil {
("frame_pushpromise_promiseid_short")
return
}
.PromiseID = .PromiseID & (1<<31 - 1)
if int() > len() {
("frame_pushpromise_pad_too_big")
return nil, ConnectionError(ErrCodeProtocol)
}
.headerFragBuf = [:len()-int()]
return , nil
}
type PushPromiseParam struct {
StreamID uint32
PromiseID uint32
BlockFragment []byte
EndHeaders bool
PadLength uint8
}
func ( *Framer) ( PushPromiseParam) error {
if !validStreamID(.StreamID) && !.AllowIllegalWrites {
return errStreamID
}
var Flags
if .PadLength != 0 {
|= FlagPushPromisePadded
}
if .EndHeaders {
|= FlagPushPromiseEndHeaders
}
.startWrite(FramePushPromise, , .StreamID)
if .PadLength != 0 {
.writeByte(.PadLength)
}
if !validStreamID(.PromiseID) && !.AllowIllegalWrites {
return errStreamID
}
.writeUint32(.PromiseID)
.wbuf = append(.wbuf, .BlockFragment...)
.wbuf = append(.wbuf, padZeros[:.PadLength]...)
return .endWrite()
}
func ( *Framer) ( FrameType, Flags, uint32, []byte) error {
.startWrite(, , )
.writeBytes()
return .endWrite()
}
func ( []byte) ( []byte, byte, error) {
if len() == 0 {
return nil, 0, io.ErrUnexpectedEOF
}
return [1:], [0], nil
}
func ( []byte) ( []byte, uint32, error) {
if len() < 4 {
return nil, 0, io.ErrUnexpectedEOF
}
return [4:], binary.BigEndian.Uint32([:4]), nil
}
type streamEnder interface {
StreamEnded() bool
}
type headersEnder interface {
HeadersEnded() bool
}
type headersOrContinuation interface {
headersEnder
HeaderBlockFragment() []byte
}
type MetaHeadersFrame struct {
*HeadersFrame
Fields []hpack.HeaderField
Truncated bool
}
func ( *MetaHeadersFrame) ( string) string {
for , := range .Fields {
if !.IsPseudo() {
return ""
}
if .Name[1:] == {
return .Value
}
}
return ""
}
func ( *MetaHeadersFrame) () []hpack.HeaderField {
for , := range .Fields {
if !.IsPseudo() {
return .Fields[:]
}
}
return nil
}
func ( *MetaHeadersFrame) () []hpack.HeaderField {
for , := range .Fields {
if !.IsPseudo() {
return .Fields[:]
}
}
return .Fields
}
func ( *MetaHeadersFrame) () error {
var , bool
:= .PseudoFields()
for , := range {
switch .Name {
case ":method", ":path", ":scheme", ":authority":
= true
case ":status":
= true
default:
return pseudoHeaderError(.Name)
}
for , := range [:] {
if .Name == .Name {
return duplicatePseudoHeaderError(.Name)
}
}
}
if && {
return errMixPseudoHeaderTypes
}
return nil
}
func ( *Framer) () int {
:= .maxHeaderListSize()
if uint32(int()) == {
return int()
}
return 0
}
func ( *Framer) ( *HeadersFrame) (*MetaHeadersFrame, error) {
if .AllowIllegalReads {
return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders")
}
:= &MetaHeadersFrame{
HeadersFrame: ,
}
var = .maxHeaderListSize()
var bool
var error
:= .ReadMetaHeaders
.SetEmitEnabled(true)
.SetMaxStringLength(.maxHeaderStringLen())
.SetEmitFunc(func( hpack.HeaderField) {
if VerboseLogs && .logReads {
.debugReadLoggerf("http2: decoded hpack field %+v", )
}
if !httpguts.ValidHeaderFieldValue(.Value) {
= headerFieldValueError(.Name)
}
:= strings.HasPrefix(.Name, ":")
if {
if {
= errPseudoAfterRegular
}
} else {
= true
if !validWireHeaderFieldName(.Name) {
= headerFieldNameError(.Name)
}
}
if != nil {
.SetEmitEnabled(false)
return
}
:= .Size()
if > {
.SetEmitEnabled(false)
.Truncated = true
return
}
-=
.Fields = append(.Fields, )
})
defer .SetEmitFunc(func( hpack.HeaderField) {})
var headersOrContinuation =
for {
:= .HeaderBlockFragment()
if , := .Write(); != nil {
return nil, ConnectionError(ErrCodeCompression)
}
if .HeadersEnded() {
break
}
if , := .ReadFrame(); != nil {
return nil,
} else {
= .(*ContinuationFrame)
}
}
.HeadersFrame.headerFragBuf = nil
.HeadersFrame.invalidate()
if := .Close(); != nil {
return nil, ConnectionError(ErrCodeCompression)
}
if != nil {
.errDetail =
if VerboseLogs {
log.Printf("http2: invalid header: %v", )
}
return nil, StreamError{.StreamID, ErrCodeProtocol, }
}
if := .checkPseudos(); != nil {
.errDetail =
if VerboseLogs {
log.Printf("http2: invalid pseudo headers: %v", )
}
return nil, StreamError{.StreamID, ErrCodeProtocol, }
}
return , nil
}
func ( Frame) string {
var bytes.Buffer
.Header().writeDebug(&)
switch f := .(type) {
case *SettingsFrame:
:= 0
.ForeachSetting(func( Setting) error {
++
if == 1 {
.WriteString(", settings:")
}
fmt.Fprintf(&, " %v=%v,", .ID, .Val)
return nil
})
if > 0 {
.Truncate(.Len() - 1)
}
case *DataFrame:
:= .Data()
const = 256
if len() > {
= [:]
}
fmt.Fprintf(&, " data=%q", )
if len(.Data()) > {
fmt.Fprintf(&, " (%d bytes omitted)", len(.Data())-)
}
case *WindowUpdateFrame:
if .StreamID == 0 {
.WriteString(" (conn)")
}
fmt.Fprintf(&, " incr=%v", .Increment)
case *PingFrame:
fmt.Fprintf(&, " ping=%q", .Data[:])
case *GoAwayFrame:
fmt.Fprintf(&, " LastStreamID=%v ErrCode=%v Debug=%q",
.LastStreamID, .ErrCode, .debugData)
case *RSTStreamFrame:
fmt.Fprintf(&, " ErrCode=%v", .ErrCode)
}
return .String()
}