|
|
@@ -122,7 +122,7 @@ var flagName = map[FrameType]map[Flags]string{
|
|
|
// 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(fh FrameHeader, payload []byte) (Frame, error)
|
|
|
+type frameParser func(fc *frameCache, fh FrameHeader, payload []byte) (Frame, error)
|
|
|
|
|
|
var frameParsers = map[FrameType]frameParser{
|
|
|
FrameData: parseDataFrame,
|
|
|
@@ -323,6 +323,8 @@ type Framer struct {
|
|
|
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 {
|
|
|
@@ -398,6 +400,27 @@ const (
|
|
|
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{
|
|
|
@@ -477,7 +500,7 @@ func (fr *Framer) ReadFrame() (Frame, error) {
|
|
|
if _, err := io.ReadFull(fr.r, payload); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
- f, err := typeFrameParser(fh.Type)(fh, payload)
|
|
|
+ f, err := typeFrameParser(fh.Type)(fr.frameCache, fh, payload)
|
|
|
if err != nil {
|
|
|
if ce, ok := err.(connError); ok {
|
|
|
return nil, fr.connError(ce.Code, ce.Reason)
|
|
|
@@ -565,7 +588,7 @@ func (f *DataFrame) Data() []byte {
|
|
|
return f.data
|
|
|
}
|
|
|
|
|
|
-func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
+func parseDataFrame(fc *frameCache, fh FrameHeader, 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
|
|
|
@@ -574,9 +597,9 @@ func parseDataFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
// PROTOCOL_ERROR.
|
|
|
return nil, connError{ErrCodeProtocol, "DATA frame with stream ID 0"}
|
|
|
}
|
|
|
- f := &DataFrame{
|
|
|
- FrameHeader: fh,
|
|
|
- }
|
|
|
+ f := fc.getDataFrame()
|
|
|
+ f.FrameHeader = fh
|
|
|
+
|
|
|
var padSize byte
|
|
|
if fh.Flags.Has(FlagDataPadded) {
|
|
|
var err error
|
|
|
@@ -672,7 +695,7 @@ type SettingsFrame struct {
|
|
|
p []byte
|
|
|
}
|
|
|
|
|
|
-func parseSettingsFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseSettingsFrame(_ *frameCache, fh FrameHeader, 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
|
|
|
@@ -774,7 +797,7 @@ type PingFrame struct {
|
|
|
|
|
|
func (f *PingFrame) IsAck() bool { return f.Flags.Has(FlagPingAck) }
|
|
|
|
|
|
-func parsePingFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
+func parsePingFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
if len(payload) != 8 {
|
|
|
return nil, ConnectionError(ErrCodeFrameSize)
|
|
|
}
|
|
|
@@ -814,7 +837,7 @@ func (f *GoAwayFrame) DebugData() []byte {
|
|
|
return f.debugData
|
|
|
}
|
|
|
|
|
|
-func parseGoAwayFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseGoAwayFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
|
|
if fh.StreamID != 0 {
|
|
|
return nil, ConnectionError(ErrCodeProtocol)
|
|
|
}
|
|
|
@@ -854,7 +877,7 @@ func (f *UnknownFrame) Payload() []byte {
|
|
|
return f.p
|
|
|
}
|
|
|
|
|
|
-func parseUnknownFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseUnknownFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
|
|
return &UnknownFrame{fh, p}, nil
|
|
|
}
|
|
|
|
|
|
@@ -865,7 +888,7 @@ type WindowUpdateFrame struct {
|
|
|
Increment uint32 // never read with high bit set
|
|
|
}
|
|
|
|
|
|
-func parseWindowUpdateFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseWindowUpdateFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
|
|
if len(p) != 4 {
|
|
|
return nil, ConnectionError(ErrCodeFrameSize)
|
|
|
}
|
|
|
@@ -930,7 +953,7 @@ func (f *HeadersFrame) HasPriority() bool {
|
|
|
return f.FrameHeader.Flags.Has(FlagHeadersPriority)
|
|
|
}
|
|
|
|
|
|
-func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
|
|
|
+func parseHeadersFrame(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
|
|
hf := &HeadersFrame{
|
|
|
FrameHeader: fh,
|
|
|
}
|
|
|
@@ -1067,7 +1090,7 @@ func (p PriorityParam) IsZero() bool {
|
|
|
return p == PriorityParam{}
|
|
|
}
|
|
|
|
|
|
-func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
+func parsePriorityFrame(_ *frameCache, fh FrameHeader, payload []byte) (Frame, error) {
|
|
|
if fh.StreamID == 0 {
|
|
|
return nil, connError{ErrCodeProtocol, "PRIORITY frame with stream ID 0"}
|
|
|
}
|
|
|
@@ -1114,7 +1137,7 @@ type RSTStreamFrame struct {
|
|
|
ErrCode ErrCode
|
|
|
}
|
|
|
|
|
|
-func parseRSTStreamFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseRSTStreamFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
|
|
if len(p) != 4 {
|
|
|
return nil, ConnectionError(ErrCodeFrameSize)
|
|
|
}
|
|
|
@@ -1144,7 +1167,7 @@ type ContinuationFrame struct {
|
|
|
headerFragBuf []byte
|
|
|
}
|
|
|
|
|
|
-func parseContinuationFrame(fh FrameHeader, p []byte) (Frame, error) {
|
|
|
+func parseContinuationFrame(_ *frameCache, fh FrameHeader, p []byte) (Frame, error) {
|
|
|
if fh.StreamID == 0 {
|
|
|
return nil, connError{ErrCodeProtocol, "CONTINUATION frame with stream ID 0"}
|
|
|
}
|
|
|
@@ -1194,7 +1217,7 @@ func (f *PushPromiseFrame) HeadersEnded() bool {
|
|
|
return f.FrameHeader.Flags.Has(FlagPushPromiseEndHeaders)
|
|
|
}
|
|
|
|
|
|
-func parsePushPromise(fh FrameHeader, p []byte) (_ Frame, err error) {
|
|
|
+func parsePushPromise(_ *frameCache, fh FrameHeader, p []byte) (_ Frame, err error) {
|
|
|
pp := &PushPromiseFrame{
|
|
|
FrameHeader: fh,
|
|
|
}
|