2017-01-24 21:50:39 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2017 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License, version 2.0 (the
|
|
|
|
* "License"); you may not use this file except in compliance with the License. You may obtain a
|
|
|
|
* copy of the License at:
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
|
|
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
|
|
* or implied. See the License for the specific language governing permissions and limitations under
|
|
|
|
* the License.
|
|
|
|
*/
|
|
|
|
package io.netty.handler.codec.http2;
|
|
|
|
|
|
|
|
import org.junit.Before;
|
|
|
|
import org.junit.Test;
|
|
|
|
import org.mockito.MockitoAnnotations;
|
|
|
|
|
|
|
|
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
|
|
|
|
import static io.netty.handler.codec.http2.Http2CodecUtil.MAX_WEIGHT;
|
|
|
|
import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT;
|
|
|
|
import static io.netty.handler.codec.http2.WeightedFairQueueByteDistributor.INITIAL_CHILDREN_MAP_SIZE;
|
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
import static org.junit.Assert.assertFalse;
|
|
|
|
import static org.junit.Assert.assertTrue;
|
2017-03-09 17:10:38 +01:00
|
|
|
import static org.mockito.Mockito.any;
|
|
|
|
import static org.mockito.Mockito.anyInt;
|
2017-01-24 21:50:39 +01:00
|
|
|
import static org.mockito.Mockito.doAnswer;
|
|
|
|
|
|
|
|
public class WeightedFairQueueByteDistributorDependencyTreeTest extends
|
|
|
|
AbstractWeightedFairQueueByteDistributorDependencyTest {
|
|
|
|
@Before
|
|
|
|
public void setup() throws Http2Exception {
|
|
|
|
MockitoAnnotations.initMocks(this);
|
|
|
|
|
|
|
|
setup(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setup(int maxStateOnlySize) {
|
|
|
|
connection = new DefaultHttp2Connection(false);
|
|
|
|
distributor = new WeightedFairQueueByteDistributor(connection, maxStateOnlySize);
|
|
|
|
|
|
|
|
// Assume we always write all the allocated bytes.
|
|
|
|
doAnswer(writeAnswer(false)).when(writer).write(any(Http2Stream.class), anyInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void closingStreamWithChildrenDoesNotCauseConcurrentModification() throws Http2Exception {
|
|
|
|
// We create enough streams to wrap around the child array. We carefully craft the stream ids so that they hash
|
|
|
|
// codes overlap with respect to the child collection. If the implementation is not careful this may lead to a
|
|
|
|
// concurrent modification exception while promoting all children to the connection stream.
|
|
|
|
final Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
final int numStreams = INITIAL_CHILDREN_MAP_SIZE - 1;
|
|
|
|
for (int i = 0, streamId = 3; i < numStreams; ++i, streamId += INITIAL_CHILDREN_MAP_SIZE) {
|
|
|
|
final Http2Stream stream = connection.local().createStream(streamId, false);
|
|
|
|
setPriority(stream.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
}
|
|
|
|
assertEquals(INITIAL_CHILDREN_MAP_SIZE, connection.numActiveStreams());
|
|
|
|
streamA.close();
|
|
|
|
assertEquals(numStreams, connection.numActiveStreams());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void closeWhileIteratingDoesNotNPE() throws Http2Exception {
|
|
|
|
final Http2Stream streamA = connection.local().createStream(3, false);
|
|
|
|
final Http2Stream streamB = connection.local().createStream(5, false);
|
|
|
|
final Http2Stream streamC = connection.local().createStream(7, false);
|
2017-03-09 17:10:38 +01:00
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
2017-01-24 21:50:39 +01:00
|
|
|
connection.forEachActiveStream(new Http2StreamVisitor() {
|
|
|
|
@Override
|
|
|
|
public boolean visit(Http2Stream stream) throws Http2Exception {
|
|
|
|
streamA.close();
|
2017-03-09 17:10:38 +01:00
|
|
|
setPriority(streamB.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
2017-01-24 21:50:39 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void localStreamCanDependUponIdleStream() throws Http2Exception {
|
|
|
|
setup(1);
|
|
|
|
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
setPriority(3, streamA.id(), MIN_WEIGHT, true);
|
|
|
|
assertTrue(distributor.isChild(3, streamA.id(), MIN_WEIGHT));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void remoteStreamCanDependUponIdleStream() throws Http2Exception {
|
|
|
|
setup(1);
|
|
|
|
|
|
|
|
Http2Stream streamA = connection.remote().createStream(2, false);
|
|
|
|
setPriority(4, streamA.id(), MIN_WEIGHT, true);
|
|
|
|
assertTrue(distributor.isChild(4, streamA.id(), MIN_WEIGHT));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void prioritizeShouldUseDefaults() throws Exception {
|
|
|
|
Http2Stream stream = connection.local().createStream(1, false);
|
|
|
|
assertTrue(distributor.isChild(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertEquals(0, distributor.numChildren(stream.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void reprioritizeWithNoChangeShouldDoNothing() throws Exception {
|
|
|
|
Http2Stream stream = connection.local().createStream(1, false);
|
|
|
|
setPriority(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertTrue(distributor.isChild(stream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertEquals(0, distributor.numChildren(stream.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void stateOnlyPriorityShouldBePreservedWhenStreamsAreCreatedAndClosed() throws Http2Exception {
|
|
|
|
setup(3);
|
|
|
|
|
|
|
|
short weight3 = MIN_WEIGHT + 1;
|
|
|
|
short weight5 = (short) (weight3 + 1);
|
|
|
|
short weight7 = (short) (weight5 + 1);
|
|
|
|
setPriority(3, connection.connectionStream().id(), weight3, true);
|
|
|
|
setPriority(5, connection.connectionStream().id(), weight5, true);
|
|
|
|
setPriority(7, connection.connectionStream().id(), weight7, true);
|
|
|
|
|
|
|
|
assertEquals(0, connection.numActiveStreams());
|
|
|
|
verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7);
|
|
|
|
|
|
|
|
// Now create stream objects and ensure the state and dependency tree is preserved.
|
|
|
|
Http2Stream streamA = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
assertEquals(3, connection.numActiveStreams());
|
|
|
|
verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7);
|
|
|
|
|
|
|
|
// Close all the streams and ensure the state and dependency tree is preserved.
|
|
|
|
streamA.close();
|
|
|
|
streamB.close();
|
|
|
|
streamC.close();
|
|
|
|
|
|
|
|
assertEquals(0, connection.numActiveStreams());
|
|
|
|
verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(weight3, weight5, weight7);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyStateOnlyPriorityShouldBePreservedWhenStreamsAreCreated(short weight3, short weight5,
|
|
|
|
short weight7) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(1, distributor.numChildren(7));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(5, 7, weight5));
|
|
|
|
assertEquals(1, distributor.numChildren(5));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(3, 5, weight3));
|
|
|
|
assertEquals(0, distributor.numChildren(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final int leadersId = 3; // js, css
|
|
|
|
private static final int unblockedId = 5;
|
|
|
|
private static final int backgroundId = 7;
|
|
|
|
private static final int speculativeId = 9;
|
|
|
|
private static final int followersId = 11; // images
|
|
|
|
private static final short leadersWeight = 201;
|
|
|
|
private static final short unblockedWeight = 101;
|
|
|
|
private static final short backgroundWeight = 1;
|
|
|
|
private static final short speculativeWeight = 1;
|
|
|
|
private static final short followersWeight = 1;
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void fireFoxQoSStreamsRemainAfterDataStreamsAreClosed() throws Http2Exception {
|
|
|
|
// http://bitsup.blogspot.com/2015/01/http2-dependency-priorities-in-firefox.html
|
|
|
|
setup(5);
|
|
|
|
|
|
|
|
setPriority(leadersId, connection.connectionStream().id(), leadersWeight, false);
|
|
|
|
setPriority(unblockedId, connection.connectionStream().id(), unblockedWeight, false);
|
|
|
|
setPriority(backgroundId, connection.connectionStream().id(), backgroundWeight, false);
|
|
|
|
setPriority(speculativeId, backgroundId, speculativeWeight, false);
|
|
|
|
setPriority(followersId, leadersId, followersWeight, false);
|
|
|
|
|
|
|
|
verifyFireFoxQoSStreams();
|
|
|
|
|
|
|
|
// Simulate a HTML request
|
|
|
|
short htmlGetStreamWeight = 2;
|
|
|
|
Http2Stream htmlGetStream = connection.local().createStream(13, false);
|
|
|
|
setPriority(htmlGetStream.id(), followersId, htmlGetStreamWeight, false);
|
|
|
|
Http2Stream favIconStream = connection.local().createStream(15, false);
|
|
|
|
setPriority(favIconStream.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
Http2Stream cssStream = connection.local().createStream(17, false);
|
|
|
|
setPriority(cssStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
Http2Stream jsStream = connection.local().createStream(19, false);
|
|
|
|
setPriority(jsStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
Http2Stream imageStream = connection.local().createStream(21, false);
|
|
|
|
setPriority(imageStream.id(), followersId, 1, false);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(4, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(leadersId, connection.connectionStream().id(), leadersWeight));
|
|
|
|
assertEquals(3, distributor.numChildren(leadersId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(unblockedId, connection.connectionStream().id(), unblockedWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(unblockedId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(backgroundId, connection.connectionStream().id(), backgroundWeight));
|
|
|
|
assertEquals(1, distributor.numChildren(backgroundId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(favIconStream.id(), connection.connectionStream().id(),
|
|
|
|
DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(favIconStream.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(followersId, leadersId, followersWeight));
|
|
|
|
assertEquals(2, distributor.numChildren(followersId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(speculativeId, backgroundId, speculativeWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(speculativeId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(cssStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(cssStream.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(jsStream.id(), leadersId, DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(jsStream.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(htmlGetStream.id(), followersId, htmlGetStreamWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(htmlGetStream.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(imageStream.id(), followersId, followersWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(imageStream.id()));
|
|
|
|
|
|
|
|
// Close all the data streams and ensure the "priority only streams" are retained in the dependency tree.
|
|
|
|
htmlGetStream.close();
|
|
|
|
favIconStream.close();
|
|
|
|
cssStream.close();
|
|
|
|
jsStream.close();
|
|
|
|
imageStream.close();
|
|
|
|
|
|
|
|
verifyFireFoxQoSStreams();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyFireFoxQoSStreams() {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(leadersId, connection.connectionStream().id(), leadersWeight));
|
|
|
|
assertEquals(1, distributor.numChildren(leadersId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(unblockedId, connection.connectionStream().id(), unblockedWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(unblockedId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(backgroundId, connection.connectionStream().id(), backgroundWeight));
|
|
|
|
assertEquals(1, distributor.numChildren(backgroundId));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(followersId, leadersId, followersWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(followersId));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(speculativeId, backgroundId, speculativeWeight));
|
|
|
|
assertEquals(0, distributor.numChildren(speculativeId));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void lowestPrecedenceStateShouldBeDropped() throws Http2Exception {
|
|
|
|
setup(3);
|
|
|
|
|
|
|
|
short weight3 = MAX_WEIGHT;
|
|
|
|
short weight5 = (short) (weight3 - 1);
|
|
|
|
short weight7 = (short) (weight5 - 1);
|
|
|
|
short weight9 = (short) (weight7 - 1);
|
|
|
|
setPriority(3, connection.connectionStream().id(), weight3, true);
|
|
|
|
setPriority(5, connection.connectionStream().id(), weight5, true);
|
|
|
|
setPriority(7, connection.connectionStream().id(), weight7, false);
|
|
|
|
assertEquals(0, connection.numActiveStreams());
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped1(weight3, weight5, weight7);
|
|
|
|
|
|
|
|
// Attempt to create a new item in the dependency tree but the maximum amount of "state only" streams is meet
|
|
|
|
// so a stream will have to be dropped. Currently the new stream is the lowest "precedence" so it is dropped.
|
|
|
|
setPriority(9, 3, weight9, false);
|
|
|
|
assertEquals(0, connection.numActiveStreams());
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped1(weight3, weight5, weight7);
|
|
|
|
|
|
|
|
// Set the priority for stream 9 such that its depth in the dependency tree is numerically lower than stream 3,
|
|
|
|
// and therefore the dependency state associated with stream 3 will be dropped.
|
|
|
|
setPriority(9, 5, weight9, true);
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped2(weight9, weight5, weight7);
|
|
|
|
|
|
|
|
// Test that stream which has been activated is lower priority than other streams that have not been activated.
|
|
|
|
Http2Stream streamA = connection.local().createStream(5, false);
|
|
|
|
streamA.close();
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped2(weight9, weight5, weight7);
|
|
|
|
|
|
|
|
// Stream 3 (hasn't been opened) should result in stream 5 being dropped.
|
|
|
|
setPriority(3, 9, weight3, false);
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped3(weight3, weight7, weight9);
|
|
|
|
|
|
|
|
// Stream 5's state has been discarded so we should be able to re-insert this state.
|
|
|
|
setPriority(5, 0, weight5, false);
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped4(weight5, weight7, weight9);
|
|
|
|
|
|
|
|
// All streams are at the same level, so stream ID should be used to drop the numeric lowest valued stream.
|
|
|
|
short weight11 = (short) (weight9 - 1);
|
|
|
|
setPriority(11, 0, weight11, false);
|
|
|
|
verifyLowestPrecedenceStateShouldBeDropped5(weight7, weight9, weight11);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyLowestPrecedenceStateShouldBeDropped1(short weight3, short weight5, short weight7) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(0, distributor.numChildren(7));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5));
|
|
|
|
assertEquals(1, distributor.numChildren(5));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(3, 5, weight3));
|
|
|
|
assertEquals(0, distributor.numChildren(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyLowestPrecedenceStateShouldBeDropped2(short weight9, short weight5, short weight7) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(0, distributor.numChildren(7));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5));
|
|
|
|
assertEquals(1, distributor.numChildren(5));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(9, 5, weight9));
|
|
|
|
assertEquals(0, distributor.numChildren(9));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyLowestPrecedenceStateShouldBeDropped3(short weight3, short weight7, short weight9) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(0, distributor.numChildren(7));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9));
|
|
|
|
assertEquals(1, distributor.numChildren(9));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(3, 9, weight3));
|
|
|
|
assertEquals(0, distributor.numChildren(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyLowestPrecedenceStateShouldBeDropped4(short weight5, short weight7, short weight9) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(5, connection.connectionStream().id(), weight5));
|
|
|
|
assertEquals(0, distributor.numChildren(5));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(0, distributor.numChildren(7));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9));
|
|
|
|
assertEquals(0, distributor.numChildren(9));
|
|
|
|
}
|
|
|
|
|
|
|
|
private void verifyLowestPrecedenceStateShouldBeDropped5(short weight7, short weight9, short weight11) {
|
|
|
|
// Level 0
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(11, connection.connectionStream().id(), weight11));
|
|
|
|
assertEquals(0, distributor.numChildren(11));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(7, connection.connectionStream().id(), weight7));
|
|
|
|
assertEquals(0, distributor.numChildren(7));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(9, connection.connectionStream().id(), weight9));
|
|
|
|
assertEquals(0, distributor.numChildren(9));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void priorityOnlyStreamsArePreservedWhenReservedStreamsAreClosed() throws Http2Exception {
|
|
|
|
setup(1);
|
|
|
|
|
|
|
|
short weight3 = MIN_WEIGHT;
|
|
|
|
setPriority(3, connection.connectionStream().id(), weight3, true);
|
|
|
|
|
|
|
|
Http2Stream streamA = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamB = connection.remote().reservePushStream(4, streamA);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(3, connection.connectionStream().id(), weight3));
|
|
|
|
assertEquals(0, distributor.numChildren(3));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
// Close both streams.
|
|
|
|
streamB.close();
|
|
|
|
streamA.close();
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(3, connection.connectionStream().id(), weight3));
|
|
|
|
assertEquals(0, distributor.numChildren(3));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void insertExclusiveShouldAddNewLevel() throws Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
assertEquals(4, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void existingChildMadeExclusiveShouldNotCreateTreeCycle() throws Http2Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Stream C is already dependent on Stream A, but now make that an exclusive dependency
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
assertEquals(4, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void newExclusiveChildShouldUpdateOldParentCorrectly() throws Http2Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
Http2Stream streamE = connection.local().createStream(9, false);
|
|
|
|
Http2Stream streamF = connection.local().createStream(11, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamF.id(), streamE.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// F is now going to be exclusively dependent on A, after this we should check that stream E
|
|
|
|
// prioritizableForTree is not over decremented.
|
|
|
|
setPriority(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
assertEquals(6, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamF.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamF.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamF.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
// Level 4
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void weightChangeWithNoTreeChangeShouldBeRespected() throws Http2Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
assertEquals(4, connection.numActiveStreams());
|
|
|
|
|
|
|
|
short newWeight = (short) (DEFAULT_PRIORITY_WEIGHT + 1);
|
|
|
|
setPriority(streamD.id(), streamA.id(), newWeight, false);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamA.id(), newWeight));
|
|
|
|
assertEquals(2, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void sameNodeDependentShouldNotStackOverflowNorChangePrioritizableForTree() throws Http2Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
2017-03-09 17:10:38 +01:00
|
|
|
boolean[] exclusives = { true, false };
|
|
|
|
short[] weights = { DEFAULT_PRIORITY_WEIGHT, 100, 200, DEFAULT_PRIORITY_WEIGHT };
|
2017-01-24 21:50:39 +01:00
|
|
|
|
|
|
|
assertEquals(4, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// The goal is to call setPriority with the same parent and vary the parameters
|
|
|
|
// we were at one point adding a circular depends to the tree and then throwing
|
|
|
|
// a StackOverflow due to infinite recursive operation.
|
|
|
|
for (short weight : weights) {
|
|
|
|
for (boolean exclusive : exclusives) {
|
|
|
|
setPriority(streamD.id(), streamA.id(), weight, exclusive);
|
|
|
|
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
assertEquals(2, distributor.numChildren(streamD.id()));
|
|
|
|
assertFalse(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertFalse(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamA.id(), weight));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void multipleCircularDependencyShouldUpdatePrioritizable() throws Http2Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
assertEquals(4, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Bring B to the root
|
|
|
|
setPriority(streamA.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
// Move all streams to be children of B
|
|
|
|
setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Move A back to the root
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
// Move all streams to be children of A
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(3, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void removeWithPrioritizableDependentsShouldNotRestructureTree() throws Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Default removal policy will cause it to be removed immediately.
|
|
|
|
streamB.close();
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void closeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
Http2Stream streamE = connection.local().createStream(9, false);
|
|
|
|
Http2Stream streamF = connection.local().createStream(11, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Close internal nodes, leave 1 leaf node open, the only remaining stream is the one that is not closed (E).
|
|
|
|
streamA.close();
|
|
|
|
streamB.close();
|
|
|
|
streamC.close();
|
|
|
|
streamD.close();
|
|
|
|
streamF.close();
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void priorityChangeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception {
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
Http2Stream streamE = connection.local().createStream(9, false);
|
|
|
|
Http2Stream streamF = connection.local().createStream(11, false);
|
|
|
|
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamC.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamD.id(), streamB.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Leave leaf nodes open (E & F)
|
|
|
|
streamA.close();
|
|
|
|
streamB.close();
|
|
|
|
streamC.close();
|
|
|
|
streamD.close();
|
|
|
|
|
|
|
|
// Move F to depend on C, even though C is closed.
|
|
|
|
setPriority(streamF.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamF.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void circularDependencyShouldRestructureTree() throws Exception {
|
|
|
|
// Using example from https://tools.ietf.org/html/rfc7540#section-5.3.3
|
|
|
|
// Initialize all the nodes
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
Http2Stream streamE = connection.local().createStream(9, false);
|
|
|
|
Http2Stream streamF = connection.local().createStream(11, false);
|
|
|
|
|
|
|
|
assertEquals(6, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamA.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamF.id()));
|
|
|
|
|
|
|
|
// Build the tree
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(5, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(4, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
assertEquals(6, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Non-exclusive re-prioritization of a->d.
|
|
|
|
setPriority(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamF.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
// Level 4
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Test
|
|
|
|
public void circularDependencyWithExclusiveShouldRestructureTree() throws Exception {
|
|
|
|
// Using example from https://tools.ietf.org/html/rfc7540#section-5.3.3
|
|
|
|
// Initialize all the nodes
|
|
|
|
Http2Stream streamA = connection.local().createStream(1, false);
|
|
|
|
Http2Stream streamB = connection.local().createStream(3, false);
|
|
|
|
Http2Stream streamC = connection.local().createStream(5, false);
|
|
|
|
Http2Stream streamD = connection.local().createStream(7, false);
|
|
|
|
Http2Stream streamE = connection.local().createStream(9, false);
|
|
|
|
Http2Stream streamF = connection.local().createStream(11, false);
|
|
|
|
|
|
|
|
assertEquals(6, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamA.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamC.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamD.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
assertEquals(0, distributor.numChildren(streamF.id()));
|
|
|
|
|
|
|
|
// Build the tree
|
|
|
|
setPriority(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(5, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
setPriority(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(4, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
setPriority(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(3, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
setPriority(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(2, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(2, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
setPriority(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, false);
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
assertEquals(6, connection.numActiveStreams());
|
|
|
|
|
|
|
|
// Exclusive re-prioritization of a->d.
|
|
|
|
setPriority(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT, true);
|
|
|
|
|
|
|
|
// Level 0
|
|
|
|
assertEquals(1, distributor.numChildren(connection.connectionStream().id()));
|
|
|
|
|
|
|
|
// Level 1
|
|
|
|
assertTrue(distributor.isChild(streamD.id(), connection.connectionStream().id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamD.id()));
|
|
|
|
|
|
|
|
// Level 2
|
|
|
|
assertTrue(distributor.isChild(streamA.id(), streamD.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(3, distributor.numChildren(streamA.id()));
|
|
|
|
|
|
|
|
// Level 3
|
|
|
|
assertTrue(distributor.isChild(streamB.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamB.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamF.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamF.id()));
|
|
|
|
|
|
|
|
assertTrue(distributor.isChild(streamC.id(), streamA.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(1, distributor.numChildren(streamC.id()));
|
|
|
|
|
|
|
|
// Level 4;
|
|
|
|
assertTrue(distributor.isChild(streamE.id(), streamC.id(), DEFAULT_PRIORITY_WEIGHT));
|
|
|
|
assertEquals(0, distributor.numChildren(streamE.id()));
|
|
|
|
}
|
|
|
|
}
|