Skip to content
This repository has been archived by the owner on Sep 17, 2019. It is now read-only.

Pushhandler #23

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 24 additions & 60 deletions frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,9 @@ func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {

// HeadersFrameParam are the parameters for writing a HEADERS frame.
type HeadersFrameParam struct {
// Write this header as a push promise.
PushPromise bool

// StreamID is the required Stream ID to initiate.
StreamID uint32
// BlockFragment is part (or all) of a Header Block.
Expand All @@ -779,7 +782,7 @@ type HeadersFrameParam struct {
// 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.
// states. It is not sent if this write is a push promise.
EndStream bool

// EndHeaders indicates that this frame contains an entire
Expand All @@ -792,11 +795,14 @@ type HeadersFrameParam struct {
PadLength uint8

// Priority, if non-zero, includes stream priority information
// in the HEADER frame.
// in the HEADER frame. It is not sent if this write is a PushPromise.
Priority PriorityParam

// PromiseID is the promised stream if this write is a PushPromise.
PromiseID uint32
}

// WriteHeaders writes a single HEADERS frame.
// WriteHeaders writes a single HEADERS or PUSH_PROMISE frame.
//
// This is a low-level header writing method. Encoding headers and
// splitting them into any necessary CONTINUATION frames is handled
Expand All @@ -812,20 +818,24 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
if p.PadLength != 0 {
flags |= FlagHeadersPadded
}
if p.EndStream {
if p.EndStream && !p.PushPromise {
flags |= FlagHeadersEndStream
}
if p.EndHeaders {
flags |= FlagHeadersEndHeaders
}
if !p.Priority.IsZero() {
if !p.Priority.IsZero() && !p.PushPromise {
flags |= FlagHeadersPriority
}
f.startWrite(FrameHeaders, flags, p.StreamID)
fh := FrameHeaders
if p.PushPromise {
fh = FramePushPromise
}
f.startWrite(fh, flags, p.StreamID)
if p.PadLength != 0 {
f.writeByte(p.PadLength)
}
if !p.Priority.IsZero() {
if !p.Priority.IsZero() && !p.PushPromise {
v := p.Priority.StreamDep
if !validStreamID(v) && !f.AllowIllegalWrites {
return errors.New("invalid dependent stream id")
Expand All @@ -836,6 +846,13 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
f.writeUint32(v)
f.writeByte(p.Priority.Weight)
}
if p.PushPromise {
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()
Expand Down Expand Up @@ -1026,59 +1043,6 @@ func parsePushPromise(fh FrameHeader, p []byte) (_ Frame, err error) {
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 {
Expand Down
5 changes: 3 additions & 2 deletions frame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,13 +511,14 @@ func TestWriteGoAway(t *testing.T) {
}

func TestWritePushPromise(t *testing.T) {
pp := PushPromiseParam{
pp := HeadersFrameParam{
PushPromise: true,
StreamID: 42,
PromiseID: 42,
BlockFragment: []byte("abc"),
}
fr, buf := testFramer()
if err := fr.WritePushPromise(pp); err != nil {
if err := fr.WriteHeaders(pp); err != nil {
t.Fatal(err)
}
const wantEnc = "\x00\x00\x07\x05\x00\x00\x00\x00*\x00\x00\x00*abc"
Expand Down
12 changes: 11 additions & 1 deletion h2demo/h2demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,17 @@ func newGopherTilesHandler() http.Handler {
return
}
}
cacheBust := time.Now().UnixNano()

if p, ok := w.(http2.Pusher); ok {
for y := 0; y < yt; y++ {
for x := 0; x < xt; x++ {
path := fmt.Sprintf("/gophertiles?x=%d&y=%d&cachebust=%d&latency=%d", x, y, cacheBust, ms)
p.Push("GET", path, nil)
}
}
}

io.WriteString(w, "<html><body>")
fmt.Fprintf(w, "A grid of %d tiled images is below. Compare:<p>", xt*yt)
for _, ms := range []int{0, 30, 200, 1000} {
Expand All @@ -295,7 +306,6 @@ func newGopherTilesHandler() http.Handler {
)
}
io.WriteString(w, "<p>\n")
cacheBust := time.Now().UnixNano()
for y := 0; y < yt; y++ {
for x := 0; x < xt; x++ {
fmt.Fprintf(w, "<img width=%d height=%d src='/gophertiles?x=%d&y=%d&cachebust=%d&latency=%d'>",
Expand Down
Loading