strengthen priority tree code and add tests
The priority tree codes has been strengthened to handle some new
corner cases.
- Handle setting the parent to itself.
- Handle exclusive reprioritization to the root stream.
- Handle reprioritization to stream dependent of stream being
reprioritized.
In addition, split out the adjustment function so that it can be
tested independently of the serverConn and add tests.
diff --git a/server.go b/server.go
index aba029a..7ab54f8 100644
--- a/server.go
+++ b/server.go
@@ -1213,7 +1213,7 @@
sc.streams[id] = st
if f.HasPriority() {
- sc.adjustStreamPriority(st.id, f.Priority)
+ adjustStreamPriority(sc.streams, st.id, f.Priority)
}
sc.curOpenStreams++
sc.req = requestParam{
@@ -1276,13 +1276,12 @@
}
func (sc *serverConn) processPriority(f *PriorityFrame) error {
- sc.adjustStreamPriority(f.StreamID, f.PriorityParam)
+ adjustStreamPriority(sc.streams, f.StreamID, f.PriorityParam)
return nil
}
-func (sc *serverConn) adjustStreamPriority(streamID uint32, priority PriorityParam) {
- // TODO: untested
- st, ok := sc.streams[streamID]
+func adjustStreamPriority(streams map[uint32]*stream, streamID uint32, priority PriorityParam) {
+ st, ok := streams[streamID]
if !ok {
// TODO: not quite correct (this streamID might
// already exist in the dep tree, but be closed), but
@@ -1290,10 +1289,27 @@
return
}
st.weight = priority.Weight
- st.parent = sc.streams[priority.StreamDep] // might be nil
- if priority.Exclusive && st.parent != nil {
- for _, openStream := range sc.streams {
- if openStream.parent == st.parent {
+ parent := streams[priority.StreamDep] // might be nil
+ if parent == st {
+ // if client tries to set this stream to be the parent of itself
+ // ignore and keep going
+ return
+ }
+
+ // section 5.3.3: If a stream is made dependent on one of its
+ // own dependencies, the formerly dependent stream is first
+ // moved to be dependent on the reprioritized stream's previous
+ // parent. The moved dependency retains its weight.
+ for piter := parent; piter != nil; piter = piter.parent {
+ if piter == st {
+ parent.parent = st.parent
+ break
+ }
+ }
+ st.parent = parent
+ if priority.Exclusive && (st.parent != nil || priority.StreamDep == 0) {
+ for _, openStream := range streams {
+ if openStream != st && openStream.parent == st.parent {
openStream.parent = st
}
}