diff --git a/internal/transport/grpchttp2/framer.go b/internal/transport/grpchttp2/framer.go index 0c1de6f7e215..2b93452c5c92 100644 --- a/internal/transport/grpchttp2/framer.go +++ b/internal/transport/grpchttp2/framer.go @@ -19,10 +19,7 @@ // Package grpchttp2 defines HTTP/2 types and a framer API and implementation. package grpchttp2 -import ( - "golang.org/x/net/http2/hpack" - "google.golang.org/grpc/mem" -) +import "golang.org/x/net/http2/hpack" // FrameType represents the type of an HTTP/2 Frame. // See [Frame Type]. @@ -109,17 +106,22 @@ type FrameHeader struct { // Each concrete Frame type defined below implements the Frame interface. type Frame interface { Header() *FrameHeader + // Free frees the underlying buffer if present so it can be reused by the + // framer. + // + // TODO: Remove method from the interface once the mem package gets merged. + // Free will be called on each mem.Buffer individually. + Free() } // DataFrame is the representation of a [DATA frame]. DATA frames convey -// arbitrary, variable-length sequences of octets associated with a stream. It -// is the user's responsibility to call Data.Free() when it is no longer -// needed. +// arbitrary, variable-length sequences of octets associated with a stream. // // [DATA frame]: https://httpwg.org/specs/rfc7540.html#DATA type DataFrame struct { hdr *FrameHeader - Data *mem.Buffer + free func() + Data []byte } // Header returns the 9 byte HTTP/2 header for this frame. @@ -127,15 +129,21 @@ func (f *DataFrame) Header() *FrameHeader { return f.hdr } +// Free frees the buffer containing the data in this frame. +func (f *DataFrame) Free() { + if f.free != nil { + f.free() + } +} + // HeadersFrame is the representation of a [HEADERS Frame]. The HEADERS frame // is used to open a stream, and additionally carries a header block fragment. -// It is the user's responsibility to call HdrBlock.Free() when it is no longer -// needed. // // [HEADERS Frame]: https://httpwg.org/specs/rfc7540.html#HEADERS type HeadersFrame struct { hdr *FrameHeader - HdrBlock *mem.Buffer + free func() + HdrBlock []byte } // Header returns the 9 byte HTTP/2 header for this frame. @@ -143,7 +151,15 @@ func (f *HeadersFrame) Header() *FrameHeader { return f.hdr } -// RSTStreamFrame is the representation of a [RST_STREAM Frame]. The RST_STREAM +// Free frees the buffer containing the header block in this frame. +func (f *HeadersFrame) Free() { + if f.free != nil { + f.free() + } +} + +// RSTStreamFrame is the representation of a [RST_STREAM Frame]. There is no +// underlying byte array in this frame, so Free() is a no-op. The RST_STREAM // frame allows for immediate termination of a stream // // [RST_STREAM Frame]: https://httpwg.org/specs/rfc7540.html#RST_STREAM @@ -157,9 +173,14 @@ func (f *RSTStreamFrame) Header() *FrameHeader { return f.hdr } -// SettingsFrame is the representation of a [SETTINGS Frame]. The SETTINGS frame -// conveys configuration parameters that affect how endpoints communicate, such -// as preferences and constraints on peer behavior. +// Free is a no-op for RSTStreamFrame. +func (f *RSTStreamFrame) Free() {} + +// SettingsFrame is the representation of a [SETTINGS Frame]. There is no +// underlying byte array in this frame, so Free() is a no-op. +// +// The SETTINGS frame conveys configuration parameters that affect how +// endpoints communicate, such as preferences and constraints on peer behavior. // // [SETTINGS Frame]: https://httpwg.org/specs/rfc7540.html#SETTINGS type SettingsFrame struct { @@ -172,17 +193,18 @@ func (f *SettingsFrame) Header() *FrameHeader { return f.hdr } +// Free is a no-op for SettingsFrame. +func (f *SettingsFrame) Free() {} + // PingFrame is the representation of a [PING Frame]. The PING frame is a // mechanism for measuring a minimal round-trip time from the sender, as well // as determining whether an idle connection is still functional. // -// It is the user's responsibility to call Data.Free() when it is no longer -// needed. -// // [PING Frame]: https://httpwg.org/specs/rfc7540.html#PING type PingFrame struct { hdr *FrameHeader - Data *mem.Buffer + free func() + Data []byte } // Header returns the 9 byte HTTP/2 header for this frame. @@ -190,19 +212,24 @@ func (f *PingFrame) Header() *FrameHeader { return f.hdr } +// Free frees the buffer containing the data in this frame. +func (f *PingFrame) Free() { + if f.free != nil { + f.free() + } +} + // GoAwayFrame is the representation of a [GOAWAY Frame]. The GOAWAY frame is // used to initiate shutdown of a connection or to signal serious error // conditions. // -// It is the user's responsibility to call DebugData.Free() when it is no longer -// needed. -// // [GOAWAY Frame]: https://httpwg.org/specs/rfc7540.html#GOAWAY type GoAwayFrame struct { hdr *FrameHeader + free func() LastStreamID uint32 Code ErrCode - DebugData *mem.Buffer + DebugData []byte } // Header returns the 9 byte HTTP/2 header for this frame. @@ -210,6 +237,13 @@ func (f *GoAwayFrame) Header() *FrameHeader { return f.hdr } +// Free frees the buffer containing the debug data in this frame. +func (f *GoAwayFrame) Free() { + if f.free != nil { + f.free() + } +} + // WindowUpdateFrame is the representation of a [WINDOW_UPDATE Frame]. The // WINDOW_UPDATE frame is used to implement flow control. // @@ -227,13 +261,11 @@ func (f *WindowUpdateFrame) Header() *FrameHeader { // ContinuationFrame is the representation of a [CONTINUATION Frame]. The // CONTINUATION frame is used to continue a sequence of header block fragments. // -// It is the user's responsibility to call HdrBlock.Free() when it is no longer -// needed. -// // [CONTINUATION Frame]: https://httpwg.org/specs/rfc7540.html#CONTINUATION type ContinuationFrame struct { hdr *FrameHeader - HdrBlock *mem.Buffer + free func() + HdrBlock []byte } // Header returns the 9 byte HTTP/2 header for this frame. @@ -241,10 +273,19 @@ func (f *ContinuationFrame) Header() *FrameHeader { return f.hdr } +// Free frees the buffer containing the header block in this frame. +func (f *ContinuationFrame) Free() { + if f.free != nil { + f.free() + } +} + // MetaHeadersFrame is the representation of one HEADERS frame and zero or more // contiguous CONTINUATION frames and the decoding of their HPACK-encoded // contents. This frame type is not transmitted over the network and is only // generated by the ReadFrame() function. +// +// Since there is no underlying buffer in this Frame, Free() is a no-op. type MetaHeadersFrame struct { hdr *FrameHeader Fields []hpack.HeaderField @@ -258,17 +299,24 @@ func (f *MetaHeadersFrame) Header() *FrameHeader { return f.hdr } +// Free is a no-op for MetaHeadersFrame. +func (f *MetaHeadersFrame) Free() {} + // Framer encapsulates the functionality to read and write HTTP/2 frames. type Framer interface { // ReadFrame returns grpchttp2.Frame. It is the caller's responsibility to - // free the underlying buffer when done using the Frame. + // call Frame.Free() once it is done using it. Note that once the mem + // package gets merged, this API will change in favor of Buffer.Free(). ReadFrame() (Frame, error) - // WriteData writes an HTTP/2 DATA frame to the stream. The data is expected - // to be freed by the caller. - WriteData(streamID uint32, endStream bool, data mem.BufferSlice) error - // WriteHeaders writes an HTTP/2 HEADERS frame to the stream. - WriteHeaders(streamID uint32, endStream, endHeaders bool, headerBlock []byte) error - // WriteRSTStream writes an HTTP/2 RST_STREAM frame to the stream. + // WriteData writes an HTTP/2 DATA frame to the stream. + // TODO: Once the mem package gets merged, data will change type to + // mem.BufferSlice. + WriteData(streamID uint32, endStream bool, data ...[]byte) error + // WriteData writes an HTTP/2 HEADERS frame to the stream. + // TODO: Once the mem package gets merged, headerBlock will change type to + // mem.Buffer. + WriteHeaders(streamID uint32, endStream, endHeaders bool, headerBlocks []byte) error + // WriteData writes an HTTP/2 RST_STREAM frame to the stream. WriteRSTStream(streamID uint32, code ErrCode) error // WriteSettings writes an HTTP/2 SETTINGS frame to the connection. WriteSettings(settings ...Setting) error @@ -277,9 +325,13 @@ type Framer interface { // WritePing writes an HTTP/2 PING frame to the connection. WritePing(ack bool, data [8]byte) error // WriteGoAway writes an HTTP/2 GOAWAY frame to the connection. + // TODO: Once the mem package gets merged, debugData will change type to + // mem.Buffer. WriteGoAway(maxStreamID uint32, code ErrCode, debugData []byte) error // WriteWindowUpdate writes an HTTP/2 WINDOW_UPDATE frame to the stream. WriteWindowUpdate(streamID, inc uint32) error // WriteContinuation writes an HTTP/2 CONTINUATION frame to the stream. + // TODO: Once the mem package gets merged, data will change type to + // mem.Buffer. WriteContinuation(streamID uint32, endHeaders bool, headerBlock []byte) error }