| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package http2 |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "errors" |
| "fmt" |
| "io" |
| "log" |
| "strings" |
| "sync" |
| |
| "golang.org/x/net/http/httpguts" |
| "golang.org/x/net/http2/hpack" |
| ) |
| |
| const frameHeaderLen = 9 |
| |
| var padZeros = make([]byte, 255) // zeros for padding |
| |
| // A FrameType is a registered frame type as defined in |
| // https://httpwg.org/specs/rfc7540.html#rfc.section.11.2 |
| 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 (t FrameType) String() string { |
| if s, ok := frameName[t]; ok { |
| return s |
| } |
| return fmt.Sprintf("UNKNOWN_FRAME_TYPE_%d", uint8(t)) |
| } |
| |
| // Flags is a bitmask of HTTP/2 flags. |
| // The meaning of flags varies depending on the frame type. |
| type Flags uint8 |
| |
| // Has reports whether f contains all (0 or more) flags in v. |
| func (f Flags) Has(v Flags) bool { |
| return (f & v) == v |
| } |
| |
| // Frame-specific FrameHeader flag bits. |
| const ( |
| // Data Frame |
| FlagDataEndStream Flags = 0x1 |
| FlagDataPadded Flags = 0x8 |
| |
| // Headers Frame |
| FlagHeadersEndStream Flags = 0x1 |
| FlagHeadersEndHeaders Flags = 0x4 |
| FlagHeadersPadded Flags = 0x8 |
| FlagHeadersPriority Flags = 0x20 |
| |
| // Settings Frame |
| FlagSettingsAck Flags = 0x1 |
| |
| // Ping Frame |
| FlagPingAck Flags = 0x1 |
| |
| // Continuation Frame |
| 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", |
| }, |
| } |
| |
| // a frameParser parses a frame given its FrameHeader and payload |
| // bytes. The length of payload will always equal fh.Length (which |
| // might be 0). |
| 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 typeFrameParser(t FrameType) frameParser { |
| if f := frameParsers[t]; f != nil { |
| return f |
| } |
| return parseUnknownFrame |
| } |
| |
| // A FrameHeader is the 9 byte header of all HTTP/2 frames. |
| // |
| // See https://httpwg.org/specs/rfc7540.html#FrameHeader |
| type FrameHeader struct { |
| valid bool // caller can access []byte fields in the Frame |
| |
| // Type is the 1 byte frame type. There are ten standard frame |
| // types, but extension frame types may be written by WriteRawFrame |
| // and will be returned by ReadFrame (as UnknownFrame). |
| Type FrameType |
| |
| // Flags are the 1 byte of 8 potential bit flags per frame. |
| // They are specific to the frame type. |
| Flags Flags |
| |
| // Length is the length of the frame, not including the 9 byte header. |
| // The maximum size is one byte less than 16MB (uint24), but only |
| // frames up to 16KB are allowed without peer agreement. |
| Length uint32 |
| |
| // StreamID is which stream this frame is for. Certain frames |
| // are not stream-specific, in which case this field is 0. |
| StreamID uint32 |
| } |
| |
| // Header returns h. It exists so FrameHeaders can be embedded in other |
| // specific frame types and implement the Frame interface. |
| func (h FrameHeader) Header() FrameHeader { return h } |
| |
| func (h FrameHeader) String() string { |
| var buf bytes.Buffer |
| buf.WriteString("[FrameHeader ") |
| h.writeDebug(&buf) |
| buf.WriteByte(']') |
| return buf.String() |
| } |
| |
| func (h FrameHeader) writeDebug(buf *bytes.Buffer) { |
| buf.WriteString(h.Type.String()) |
| if h.Flags != 0 { |
| buf.WriteString(" flags=") |
| set := 0 |
| for i := uint8(0); i < 8; i++ { |
| if h.Flags&(1<<i) == 0 { |
| continue |
| } |
| set++ |
| if set > 1 { |
| buf.WriteByte('|') |
| } |
| name := flagName[h.Type][Flags(1<<i)] |
| if name != "" { |
| buf.WriteString(name) |
| } else { |
| fmt.Fprintf(buf, "0x%x", 1<<i) |
| } |
| } |
| } |
| if h.StreamID != 0 { |
| fmt.Fprintf(buf, " stream=%d", h.StreamID) |
| } |
| fmt.Fprintf(buf, " len=%d", h.Length) |
| } |
| |
| func (h *FrameHeader) checkValid() { |
| if !h.valid { |
| panic("Frame accessor called on non-owned Frame") |
| } |
| } |
| |
| func (h *FrameHeader) invalidate() { h.valid = false } |
| |
| // frame header bytes. |
| // Used only by ReadFrameHeader. |
| var fhBytes = sync.Pool{ |
| New: func() interface{} { |
| buf := make([]byte, frameHeaderLen) |
| return &buf |
| }, |
| } |
| |
| // ReadFrameHeader reads 9 bytes from r and returns a FrameHeader. |
| // Most users should use Framer.ReadFrame instead. |
| func ReadFrameHeader(r io.Reader) (FrameHeader, error) { |
| bufp := fhBytes.Get().(*[]byte) |
| defer fhBytes.Put(bufp) |
| return readFrameHeader(*bufp, r) |
| } |
| |
| func readFrameHeader(buf []byte, r io.Reader) (FrameHeader, error) { |
| _, err := io.ReadFull(r, buf[:frameHeaderLen]) |
| if err != nil { |
| return FrameHeader{}, err |
| } |
| return FrameHeader{ |
| Length: (uint32(buf[0])<<16 | uint32(buf[1])<<8 | uint32(buf[2])), |
| Type: FrameType(buf[3]), |
| Flags: Flags(buf[4]), |
| StreamID: binary.BigEndian.Uint32(buf[5:]) & (1<<31 - 1), |
| valid: true, |
| }, nil |
| } |
| |
| // A Frame is the base interface implemented by all frame types. |
| // Callers will generally type-assert the specific frame type: |
| // *HeadersFrame, *SettingsFrame, *WindowUpdateFrame, etc. |
| // |
| // Frames are only valid until the next call to Framer.ReadFrame. |
| type Frame interface { |
| Header() FrameHeader |
| |
| // invalidate is called by Framer.ReadFrame to make this |
| // frame's buffers as being invalid, since the subsequent |
| // frame will reuse them. |
| invalidate() |
| } |
| |
| // A Framer reads and writes Frames. |
| type Framer struct { |
| r io.Reader |
| lastFrame Frame |
| errDetail error |
| |
| // countError is a non-nil func that's called on a frame parse |
| // error with some unique error path token. It's initialized |
| // from Transport.CountError or Server.CountError. |
| countError func(errToken string) |
| |
| // lastHeaderStream is non-zero if the last frame was an |
| // unfinished HEADERS/CONTINUATION. |
| lastHeaderStream uint32 |
| |
| maxReadSize uint32 |
| headerBuf [frameHeaderLen]byte |
| |
| // TODO: let getReadBuf be configurable, and use a less memory-pinning |
| // allocator in server.go to minimize memory pinned for many idle conns. |
| // Will probably also need to make frame invalidation have a hook too. |
| getReadBuf func(size uint32) []byte |
| readBuf []byte // cache for default getReadBuf |
| |
| maxWriteSize uint32 // zero means unlimited; TODO: implement |
| |
| w io.Writer |
| wbuf []byte |
| |
| // AllowIllegalWrites permits the Framer's Write methods to |
| // write frames that do not conform to the HTTP/2 spec. This |
| // permits using the Framer to test other HTTP/2 |
| // implementations' conformance to the spec. |
| // If false, the Write methods will prefer to return an error |
| // rather than comply. |
| AllowIllegalWrites bool |
| |
| // AllowIllegalReads permits the Framer's ReadFrame method |
| // to return non-compliant frames or frame orders. |
| // This is for testing and permits using the Framer to test |
| // other HTTP/2 implementations' conformance to the spec. |
| // It is not compatible with ReadMetaHeaders. |
| AllowIllegalReads bool |
| |
| // ReadMetaHeaders if non-nil causes ReadFrame to merge |
| // HEADERS and CONTINUATION frames together and return |
| // MetaHeadersFrame instead. |
| ReadMetaHeaders *hpack.Decoder |
| |
| // MaxHeaderListSize is the http2 MAX_HEADER_LIST_SIZE. |
| // It's used only if ReadMetaHeaders is set; 0 means a sane default |
| // (currently 16MB) |
| // If the limit is hit, MetaHeadersFrame.Truncated is set true. |
| MaxHeaderListSize uint32 |
| |
| // TODO: track which type of frame & with which flags was sent |
| // last. Then return an error (unless AllowIllegalWrites) if |
| // we're in the middle of a header block and a |
| // non-Continuation or Continuation on a different stream is |
| // attempted to be written. |
| |
| logReads, logWrites bool |
| |
| debugFramer *Framer // only use for logging written writes |
| debugFramerBuf *bytes.Buffer |
| debugReadLoggerf func(string, ...interface{}) |
| debugWriteLoggerf func(string, ...interface{}) |
| |
| frameCache *frameCache // nil if frames aren't reused (default) |
| } |
| |
| func (fr *Framer) maxHeaderListSize() uint32 { |
| if fr.MaxHeaderListSize == 0 { |
| return 16 << 20 // sane default, per docs |
| } |
| return fr.MaxHeaderListSize |
| } |
| |
| func (f *Framer) startWrite(ftype FrameType, flags Flags, streamID uint32) { |
| // Write the FrameHeader. |
| f.wbuf = append(f.wbuf[:0], |
| 0, // 3 bytes of length, filled in in endWrite |
| 0, |
| 0, |
| byte(ftype), |
| byte(flags), |
| byte(streamID>>24), |
| byte(streamID>>16), |
| byte(streamID>>8), |
| byte(streamID)) |
| } |
| |
| func (f *Framer) endWrite() error { |
| // Now that we know the final size, fill in the FrameHeader in |
| // the space previously reserved for it. Abuse append. |
| length := len(f.wbuf) - frameHeaderLen |
| if length >= (1 << 24) { |
| return ErrFrameTooLarge |
| } |
| _ = append(f.wbuf[:0], |
| byte(length>>16), |
| byte(length>>8), |
| byte(length)) |
| if f.logWrites { |
| f.logWrite() |
| } |
| |
| n, err := f.w.Write(f.wbuf) |
| if err == nil && n != len(f.wbuf) { |
| err = io.ErrShortWrite |
| } |
| return err |
| } |
| |
| func (f *Framer) logWrite() { |
| if f.debugFramer == nil { |
| f.debugFramerBuf = new(bytes.Buffer) |
| f.debugFramer = NewFramer(nil, f.debugFramerBuf) |
| f.debugFramer.logReads = false // we log it ourselves, saying "wrote" below |
| // Let us read anything, even if we accidentally wrote it |
| // in the wrong order: |
| f.debugFramer.AllowIllegalReads = true |
| } |
| f.debugFramerBuf.Write(f.wbuf) |
| fr, err := f.debugFramer.ReadFrame() |
| if err != nil { |
| f.debugWriteLoggerf("http2: Framer %p: failed to decode just-written frame", f) |
| return |
| } |
| f.debugWriteLoggerf("http2: Framer %p: wrote %v", f, summarizeFrame(fr)) |
| } |
| |
| func (f *Framer) writeByte(v byte) { f.wbuf = append(f.wbuf, v) } |
| func (f *Framer) writeBytes(v []byte) { f.wbuf = append(f.wbuf, v...) } |
| func (f *Framer) writeUint16(v uint16) { f.wbuf = append(f.wbuf, byte(v>>8), byte(v)) } |
| func (f *Framer) writeUint32(v uint32) { |
| f.wbuf = append(f.wbuf, byte(v>>24), byte(v>>16), byte(v>>8), byte(v)) |
| } |
| |
| const ( |
| minMaxFrameSize = 1 << 14 |
| maxFrameSize = 1<<24 - 1 |
| ) |
| |
| // SetReuseFrames allows the Framer to reuse Frames. |
| // If called on a Framer, Frames returned by calls to ReadFrame are only |
| // valid until the next call to ReadFrame. |
| func (fr *Framer) SetReuseFrames() { |
| if fr.frameCache != nil { |
| return |
| } |
| fr.frameCache = &frameCache{} |
| } |
| |
| type frameCache struct { |
| dataFrame DataFrame |
| } |
| |
| func (fc *frameCache) getDataFrame() *DataFrame { |
| if fc == nil { |
| return &DataFrame{} |
| } |
| return &fc.dataFrame |
| } |
| |
| // NewFramer returns a Framer that writes frames to w and reads them from r. |
| func NewFramer(w io.Writer, r io.Reader) *Framer { |
| fr := &Framer{ |
| w: w, |
| r: r, |
| countError: func(string) {}, |
| logReads: logFrameReads, |
| logWrites: logFrameWrites, |
| debugReadLoggerf: log.Printf, |
| debugWriteLoggerf: log.Printf, |
| } |
| fr.getReadBuf = func(size uint32) []byte { |
| if cap(fr.readBuf) >= int(size) { |
| return fr.readBuf[:size] |
| } |
| fr.readBuf = make([]byte, size) |
| return fr.readBuf |
| } |
| fr.SetMaxReadFrameSize(maxFrameSize) |
| return fr |
| } |
| |
| // SetMaxReadFrameSize sets the maximum size of a frame |
| // that will be read by a subsequent call to ReadFrame. |
| // It is the caller's responsibility to advertise this |
| // limit with a SETTINGS frame. |
| func (fr *Framer) SetMaxReadFrameSize(v uint32) { |
| if v > maxFrameSize { |
| v = maxFrameSize |
| } |
| fr.maxReadSize = v |
| } |
| |
| // ErrorDetail returns a more detailed error of the last error |
| // returned by Framer.ReadFrame. For instance, if ReadFrame |
| // returns a StreamError with code PROTOCOL_ERROR, ErrorDetail |
| // will say exactly what was invalid. ErrorDetail is not guaranteed |
| // to return a non-nil value and like the rest of the http2 package, |
| // its return value is not protected by an API compatibility promise. |
| // ErrorDetail is reset after the next call to ReadFrame. |
| func (fr *Framer) ErrorDetail() error { |
| return fr.errDetail |
| } |
| |
| // ErrFrameTooLarge is returned from Framer.ReadFrame when the peer |
| // sends a frame that is larger than declared with SetMaxReadFrameSize. |
| var ErrFrameTooLarge = errors.New("http2: frame too large") |
| |
| // terminalReadFrameError reports whether err is an unrecoverable |
| // error from ReadFrame and no other frames should be read. |
| func terminalReadFrameError(err error) bool { |
| if _, ok := err.(StreamError); ok { |
| return false |
| } |
| return err != nil |
| } |
| |
| // ReadFrame reads a single frame. The returned Frame is only valid |
| // until the next call to ReadFrame. |
| // |
| // If the frame is larger than previously set with SetMaxReadFrameSize, the |
| // returned error is ErrFrameTooLarge. Other errors may be of type |
| // ConnectionError, StreamError, or anything else from the underlying |
| // reader. |
| // |
| // If ReadFrame returns an error and a non-nil Frame, the Frame's StreamID |
| // indicates the stream responsible for the error. |
| func (fr *Framer) ReadFrame() (Frame, error) { |
| fr.errDetail = nil |
| if fr.lastFrame != nil { |
| fr.lastFrame.invalidate() |
| } |
| fh, err := readFrameHeader(fr.headerBuf[:], fr.r) |
| if err != nil { |
| return nil, err |
| } |
| if fh.Length > fr.maxReadSize { |
| return nil, ErrFrameTooLarge |
| } |
| payload := fr.getReadBuf(fh.Length) |
| if _, err := io.ReadFull(fr.r, payload); err != nil { |
| return nil, err |
| } |
| f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, fr.countError, payload) |
| if err != nil { |
| if ce, ok := err.(connError); ok { |
| return nil, fr.connError(ce.Code, ce.Reason) |
| } |
| return nil, err |
| } |
| if err := fr.checkFrameOrder(f); err != nil { |
| return nil, err |
| } |
| if fr.logReads { |
| fr.debugReadLoggerf("http2: Framer %p: read %v", fr, summarizeFrame(f)) |
| } |
| if fh.Type == FrameHeaders && fr.ReadMetaHeaders != nil { |
| return fr.readMetaFrame(f.(*HeadersFrame)) |
| } |
| return f, nil |
| } |
| |
| // connError returns ConnectionError(code) but first |
| // stashes away a public reason to the caller can optionally relay it |
| // to the peer before hanging up on them. This might help others debug |
| // their implementations. |
| func (fr *Framer) connError(code ErrCode, reason string) error { |
| fr.errDetail = errors.New(reason) |
| return ConnectionError(code) |
| } |
| |
| // checkFrameOrder reports an error if f is an invalid frame to return |
| // next from ReadFrame. Mostly it checks whether HEADERS and |
| // CONTINUATION frames are contiguous. |
| func (fr *Framer) checkFrameOrder(f Frame) error { |
| last := fr.lastFrame |
| fr.lastFrame = f |
| if fr.AllowIllegalReads { |
| return nil |
| } |
| |
| fh := f.Header() |
| if fr.lastHeaderStream != 0 { |
| if fh.Type != FrameContinuation { |
| return fr.connError(ErrCodeProtocol, |
| fmt.Sprintf("got %s for stream %d; expected CONTINUATION following %s for stream %d", |
| fh.Type, fh.StreamID, |
| last.Header().Type, fr.lastHeaderStream)) |
| } |
| if fh.StreamID != fr.lastHeaderStream { |
| return fr.connError(ErrCodeProtocol, |
| fmt.Sprintf("got CONTINUATION for stream %d; expected stream %d", |
| fh.StreamID, fr.lastHeaderStream)) |
| } |
| } else if fh.Type == FrameContinuation { |
| return fr.connError(ErrCodeProtocol, fmt.Sprintf("unexpected CONTINUATION for stream %d", fh.StreamID)) |
| } |
| |
| switch fh.Type { |
| case FrameHeaders, FrameContinuation: |
| if fh.Flags.Has(FlagHeadersEndHeaders) { |
| fr.lastHeaderStream = 0 |
| } else { |
| fr.lastHeaderStream = fh.StreamID |
| } |
| } |
| |
| return nil |
| } |
| |
| // A DataFrame conveys arbitrary, variable-length sequences of octets |
| // associated with a stream. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.1 |
| type DataFrame struct { |
| FrameHeader |
| data []byte |
| } |
| |
| func (f *DataFrame) StreamEnded() bool { |
| return f.FrameHeader.Flags.Has(FlagDataEndStream) |
| } |
| |
| // Data returns the frame's data octets, not including any padding |
| // size byte or padding suffix bytes. |
| // The caller must not retain the returned memory past the next |
| // call to ReadFrame. |
| func (f *DataFrame) Data() []byte { |
| f.checkValid() |
| return f.data |
| } |
| |
| func parseDataFrame(fc *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { |
| if fh.StreamID == 0 { |
| // DATA frames MUST be associated with a stream. If a |
| // DATA frame is received whose stream identifier |
| // field is 0x0, the recipient MUST respond with a |
| // connection error (Section 5.4.1) of type |
| // PROTOCOL_ERROR. |
| countError("frame_data_stream_0") |
| return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"} |
| } |
| f := fc.getDataFrame() |
| f.FrameHeader = fh |
| |
| var padSize byte |
| if fh.Flags.Has(FlagDataPadded) { |
| var err error |
| payload, padSize, err = readByte(payload) |
| if err != nil { |
| countError("frame_data_pad_byte_short") |
| return nil, err |
| } |
| } |
| if int(padSize) > len(payload) { |
| // If the length of the padding is greater than the |
| // length of the frame payload, the recipient MUST |
| // treat this as a connection error. |
| // Filed: https://github.com/http2/http2-spec/issues/610 |
| countError("frame_data_pad_too_big") |
| return nil, connError{ErrCodeProtocol, "pad size larger than data payload"} |
| } |
| f.data = payload[:len(payload)-int(padSize)] |
| return f, 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 validStreamIDOrZero(streamID uint32) bool { |
| return streamID&(1<<31) == 0 |
| } |
| |
| func validStreamID(streamID uint32) bool { |
| return streamID != 0 && streamID&(1<<31) == 0 |
| } |
| |
| // WriteData writes a DATA frame. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility not to violate the maximum frame size |
| // and to not call other Write methods concurrently. |
| func (f *Framer) WriteData(streamID uint32, endStream bool, data []byte) error { |
| return f.WriteDataPadded(streamID, endStream, data, nil) |
| } |
| |
| // WriteDataPadded writes a DATA frame with optional padding. |
| // |
| // If pad is nil, the padding bit is not sent. |
| // The length of pad must not exceed 255 bytes. |
| // The bytes of pad must all be zero, unless f.AllowIllegalWrites is set. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility not to violate the maximum frame size |
| // and to not call other Write methods concurrently. |
| func (f *Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { |
| if err := f.startWriteDataPadded(streamID, endStream, data, pad); err != nil { |
| return err |
| } |
| return f.endWrite() |
| } |
| |
| // startWriteDataPadded is WriteDataPadded, but only writes the frame to the Framer's internal buffer. |
| // The caller should call endWrite to flush the frame to the underlying writer. |
| func (f *Framer) startWriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error { |
| if !validStreamID(streamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| if len(pad) > 0 { |
| if len(pad) > 255 { |
| return errPadLength |
| } |
| if !f.AllowIllegalWrites { |
| for _, b := range pad { |
| if b != 0 { |
| // "Padding octets MUST be set to zero when sending." |
| return errPadBytes |
| } |
| } |
| } |
| } |
| var flags Flags |
| if endStream { |
| flags |= FlagDataEndStream |
| } |
| if pad != nil { |
| flags |= FlagDataPadded |
| } |
| f.startWrite(FrameData, flags, streamID) |
| if pad != nil { |
| f.wbuf = append(f.wbuf, byte(len(pad))) |
| } |
| f.wbuf = append(f.wbuf, data...) |
| f.wbuf = append(f.wbuf, pad...) |
| return nil |
| } |
| |
| // A SettingsFrame conveys configuration parameters that affect how |
| // endpoints communicate, such as preferences and constraints on peer |
| // behavior. |
| // |
| // See https://httpwg.org/specs/rfc7540.html#SETTINGS |
| type SettingsFrame struct { |
| FrameHeader |
| p []byte |
| } |
| |
| func parseSettingsFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| if fh.Flags.Has(FlagSettingsAck) && fh.Length > 0 { |
| // When this (ACK 0x1) bit is set, the payload of the |
| // SETTINGS frame MUST be empty. Receipt of a |
| // SETTINGS frame with the ACK flag set and a length |
| // field value other than 0 MUST be treated as a |
| // connection error (Section 5.4.1) of type |
| // FRAME_SIZE_ERROR. |
| countError("frame_settings_ack_with_length") |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| if fh.StreamID != 0 { |
| // SETTINGS frames always apply to a connection, |
| // never a single stream. The stream identifier for a |
| // SETTINGS frame MUST be zero (0x0). If an endpoint |
| // receives a SETTINGS frame whose stream identifier |
| // field is anything other than 0x0, the endpoint MUST |
| // respond with a connection error (Section 5.4.1) of |
| // type PROTOCOL_ERROR. |
| countError("frame_settings_has_stream") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| if len(p)%6 != 0 { |
| countError("frame_settings_mod_6") |
| // Expecting even number of 6 byte settings. |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| f := &SettingsFrame{FrameHeader: fh, p: p} |
| if v, ok := f.Value(SettingInitialWindowSize); ok && v > (1<<31)-1 { |
| countError("frame_settings_window_size_too_big") |
| // Values above the maximum flow control window size of 2^31 - 1 MUST |
| // be treated as a connection error (Section 5.4.1) of type |
| // FLOW_CONTROL_ERROR. |
| return nil, ConnectionError(ErrCodeFlowControl) |
| } |
| return f, nil |
| } |
| |
| func (f *SettingsFrame) IsAck() bool { |
| return f.FrameHeader.Flags.Has(FlagSettingsAck) |
| } |
| |
| func (f *SettingsFrame) Value(id SettingID) (v uint32, ok bool) { |
| f.checkValid() |
| for i := 0; i < f.NumSettings(); i++ { |
| if s := f.Setting(i); s.ID == id { |
| return s.Val, true |
| } |
| } |
| return 0, false |
| } |
| |
| // Setting returns the setting from the frame at the given 0-based index. |
| // The index must be >= 0 and less than f.NumSettings(). |
| func (f *SettingsFrame) Setting(i int) Setting { |
| buf := f.p |
| return Setting{ |
| ID: SettingID(binary.BigEndian.Uint16(buf[i*6 : i*6+2])), |
| Val: binary.BigEndian.Uint32(buf[i*6+2 : i*6+6]), |
| } |
| } |
| |
| func (f *SettingsFrame) NumSettings() int { return len(f.p) / 6 } |
| |
| // HasDuplicates reports whether f contains any duplicate setting IDs. |
| func (f *SettingsFrame) HasDuplicates() bool { |
| num := f.NumSettings() |
| if num == 0 { |
| return false |
| } |
| // If it's small enough (the common case), just do the n^2 |
| // thing and avoid a map allocation. |
| if num < 10 { |
| for i := 0; i < num; i++ { |
| idi := f.Setting(i).ID |
| for j := i + 1; j < num; j++ { |
| idj := f.Setting(j).ID |
| if idi == idj { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| seen := map[SettingID]bool{} |
| for i := 0; i < num; i++ { |
| id := f.Setting(i).ID |
| if seen[id] { |
| return true |
| } |
| seen[id] = true |
| } |
| return false |
| } |
| |
| // ForeachSetting runs fn for each setting. |
| // It stops and returns the first error. |
| func (f *SettingsFrame) ForeachSetting(fn func(Setting) error) error { |
| f.checkValid() |
| for i := 0; i < f.NumSettings(); i++ { |
| if err := fn(f.Setting(i)); err != nil { |
| return err |
| } |
| } |
| return nil |
| } |
| |
| // WriteSettings writes a SETTINGS frame with zero or more settings |
| // specified and the ACK bit not set. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WriteSettings(settings ...Setting) error { |
| f.startWrite(FrameSettings, 0, 0) |
| for _, s := range settings { |
| f.writeUint16(uint16(s.ID)) |
| f.writeUint32(s.Val) |
| } |
| return f.endWrite() |
| } |
| |
| // WriteSettingsAck writes an empty SETTINGS frame with the ACK bit set. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WriteSettingsAck() error { |
| f.startWrite(FrameSettings, FlagSettingsAck, 0) |
| return f.endWrite() |
| } |
| |
| // A PingFrame is a mechanism for measuring a minimal round trip time |
| // from the sender, as well as determining whether an idle connection |
| // is still functional. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.7 |
| type PingFrame struct { |
| FrameHeader |
| Data [8]byte |
| } |
| |
| func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) } |
| |
| func parsePingFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { |
| if len(payload) != 8 { |
| countError("frame_ping_length") |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| if fh.StreamID != 0 { |
| countError("frame_ping_has_stream") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| f := &PingFrame{FrameHeader: fh} |
| copy(f.Data[:], payload) |
| return f, nil |
| } |
| |
| func (f *Framer) WritePing(ack bool, data [8]byte) error { |
| var flags Flags |
| if ack { |
| flags = FlagPingAck |
| } |
| f.startWrite(FramePing, flags, 0) |
| f.writeBytes(data[:]) |
| return f.endWrite() |
| } |
| |
| // A GoAwayFrame informs the remote peer to stop creating streams on this connection. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.8 |
| type GoAwayFrame struct { |
| FrameHeader |
| LastStreamID uint32 |
| ErrCode ErrCode |
| debugData []byte |
| } |
| |
| // DebugData returns any debug data in the GOAWAY frame. Its contents |
| // are not defined. |
| // The caller must not retain the returned memory past the next |
| // call to ReadFrame. |
| func (f *GoAwayFrame) DebugData() []byte { |
| f.checkValid() |
| return f.debugData |
| } |
| |
| func parseGoAwayFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| if fh.StreamID != 0 { |
| countError("frame_goaway_has_stream") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| if len(p) < 8 { |
| countError("frame_goaway_short") |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| return &GoAwayFrame{ |
| FrameHeader: fh, |
| LastStreamID: binary.BigEndian.Uint32(p[:4]) & (1<<31 - 1), |
| ErrCode: ErrCode(binary.BigEndian.Uint32(p[4:8])), |
| debugData: p[8:], |
| }, nil |
| } |
| |
| func (f *Framer) WriteGoAway(maxStreamID uint32, code ErrCode, debugData []byte) error { |
| f.startWrite(FrameGoAway, 0, 0) |
| f.writeUint32(maxStreamID & (1<<31 - 1)) |
| f.writeUint32(uint32(code)) |
| f.writeBytes(debugData) |
| return f.endWrite() |
| } |
| |
| // An UnknownFrame is the frame type returned when the frame type is unknown |
| // or no specific frame type parser exists. |
| type UnknownFrame struct { |
| FrameHeader |
| p []byte |
| } |
| |
| // Payload returns the frame's payload (after the header). It is not |
| // valid to call this method after a subsequent call to |
| // Framer.ReadFrame, nor is it valid to retain the returned slice. |
| // The memory is owned by the Framer and is invalidated when the next |
| // frame is read. |
| func (f *UnknownFrame) Payload() []byte { |
| f.checkValid() |
| return f.p |
| } |
| |
| func parseUnknownFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| return &UnknownFrame{fh, p}, nil |
| } |
| |
| // A WindowUpdateFrame is used to implement flow control. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.9 |
| type WindowUpdateFrame struct { |
| FrameHeader |
| Increment uint32 // never read with high bit set |
| } |
| |
| func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| if len(p) != 4 { |
| countError("frame_windowupdate_bad_len") |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| inc := binary.BigEndian.Uint32(p[:4]) & 0x7fffffff // mask off high reserved bit |
| if inc == 0 { |
| // A receiver MUST treat the receipt of a |
| // WINDOW_UPDATE frame with an flow control window |
| // increment of 0 as a stream error (Section 5.4.2) of |
| // type PROTOCOL_ERROR; errors on the connection flow |
| // control window MUST be treated as a connection |
| // error (Section 5.4.1). |
| if fh.StreamID == 0 { |
| countError("frame_windowupdate_zero_inc_conn") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| countError("frame_windowupdate_zero_inc_stream") |
| return nil, streamError(fh.StreamID, ErrCodeProtocol) |
| } |
| return &WindowUpdateFrame{ |
| FrameHeader: fh, |
| Increment: inc, |
| }, nil |
| } |
| |
| // WriteWindowUpdate writes a WINDOW_UPDATE frame. |
| // The increment value must be between 1 and 2,147,483,647, inclusive. |
| // If the Stream ID is zero, the window update applies to the |
| // connection as a whole. |
| func (f *Framer) WriteWindowUpdate(streamID, incr uint32) error { |
| // "The legal range for the increment to the flow control window is 1 to 2^31-1 (2,147,483,647) octets." |
| if (incr < 1 || incr > 2147483647) && !f.AllowIllegalWrites { |
| return errors.New("illegal window increment value") |
| } |
| f.startWrite(FrameWindowUpdate, 0, streamID) |
| f.writeUint32(incr) |
| return f.endWrite() |
| } |
| |
| // A HeadersFrame is used to open a stream and additionally carries a |
| // header block fragment. |
| type HeadersFrame struct { |
| FrameHeader |
| |
| // Priority is set if FlagHeadersPriority is set in the FrameHeader. |
| Priority PriorityParam |
| |
| headerFragBuf []byte // not owned |
| } |
| |
| func (f *HeadersFrame) HeaderBlockFragment() []byte { |
| f.checkValid() |
| return f.headerFragBuf |
| } |
| |
| func (f *HeadersFrame) HeadersEnded() bool { |
| return f.FrameHeader.Flags.Has(FlagHeadersEndHeaders) |
| } |
| |
| func (f *HeadersFrame) StreamEnded() bool { |
| return f.FrameHeader.Flags.Has(FlagHeadersEndStream) |
| } |
| |
| func (f *HeadersFrame) HasPriority() bool { |
| return f.FrameHeader.Flags.Has(FlagHeadersPriority) |
| } |
| |
| func parseHeadersFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) { |
| hf := &HeadersFrame{ |
| FrameHeader: fh, |
| } |
| if fh.StreamID == 0 { |
| // HEADERS frames MUST be associated with a stream. If a HEADERS frame |
| // is received whose stream identifier field is 0x0, the recipient MUST |
| // respond with a connection error (Section 5.4.1) of type |
| // PROTOCOL_ERROR. |
| countError("frame_headers_zero_stream") |
| return nil, connError{ErrCodeProtocol, "HEADERS frame with stream ID 0"} |
| } |
| var padLength uint8 |
| if fh.Flags.Has(FlagHeadersPadded) { |
| if p, padLength, err = readByte(p); err != nil { |
| countError("frame_headers_pad_short") |
| return |
| } |
| } |
| if fh.Flags.Has(FlagHeadersPriority) { |
| var v uint32 |
| p, v, err = readUint32(p) |
| if err != nil { |
| countError("frame_headers_prio_short") |
| return nil, err |
| } |
| hf.Priority.StreamDep = v & 0x7fffffff |
| hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set |
| p, hf.Priority.Weight, err = readByte(p) |
| if err != nil { |
| countError("frame_headers_prio_weight_short") |
| return nil, err |
| } |
| } |
| if len(p)-int(padLength) < 0 { |
| countError("frame_headers_pad_too_big") |
| return nil, streamError(fh.StreamID, ErrCodeProtocol) |
| } |
| hf.headerFragBuf = p[:len(p)-int(padLength)] |
| return hf, nil |
| } |
| |
| // HeadersFrameParam are the parameters for writing a HEADERS frame. |
| type HeadersFrameParam struct { |
| // StreamID is the required Stream ID to initiate. |
| StreamID uint32 |
| // BlockFragment is part (or all) of a Header Block. |
| BlockFragment []byte |
| |
| // EndStream indicates that the header block is the last that |
| // the endpoint will send for the identified stream. Setting |
| // this flag causes the stream to enter one of "half closed" |
| // states. |
| EndStream bool |
| |
| // EndHeaders indicates that this frame contains an entire |
| // header block and is not followed by any |
| // CONTINUATION frames. |
| EndHeaders bool |
| |
| // PadLength is the optional number of bytes of zeros to add |
| // to this frame. |
| PadLength uint8 |
| |
| // Priority, if non-zero, includes stream priority information |
| // in the HEADER frame. |
| Priority PriorityParam |
| } |
| |
| // WriteHeaders writes a single HEADERS frame. |
| // |
| // This is a low-level header writing method. Encoding headers and |
| // splitting them into any necessary CONTINUATION frames is handled |
| // elsewhere. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WriteHeaders(p HeadersFrameParam) error { |
| if !validStreamID(p.StreamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| var flags Flags |
| if p.PadLength != 0 { |
| flags |= FlagHeadersPadded |
| } |
| if p.EndStream { |
| flags |= FlagHeadersEndStream |
| } |
| if p.EndHeaders { |
| flags |= FlagHeadersEndHeaders |
| } |
| if !p.Priority.IsZero() { |
| flags |= FlagHeadersPriority |
| } |
| f.startWrite(FrameHeaders, flags, p.StreamID) |
| if p.PadLength != 0 { |
| f.writeByte(p.PadLength) |
| } |
| if !p.Priority.IsZero() { |
| v := p.Priority.StreamDep |
| if !validStreamIDOrZero(v) && !f.AllowIllegalWrites { |
| return errDepStreamID |
| } |
| if p.Priority.Exclusive { |
| v |= 1 << 31 |
| } |
| f.writeUint32(v) |
| f.writeByte(p.Priority.Weight) |
| } |
| f.wbuf = append(f.wbuf, p.BlockFragment...) |
| f.wbuf = append(f.wbuf, padZeros[:p.PadLength]...) |
| return f.endWrite() |
| } |
| |
| // A PriorityFrame specifies the sender-advised priority of a stream. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.3 |
| type PriorityFrame struct { |
| FrameHeader |
| PriorityParam |
| } |
| |
| // PriorityParam are the stream prioritzation parameters. |
| type PriorityParam struct { |
| // StreamDep is a 31-bit stream identifier for the |
| // stream that this stream depends on. Zero means no |
| // dependency. |
| StreamDep uint32 |
| |
| // Exclusive is whether the dependency is exclusive. |
| Exclusive bool |
| |
| // Weight is the stream's zero-indexed weight. It should be |
| // set together with StreamDep, or neither should be set. Per |
| // the spec, "Add one to the value to obtain a weight between |
| // 1 and 256." |
| Weight uint8 |
| } |
| |
| func (p PriorityParam) IsZero() bool { |
| return p == PriorityParam{} |
| } |
| |
| func parsePriorityFrame(_ *frameCache, fh FrameHeader, countError func(string), payload []byte) (Frame, error) { |
| if fh.StreamID == 0 { |
| countError("frame_priority_zero_stream") |
| return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"} |
| } |
| if len(payload) != 5 { |
| countError("frame_priority_bad_length") |
| return nil, connError{ErrCodeFrameSize, fmt.Sprintf("PRIORITY frame payload size was %d; want 5", len(payload))} |
| } |
| v := binary.BigEndian.Uint32(payload[:4]) |
| streamID := v & 0x7fffffff // mask off high bit |
| return &PriorityFrame{ |
| FrameHeader: fh, |
| PriorityParam: PriorityParam{ |
| Weight: payload[4], |
| StreamDep: streamID, |
| Exclusive: streamID != v, // was high bit set? |
| }, |
| }, nil |
| } |
| |
| // WritePriority writes a PRIORITY frame. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WritePriority(streamID uint32, p PriorityParam) error { |
| if !validStreamID(streamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| if !validStreamIDOrZero(p.StreamDep) { |
| return errDepStreamID |
| } |
| f.startWrite(FramePriority, 0, streamID) |
| v := p.StreamDep |
| if p.Exclusive { |
| v |= 1 << 31 |
| } |
| f.writeUint32(v) |
| f.writeByte(p.Weight) |
| return f.endWrite() |
| } |
| |
| // A RSTStreamFrame allows for abnormal termination of a stream. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.4 |
| type RSTStreamFrame struct { |
| FrameHeader |
| ErrCode ErrCode |
| } |
| |
| func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| if len(p) != 4 { |
| countError("frame_rststream_bad_len") |
| return nil, ConnectionError(ErrCodeFrameSize) |
| } |
| if fh.StreamID == 0 { |
| countError("frame_rststream_zero_stream") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| return &RSTStreamFrame{fh, ErrCode(binary.BigEndian.Uint32(p[:4]))}, nil |
| } |
| |
| // WriteRSTStream writes a RST_STREAM frame. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WriteRSTStream(streamID uint32, code ErrCode) error { |
| if !validStreamID(streamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| f.startWrite(FrameRSTStream, 0, streamID) |
| f.writeUint32(uint32(code)) |
| return f.endWrite() |
| } |
| |
| // A ContinuationFrame is used to continue a sequence of header block fragments. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.10 |
| type ContinuationFrame struct { |
| FrameHeader |
| headerFragBuf []byte |
| } |
| |
| func parseContinuationFrame(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (Frame, error) { |
| if fh.StreamID == 0 { |
| countError("frame_continuation_zero_stream") |
| return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"} |
| } |
| return &ContinuationFrame{fh, p}, nil |
| } |
| |
| func (f *ContinuationFrame) HeaderBlockFragment() []byte { |
| f.checkValid() |
| return f.headerFragBuf |
| } |
| |
| func (f *ContinuationFrame) HeadersEnded() bool { |
| return f.FrameHeader.Flags.Has(FlagContinuationEndHeaders) |
| } |
| |
| // WriteContinuation writes a CONTINUATION frame. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WriteContinuation(streamID uint32, endHeaders bool, headerBlockFragment []byte) error { |
| if !validStreamID(streamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| var flags Flags |
| if endHeaders { |
| flags |= FlagContinuationEndHeaders |
| } |
| f.startWrite(FrameContinuation, flags, streamID) |
| f.wbuf = append(f.wbuf, headerBlockFragment...) |
| return f.endWrite() |
| } |
| |
| // A PushPromiseFrame is used to initiate a server stream. |
| // See https://httpwg.org/specs/rfc7540.html#rfc.section.6.6 |
| type PushPromiseFrame struct { |
| FrameHeader |
| PromiseID uint32 |
| headerFragBuf []byte // not owned |
| } |
| |
| func (f *PushPromiseFrame) HeaderBlockFragment() []byte { |
| f.checkValid() |
| return f.headerFragBuf |
| } |
| |
| func (f *PushPromiseFrame) HeadersEnded() bool { |
| return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders) |
| } |
| |
| func parsePushPromise(_ *frameCache, fh FrameHeader, countError func(string), p []byte) (_ Frame, err error) { |
| pp := &PushPromiseFrame{ |
| FrameHeader: fh, |
| } |
| if pp.StreamID == 0 { |
| // PUSH_PROMISE frames MUST be associated with an existing, |
| // peer-initiated stream. The stream identifier of a |
| // PUSH_PROMISE frame indicates the stream it is associated |
| // with. If the stream identifier field specifies the value |
| // 0x0, a recipient MUST respond with a connection error |
| // (Section 5.4.1) of type PROTOCOL_ERROR. |
| countError("frame_pushpromise_zero_stream") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| // The PUSH_PROMISE frame includes optional padding. |
| // Padding fields and flags are identical to those defined for DATA frames |
| var padLength uint8 |
| if fh.Flags.Has(FlagPushPromisePadded) { |
| if p, padLength, err = readByte(p); err != nil { |
| countError("frame_pushpromise_pad_short") |
| return |
| } |
| } |
| |
| p, pp.PromiseID, err = readUint32(p) |
| if err != nil { |
| countError("frame_pushpromise_promiseid_short") |
| return |
| } |
| pp.PromiseID = pp.PromiseID & (1<<31 - 1) |
| |
| if int(padLength) > len(p) { |
| // like the DATA frame, error out if padding is longer than the body. |
| countError("frame_pushpromise_pad_too_big") |
| return nil, ConnectionError(ErrCodeProtocol) |
| } |
| pp.headerFragBuf = p[:len(p)-int(padLength)] |
| return pp, nil |
| } |
| |
| // PushPromiseParam are the parameters for writing a PUSH_PROMISE frame. |
| type PushPromiseParam struct { |
| // StreamID is the required Stream ID to initiate. |
| StreamID uint32 |
| |
| // PromiseID is the required Stream ID which this |
| // Push Promises |
| PromiseID uint32 |
| |
| // BlockFragment is part (or all) of a Header Block. |
| BlockFragment []byte |
| |
| // EndHeaders indicates that this frame contains an entire |
| // header block and is not followed by any |
| // CONTINUATION frames. |
| EndHeaders bool |
| |
| // PadLength is the optional number of bytes of zeros to add |
| // to this frame. |
| PadLength uint8 |
| } |
| |
| // WritePushPromise writes a single PushPromise Frame. |
| // |
| // As with Header Frames, This is the low level call for writing |
| // individual frames. Continuation frames are handled elsewhere. |
| // |
| // It will perform exactly one Write to the underlying Writer. |
| // It is the caller's responsibility to not call other Write methods concurrently. |
| func (f *Framer) WritePushPromise(p PushPromiseParam) error { |
| if !validStreamID(p.StreamID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| var flags Flags |
| if p.PadLength != 0 { |
| flags |= FlagPushPromisePadded |
| } |
| if p.EndHeaders { |
| flags |= FlagPushPromiseEndHeaders |
| } |
| f.startWrite(FramePushPromise, flags, p.StreamID) |
| if p.PadLength != 0 { |
| f.writeByte(p.PadLength) |
| } |
| if !validStreamID(p.PromiseID) && !f.AllowIllegalWrites { |
| return errStreamID |
| } |
| f.writeUint32(p.PromiseID) |
| f.wbuf = append(f.wbuf, p.BlockFragment...) |
| f.wbuf = append(f.wbuf, padZeros[:p.PadLength]...) |
| return f.endWrite() |
| } |
| |
| // WriteRawFrame writes a raw frame. This can be used to write |
| // extension frames unknown to this package. |
| func (f *Framer) WriteRawFrame(t FrameType, flags Flags, streamID uint32, payload []byte) error { |
| f.startWrite(t, flags, streamID) |
| f.writeBytes(payload) |
| return f.endWrite() |
| } |
| |
| func readByte(p []byte) (remain []byte, b byte, err error) { |
| if len(p) == 0 { |
| return nil, 0, io.ErrUnexpectedEOF |
| } |
| return p[1:], p[0], nil |
| } |
| |
| func readUint32(p []byte) (remain []byte, v uint32, err error) { |
| if len(p) < 4 { |
| return nil, 0, io.ErrUnexpectedEOF |
| } |
| return p[4:], binary.BigEndian.Uint32(p[:4]), nil |
| } |
| |
| type streamEnder interface { |
| StreamEnded() bool |
| } |
| |
| type headersEnder interface { |
| HeadersEnded() bool |
| } |
| |
| type headersOrContinuation interface { |
| headersEnder |
| HeaderBlockFragment() []byte |
| } |
| |
| // A MetaHeadersFrame is the representation of one HEADERS frame and |
| // zero or more contiguous CONTINUATION frames and the decoding of |
| // their HPACK-encoded contents. |
| // |
| // This type of frame does not appear on the wire and is only returned |
| // by the Framer when Framer.ReadMetaHeaders is set. |
| type MetaHeadersFrame struct { |
| *HeadersFrame |
| |
| // Fields are the fields contained in the HEADERS and |
| // CONTINUATION frames. The underlying slice is owned by the |
| // Framer and must not be retained after the next call to |
| // ReadFrame. |
| // |
| // Fields are guaranteed to be in the correct http2 order and |
| // not have unknown pseudo header fields or invalid header |
| // field names or values. Required pseudo header fields may be |
| // missing, however. Use the MetaHeadersFrame.Pseudo accessor |
| // method access pseudo headers. |
| Fields []hpack.HeaderField |
| |
| // Truncated is whether the max header list size limit was hit |
| // and Fields is incomplete. The hpack decoder state is still |
| // valid, however. |
| Truncated bool |
| } |
| |
| // PseudoValue returns the given pseudo header field's value. |
| // The provided pseudo field should not contain the leading colon. |
| func (mh *MetaHeadersFrame) PseudoValue(pseudo string) string { |
| for _, hf := range mh.Fields { |
| if !hf.IsPseudo() { |
| return "" |
| } |
| if hf.Name[1:] == pseudo { |
| return hf.Value |
| } |
| } |
| return "" |
| } |
| |
| // RegularFields returns the regular (non-pseudo) header fields of mh. |
| // The caller does not own the returned slice. |
| func (mh *MetaHeadersFrame) RegularFields() []hpack.HeaderField { |
| for i, hf := range mh.Fields { |
| if !hf.IsPseudo() { |
| return mh.Fields[i:] |
| } |
| } |
| return nil |
| } |
| |
| // PseudoFields returns the pseudo header fields of mh. |
| // The caller does not own the returned slice. |
| func (mh *MetaHeadersFrame) PseudoFields() []hpack.HeaderField { |
| for i, hf := range mh.Fields { |
| if !hf.IsPseudo() { |
| return mh.Fields[:i] |
| } |
| } |
| return mh.Fields |
| } |
| |
| func (mh *MetaHeadersFrame) checkPseudos() error { |
| var isRequest, isResponse bool |
| pf := mh.PseudoFields() |
| for i, hf := range pf { |
| switch hf.Name { |
| case ":method", ":path", ":scheme", ":authority": |
| isRequest = true |
| case ":status": |
| isResponse = true |
| default: |
| return pseudoHeaderError(hf.Name) |
| } |
| // Check for duplicates. |
| // This would be a bad algorithm, but N is 4. |
| // And this doesn't allocate. |
| for _, hf2 := range pf[:i] { |
| if hf.Name == hf2.Name { |
| return duplicatePseudoHeaderError(hf.Name) |
| } |
| } |
| } |
| if isRequest && isResponse { |
| return errMixPseudoHeaderTypes |
| } |
| return nil |
| } |
| |
| func (fr *Framer) maxHeaderStringLen() int { |
| v := fr.maxHeaderListSize() |
| if uint32(int(v)) == v { |
| return int(v) |
| } |
| // They had a crazy big number for MaxHeaderBytes anyway, |
| // so give them unlimited header lengths: |
| return 0 |
| } |
| |
| // readMetaFrame returns 0 or more CONTINUATION frames from fr and |
| // merge them into the provided hf and returns a MetaHeadersFrame |
| // with the decoded hpack values. |
| func (fr *Framer) readMetaFrame(hf *HeadersFrame) (Frame, error) { |
| if fr.AllowIllegalReads { |
| return nil, errors.New("illegal use of AllowIllegalReads with ReadMetaHeaders") |
| } |
| mh := &MetaHeadersFrame{ |
| HeadersFrame: hf, |
| } |
| var remainSize = fr.maxHeaderListSize() |
| var sawRegular bool |
| |
| var invalid error // pseudo header field errors |
| hdec := fr.ReadMetaHeaders |
| hdec.SetEmitEnabled(true) |
| hdec.SetMaxStringLength(fr.maxHeaderStringLen()) |
| hdec.SetEmitFunc(func(hf hpack.HeaderField) { |
| if VerboseLogs && fr.logReads { |
| fr.debugReadLoggerf("http2: decoded hpack field %+v", hf) |
| } |
| if !httpguts.ValidHeaderFieldValue(hf.Value) { |
| // Don't include the value in the error, because it may be sensitive. |
| invalid = headerFieldValueError(hf.Name) |
| } |
| isPseudo := strings.HasPrefix(hf.Name, ":") |
| if isPseudo { |
| if sawRegular { |
| invalid = errPseudoAfterRegular |
| } |
| } else { |
| sawRegular = true |
| if !validWireHeaderFieldName(hf.Name) { |
| invalid = headerFieldNameError(hf.Name) |
| } |
| } |
| |
| if invalid != nil { |
| hdec.SetEmitEnabled(false) |
| return |
| } |
| |
| size := hf.Size() |
| if size > remainSize { |
| hdec.SetEmitEnabled(false) |
| mh.Truncated = true |
| remainSize = 0 |
| return |
| } |
| remainSize -= size |
| |
| mh.Fields = append(mh.Fields, hf) |
| }) |
| // Lose reference to MetaHeadersFrame: |
| defer hdec.SetEmitFunc(func(hf hpack.HeaderField) {}) |
| |
| var hc headersOrContinuation = hf |
| for { |
| frag := hc.HeaderBlockFragment() |
| |
| // Avoid parsing large amounts of headers that we will then discard. |
| // If the sender exceeds the max header list size by too much, |
| // skip parsing the fragment and close the connection. |
| // |
| // "Too much" is either any CONTINUATION frame after we've already |
| // exceeded the max header list size (in which case remainSize is 0), |
| // or a frame whose encoded size is more than twice the remaining |
| // header list bytes we're willing to accept. |
| if int64(len(frag)) > int64(2*remainSize) { |
| if VerboseLogs { |
| log.Printf("http2: header list too large") |
| } |
| // It would be nice to send a RST_STREAM before sending the GOAWAY, |
| // but the structure of the server's frame writer makes this difficult. |
| return mh, ConnectionError(ErrCodeProtocol) |
| } |
| |
| // Also close the connection after any CONTINUATION frame following an |
| // invalid header, since we stop tracking the size of the headers after |
| // an invalid one. |
| if invalid != nil { |
| if VerboseLogs { |
| log.Printf("http2: invalid header: %v", invalid) |
| } |
| // It would be nice to send a RST_STREAM before sending the GOAWAY, |
| // but the structure of the server's frame writer makes this difficult. |
| return mh, ConnectionError(ErrCodeProtocol) |
| } |
| |
| if _, err := hdec.Write(frag); err != nil { |
| return mh, ConnectionError(ErrCodeCompression) |
| } |
| |
| if hc.HeadersEnded() { |
| break |
| } |
| if f, err := fr.ReadFrame(); err != nil { |
| return nil, err |
| } else { |
| hc = f.(*ContinuationFrame) // guaranteed by checkFrameOrder |
| } |
| } |
| |
| mh.HeadersFrame.headerFragBuf = nil |
| mh.HeadersFrame.invalidate() |
| |
| if err := hdec.Close(); err != nil { |
| return mh, ConnectionError(ErrCodeCompression) |
| } |
| if invalid != nil { |
| fr.errDetail = invalid |
| if VerboseLogs { |
| log.Printf("http2: invalid header: %v", invalid) |
| } |
| return nil, StreamError{mh.StreamID, ErrCodeProtocol, invalid} |
| } |
| if err := mh.checkPseudos(); err != nil { |
| fr.errDetail = err |
| if VerboseLogs { |
| log.Printf("http2: invalid pseudo headers: %v", err) |
| } |
| return nil, StreamError{mh.StreamID, ErrCodeProtocol, err} |
| } |
| return mh, nil |
| } |
| |
| func summarizeFrame(f Frame) string { |
| var buf bytes.Buffer |
| f.Header().writeDebug(&buf) |
| switch f := f.(type) { |
| case *SettingsFrame: |
| n := 0 |
| f.ForeachSetting(func(s Setting) error { |
| n++ |
| if n == 1 { |
| buf.WriteString(", settings:") |
| } |
| fmt.Fprintf(&buf, " %v=%v,", s.ID, s.Val) |
| return nil |
| }) |
| if n > 0 { |
| buf.Truncate(buf.Len() - 1) // remove trailing comma |
| } |
| case *DataFrame: |
| data := f.Data() |
| const max = 256 |
| if len(data) > max { |
| data = data[:max] |
| } |
| fmt.Fprintf(&buf, " data=%q", data) |
| if len(f.Data()) > max { |
| fmt.Fprintf(&buf, " (%d bytes omitted)", len(f.Data())-max) |
| } |
| case *WindowUpdateFrame: |
| if f.StreamID == 0 { |
| buf.WriteString(" (conn)") |
| } |
| fmt.Fprintf(&buf, " incr=%v", f.Increment) |
| case *PingFrame: |
| fmt.Fprintf(&buf, " ping=%q", f.Data[:]) |
| case *GoAwayFrame: |
| fmt.Fprintf(&buf, " LastStreamID=%v ErrCode=%v Debug=%q", |
| f.LastStreamID, f.ErrCode, f.debugData) |
| case *RSTStreamFrame: |
| fmt.Fprintf(&buf, " ErrCode=%v", f.ErrCode) |
| } |
| return buf.String() |
| } |