Browse Source

PRIORITY reading and writing.

Brad Fitzpatrick 11 years ago
parent
commit
281fedb9ff
2 changed files with 124 additions and 11 deletions
  1. 50 5
      frame.go
  2. 74 6
      frame_test.go

+ 50 - 5
frame.go

@@ -145,7 +145,7 @@ type frameParser func(fh FrameHeader, payload []byte) (Frame, error)
 var frameParsers = map[FrameType]frameParser{
 var frameParsers = map[FrameType]frameParser{
 	FrameData:         parseDataFrame,
 	FrameData:         parseDataFrame,
 	FrameHeaders:      parseHeadersFrame,
 	FrameHeaders:      parseHeadersFrame,
-	FramePriority:     nil, // TODO
+	FramePriority:     parsePriorityFrame,
 	FrameRSTStream:    parseRSTStreamFrame,
 	FrameRSTStream:    parseRSTStreamFrame,
 	FrameSettings:     parseSettingsFrame,
 	FrameSettings:     parseSettingsFrame,
 	FramePushPromise:  nil, // TODO
 	FramePushPromise:  nil, // TODO
@@ -630,7 +630,7 @@ func parseHeadersFrame(fh FrameHeader, p []byte) (_ Frame, err error) {
 			return nil, err
 			return nil, err
 		}
 		}
 		hf.Priority.StreamDep = v & 0x7fffffff
 		hf.Priority.StreamDep = v & 0x7fffffff
-		hf.Priority.ExclusiveDep = (v != hf.Priority.StreamDep) // high bit was set
+		hf.Priority.Exclusive = (v != hf.Priority.StreamDep) // high bit was set
 		p, hf.Priority.Weight, err = readByte(p)
 		p, hf.Priority.Weight, err = readByte(p)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
@@ -701,7 +701,7 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
 		if !validStreamID(v) && !f.AllowIllegalWrites {
 		if !validStreamID(v) && !f.AllowIllegalWrites {
 			return errors.New("invalid dependent stream id")
 			return errors.New("invalid dependent stream id")
 		}
 		}
-		if p.Priority.ExclusiveDep {
+		if p.Priority.Exclusive {
 			v |= 1 << 31
 			v |= 1 << 31
 		}
 		}
 		f.writeUint32(v)
 		f.writeUint32(v)
@@ -712,6 +712,13 @@ func (f *Framer) WriteHeaders(p HeadersFrameParam) error {
 	return f.endWrite()
 	return f.endWrite()
 }
 }
 
 
+// A PriorityFrame specifies the sender-advised priority of a stream.
+// See http://http2.github.io/http2-spec/#rfc.section.6.3
+type PriorityFrame struct {
+	FrameHeader
+	PriorityParam
+}
+
 // PriorityParam are the stream prioritzation parameters.
 // PriorityParam are the stream prioritzation parameters.
 type PriorityParam struct {
 type PriorityParam struct {
 	// StreamDep is a 31-bit stream identifier for the
 	// StreamDep is a 31-bit stream identifier for the
@@ -719,8 +726,8 @@ type PriorityParam struct {
 	// dependency.
 	// dependency.
 	StreamDep uint32
 	StreamDep uint32
 
 
-	// ExclusiveDep is whether the dependency is exclusive.
-	ExclusiveDep bool
+	// Exclusive is whether the dependency is exclusive.
+	Exclusive bool
 
 
 	// Weight is the stream's weight. It should be set together
 	// Weight is the stream's weight. It should be set together
 	// with StreamDep, or neither should be set.
 	// with StreamDep, or neither should be set.
@@ -731,6 +738,44 @@ func (p PriorityParam) IsZero() bool {
 	return p == PriorityParam{}
 	return p == PriorityParam{}
 }
 }
 
 
+func parsePriorityFrame(fh FrameHeader, payload []byte) (Frame, error) {
+	if fh.StreamID == 0 {
+		return nil, ConnectionError(ErrCodeProtocol)
+	}
+	if len(payload) < 5 {
+		// TODO: != 5 or < 5? https://github.com/http2/http2-spec/issues/611
+		return nil, ConnectionError(ErrCodeProtocol)
+	}
+	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
+	}
+	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.
 // A RSTStreamFrame allows for abnormal termination of a stream.
 // See http://http2.github.io/http2-spec/#rfc.section.6.4
 // See http://http2.github.io/http2-spec/#rfc.section.6.4
 type RSTStreamFrame struct {
 type RSTStreamFrame struct {

+ 74 - 6
frame_test.go

@@ -148,9 +148,9 @@ func TestWriteHeaders(t *testing.T) {
 				EndHeaders:    true,
 				EndHeaders:    true,
 				PadLength:     2,
 				PadLength:     2,
 				Priority: PriorityParam{
 				Priority: PriorityParam{
-					StreamDep:    15,
-					ExclusiveDep: true,
-					Weight:       127,
+					StreamDep: 15,
+					Exclusive: true,
+					Weight:    127,
 				},
 				},
 			},
 			},
 			"\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x0f\u007fabc\x00\x00",
 			"\x00\x00\v\x01-\x00\x00\x00*\x02\x80\x00\x00\x0f\u007fabc\x00\x00",
@@ -163,9 +163,9 @@ func TestWriteHeaders(t *testing.T) {
 					Length:   uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding
 					Length:   uint32(1 + 5 + len("abc") + 2), // pad length + priority + contents + padding
 				},
 				},
 				Priority: PriorityParam{
 				Priority: PriorityParam{
-					StreamDep:    15,
-					ExclusiveDep: true,
-					Weight:       127,
+					StreamDep: 15,
+					Exclusive: true,
+					Weight:    127,
 				},
 				},
 				headerFragBuf: []byte("abc"),
 				headerFragBuf: []byte("abc"),
 			},
 			},
@@ -246,3 +246,71 @@ func TestWriteContinuation(t *testing.T) {
 		}
 		}
 	}
 	}
 }
 }
+
+func TestWritePriority(t *testing.T) {
+	const streamID = 42
+	tests := []struct {
+		name      string
+		priority  PriorityParam
+		wantFrame *PriorityFrame
+	}{
+		{
+			"not exclusive",
+			PriorityParam{
+				StreamDep: 2,
+				Exclusive: false,
+				Weight:    127,
+			},
+			&PriorityFrame{
+				FrameHeader{
+					valid:    true,
+					StreamID: streamID,
+					Type:     FramePriority,
+					Length:   5,
+				},
+				PriorityParam{
+					StreamDep: 2,
+					Exclusive: false,
+					Weight:    127,
+				},
+			},
+		},
+
+		{
+			"exclusive",
+			PriorityParam{
+				StreamDep: 3,
+				Exclusive: true,
+				Weight:    77,
+			},
+			&PriorityFrame{
+				FrameHeader{
+					valid:    true,
+					StreamID: streamID,
+					Type:     FramePriority,
+					Length:   5,
+				},
+				PriorityParam{
+					StreamDep: 3,
+					Exclusive: true,
+					Weight:    77,
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		fr, _ := testFramer()
+		if err := fr.WritePriority(streamID, tt.priority); err != nil {
+			t.Errorf("test %q: %v", tt.name, err)
+			continue
+		}
+		f, err := fr.ReadFrame()
+		if err != nil {
+			t.Errorf("test %q: failed to read the frame back: %v", tt.name, err)
+			continue
+		}
+		if !reflect.DeepEqual(f, tt.wantFrame) {
+			t.Errorf("test %q: mismatch.\n got: %#v\nwant: %#v\n", tt.name, f, tt.wantFrame)
+		}
+	}
+}