|
Java example source code file (DefaultHttp2ConnectionTest.java)
The DefaultHttp2ConnectionTest.java Java example source code/* * Copyright 2014 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 io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.DefaultEventLoopGroup; import io.netty.handler.codec.http2.Http2Connection.Endpoint; import io.netty.handler.codec.http2.Http2Stream.State; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise; import io.netty.util.internal.PlatformDependent; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; import static io.netty.handler.codec.http2.Http2CodecUtil.MIN_WEIGHT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyShort; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** * Tests for {@link DefaultHttp2Connection}. */ public class DefaultHttp2ConnectionTest { @Rule public ExpectedException thrown = ExpectedException.none(); private DefaultHttp2Connection server; private DefaultHttp2Connection client; private static DefaultEventLoopGroup group; @Mock private Http2Connection.Listener clientListener; @Mock private Http2Connection.Listener clientListener2; @BeforeClass public static void beforeClass() { group = new DefaultEventLoopGroup(2); } @AfterClass public static void afterClass() { group.shutdownGracefully(); } @Before public void setup() { MockitoAnnotations.initMocks(this); server = new DefaultHttp2Connection(true); client = new DefaultHttp2Connection(false); client.addListener(clientListener); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { assertNotNull(client.stream(invocation.getArgumentAt(0, Http2Stream.class).id())); return null; } }).when(clientListener).onStreamClosed(any(Http2Stream.class)); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { assertNull(client.stream(invocation.getArgumentAt(0, Http2Stream.class).id())); return null; } }).when(clientListener).onStreamRemoved(any(Http2Stream.class)); } @Test public void getStreamWithoutStreamShouldReturnNull() { assertNull(server.stream(100)); } @Test public void removeAllStreamsWithEmptyStreams() throws InterruptedException { testRemoveAllStreams(); } @Test public void removeAllStreamsWithJustOneLocalStream() throws InterruptedException, Http2Exception { client.local().createStream(3, false); testRemoveAllStreams(); } @Test public void removeAllStreamsWithJustOneRemoveStream() throws InterruptedException, Http2Exception { client.remote().createStream(2, false); testRemoveAllStreams(); } @Test public void removeAllStreamsWithManyActiveStreams() throws InterruptedException, Http2Exception { Endpoint<Http2RemoteFlowController> remote = client.remote(); Endpoint<Http2LocalFlowController> local = client.local(); for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { local.createStream(c, false); remote.createStream(s, false); } testRemoveAllStreams(); } @Test public void removeAllStreamsWithNonActiveStreams() throws InterruptedException, Http2Exception { client.local().createIdleStream(3); client.remote().createIdleStream(2); testRemoveAllStreams(); } @Test public void removeAllStreamsWithNonActiveAndActiveStreams() throws InterruptedException, Http2Exception { client.local().createIdleStream(3); client.remote().createIdleStream(2); client.local().createStream(5, false); client.remote().createStream(4, true); testRemoveAllStreams(); } @Test public void removeIndividualStreamsWhileCloseDoesNotNPE() throws InterruptedException, Http2Exception { final Http2Stream streamA = client.local().createStream(3, false); final Http2Stream streamB = client.remote().createStream(2, false); doAnswer(new Answer<Void>() { @Override public Void answer(InvocationOnMock invocation) throws Throwable { streamA.close(); streamB.close(); return null; } }).when(clientListener2).onStreamClosed(any(Http2Stream.class)); try { client.addListener(clientListener2); testRemoveAllStreams(); } finally { client.removeListener(clientListener2); } } @Test public void removeAllStreamsWhileIteratingActiveStreams() throws InterruptedException, Http2Exception { final Endpoint<Http2RemoteFlowController> remote = client.remote(); final Endpoint<Http2LocalFlowController> local = client.local(); for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { local.createStream(c, false); remote.createStream(s, false); } final Promise<Void> promise = group.next().newPromise(); final CountDownLatch latch = new CountDownLatch(client.numActiveStreams()); client.forEachActiveStream(new Http2StreamVisitor() { @Override public boolean visit(Http2Stream stream) throws Http2Exception { client.close(promise).addListener(new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { assertTrue(promise.isDone()); latch.countDown(); } }); return true; } }); assertTrue(latch.await(2, TimeUnit.SECONDS)); } @Test public void removeAllStreamsWhileIteratingActiveStreamsAndExceptionOccurs() throws InterruptedException, Http2Exception { final Endpoint<Http2RemoteFlowController> remote = client.remote(); final Endpoint<Http2LocalFlowController> local = client.local(); for (int c = 3, s = 2; c < 5000; c += 2, s += 2) { local.createStream(c, false); remote.createStream(s, false); } final Promise<Void> promise = group.next().newPromise(); final CountDownLatch latch = new CountDownLatch(1); try { client.forEachActiveStream(new Http2StreamVisitor() { @Override public boolean visit(Http2Stream stream) throws Http2Exception { // This close call is basically a noop, because the following statement will throw an exception. client.close(promise); // Do an invalid operation while iterating. remote.createStream(3, false); return true; } }); } catch (Http2Exception ignored) { client.close(promise).addListener(new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { assertTrue(promise.isDone()); latch.countDown(); } }); } assertTrue(latch.await(2, TimeUnit.SECONDS)); } @Test public void closeWhileIteratingDoesNotNPE() throws Http2Exception { final Http2Stream streamA = client.local().createStream(3, false); final Http2Stream streamB = client.local().createStream(5, false); final Http2Stream streamC = client.local().createStream(7, false); streamB.setPriority(streamA.id(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT, false); client.forEachActiveStream(new Http2StreamVisitor() { @Override public boolean visit(Http2Stream stream) throws Http2Exception { streamA.close(); streamB.setPriority(streamC.id(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT, false); return true; } }); } @Test public void goAwayReceivedShouldCloseStreamsGreaterThanLastStream() throws Exception { Http2Stream stream1 = client.local().createStream(3, false); Http2Stream stream2 = client.local().createStream(5, false); Http2Stream remoteStream = client.remote().createStream(4, false); assertEquals(State.OPEN, stream1.state()); assertEquals(State.OPEN, stream2.state()); client.goAwayReceived(3, 8, null); assertEquals(State.OPEN, stream1.state()); assertEquals(State.CLOSED, stream2.state()); assertEquals(State.OPEN, remoteStream.state()); assertEquals(3, client.local().lastStreamKnownByPeer()); assertEquals(5, client.local().lastStreamCreated()); // The remote endpoint must not be affected by a received GOAWAY frame. assertEquals(-1, client.remote().lastStreamKnownByPeer()); assertEquals(State.OPEN, remoteStream.state()); } @Test public void goAwaySentShouldCloseStreamsGreaterThanLastStream() throws Exception { Http2Stream stream1 = server.remote().createStream(3, false); Http2Stream stream2 = server.remote().createStream(5, false); Http2Stream localStream = server.local().createStream(4, false); server.goAwaySent(3, 8, null); assertEquals(State.OPEN, stream1.state()); assertEquals(State.CLOSED, stream2.state()); assertEquals(3, server.remote().lastStreamKnownByPeer()); assertEquals(5, server.remote().lastStreamCreated()); // The local endpoint must not be affected by a sent GOAWAY frame. assertEquals(-1, server.local().lastStreamKnownByPeer()); assertEquals(State.OPEN, localStream.state()); } @Test public void serverCreateStreamShouldSucceed() throws Http2Exception { Http2Stream stream = server.local().createStream(2, false); assertEquals(2, stream.id()); assertEquals(State.OPEN, stream.state()); assertEquals(1, server.numActiveStreams()); assertEquals(2, server.local().lastStreamCreated()); stream = server.local().createStream(4, true); assertEquals(4, stream.id()); assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); assertEquals(2, server.numActiveStreams()); assertEquals(4, server.local().lastStreamCreated()); stream = server.remote().createStream(3, true); assertEquals(3, stream.id()); assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); assertEquals(3, server.numActiveStreams()); assertEquals(3, server.remote().lastStreamCreated()); stream = server.remote().createStream(5, false); assertEquals(5, stream.id()); assertEquals(State.OPEN, stream.state()); assertEquals(4, server.numActiveStreams()); assertEquals(5, server.remote().lastStreamCreated()); } @Test public void clientCreateStreamShouldSucceed() throws Http2Exception { Http2Stream stream = client.remote().createStream(2, false); assertEquals(2, stream.id()); assertEquals(State.OPEN, stream.state()); assertEquals(1, client.numActiveStreams()); assertEquals(2, client.remote().lastStreamCreated()); stream = client.remote().createStream(4, true); assertEquals(4, stream.id()); assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); assertEquals(2, client.numActiveStreams()); assertEquals(4, client.remote().lastStreamCreated()); stream = client.local().createStream(3, true); assertEquals(3, stream.id()); assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); assertEquals(3, client.numActiveStreams()); assertEquals(3, client.local().lastStreamCreated()); stream = client.local().createStream(5, false); assertEquals(5, stream.id()); assertEquals(State.OPEN, stream.state()); assertEquals(4, client.numActiveStreams()); assertEquals(5, client.local().lastStreamCreated()); } @Test public void serverReservePushStreamShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); Http2Stream pushStream = server.local().reservePushStream(2, stream); assertEquals(2, pushStream.id()); assertEquals(State.RESERVED_LOCAL, pushStream.state()); assertEquals(1, server.numActiveStreams()); assertEquals(2, server.local().lastStreamCreated()); } @Test public void clientReservePushStreamShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); Http2Stream pushStream = server.local().reservePushStream(4, stream); assertEquals(4, pushStream.id()); assertEquals(State.RESERVED_LOCAL, pushStream.state()); assertEquals(1, server.numActiveStreams()); assertEquals(4, server.local().lastStreamCreated()); } @Test public void serverRemoteIncrementAndGetStreamShouldSucceed() throws Http2Exception { incrementAndGetStreamShouldSucceed(server.remote()); } @Test public void serverLocalIncrementAndGetStreamShouldSucceed() throws Http2Exception { incrementAndGetStreamShouldSucceed(server.local()); } @Test public void clientRemoteIncrementAndGetStreamShouldSucceed() throws Http2Exception { incrementAndGetStreamShouldSucceed(client.remote()); } @Test public void clientLocalIncrementAndGetStreamShouldSucceed() throws Http2Exception { incrementAndGetStreamShouldSucceed(client.local()); } @Test(expected = Http2NoMoreStreamIdsException.class) public void serverRemoteIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { incrementAndGetStreamShouldRespectOverflow(server.remote(), Integer.MAX_VALUE); } @Test(expected = Http2NoMoreStreamIdsException.class) public void serverLocalIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { incrementAndGetStreamShouldRespectOverflow(server.local(), Integer.MAX_VALUE - 1); } @Test(expected = Http2NoMoreStreamIdsException.class) public void clientRemoteIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { incrementAndGetStreamShouldRespectOverflow(client.remote(), Integer.MAX_VALUE - 1); } @Test(expected = Http2NoMoreStreamIdsException.class) public void clientLocalIncrementAndGetStreamShouldRespectOverflow() throws Http2Exception { incrementAndGetStreamShouldRespectOverflow(client.local(), Integer.MAX_VALUE); } @Test(expected = Http2Exception.class) public void newStreamBehindExpectedShouldThrow() throws Http2Exception { server.local().createStream(0, true); } @Test(expected = Http2Exception.class) public void newStreamNotForServerShouldThrow() throws Http2Exception { server.local().createStream(11, true); } @Test(expected = Http2Exception.class) public void newStreamNotForClientShouldThrow() throws Http2Exception { client.local().createStream(10, true); } @Test(expected = Http2Exception.class) public void createShouldThrowWhenMaxAllowedStreamsExceeded() throws Http2Exception { server.local().maxActiveStreams(0); server.local().createStream(2, true); } @Test public void createIdleShouldSucceedWhenMaxAllowedStreamsExceeded() throws Http2Exception { server.local().maxActiveStreams(0); Http2Stream stream = server.local().createIdleStream(2); // Opening should fail, however. thrown.expect(Http2Exception.class); thrown.expectMessage("Maximum active streams violated for this endpoint."); stream.open(false); } @Test(expected = Http2Exception.class) public void reserveWithPushDisallowedShouldThrow() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); server.remote().allowPushTo(false); server.local().reservePushStream(2, stream); } @Test(expected = Http2Exception.class) public void goAwayReceivedShouldDisallowCreation() throws Http2Exception { server.goAwayReceived(0, 1L, Unpooled.EMPTY_BUFFER); server.remote().createStream(3, true); } @Test public void closeShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); stream.close(); assertEquals(State.CLOSED, stream.state()); assertEquals(0, server.numActiveStreams()); } @Test public void closeLocalWhenOpenShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, false); stream.closeLocalSide(); assertEquals(State.HALF_CLOSED_LOCAL, stream.state()); assertEquals(1, server.numActiveStreams()); } @Test public void closeRemoteWhenOpenShouldSucceed() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, false); stream.closeRemoteSide(); assertEquals(State.HALF_CLOSED_REMOTE, stream.state()); assertEquals(1, server.numActiveStreams()); } @Test public void closeOnlyOpenSideShouldClose() throws Http2Exception { Http2Stream stream = server.remote().createStream(3, true); stream.closeLocalSide(); assertEquals(State.CLOSED, stream.state()); assertEquals(0, server.numActiveStreams()); } @SuppressWarnings("NumericOverflow") @Test(expected = Http2Exception.class) public void localStreamInvalidStreamIdShouldThrow() throws Http2Exception { client.local().createStream(Integer.MAX_VALUE + 2, false); } @SuppressWarnings("NumericOverflow") @Test(expected = Http2Exception.class) public void remoteStreamInvalidStreamIdShouldThrow() throws Http2Exception { client.remote().createStream(Integer.MAX_VALUE + 1, false); } @Test public void localStreamCanDependUponIdleStream() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); streamA.setPriority(3, MIN_WEIGHT, true); verifyDependUponIdleStream(streamA, client.stream(3), client.local()); } @Test public void remoteStreamCanDependUponIdleStream() throws Http2Exception { Http2Stream streamA = client.remote().createStream(2, false); streamA.setPriority(4, MIN_WEIGHT, true); verifyDependUponIdleStream(streamA, client.stream(4), client.remote()); } @Test public void prioritizeShouldUseDefaults() throws Exception { Http2Stream stream = client.local().createStream(1, false); assertEquals(1, client.connectionStream().numChildren()); assertEquals(stream, child(client.connectionStream(), 1)); assertEquals(DEFAULT_PRIORITY_WEIGHT, stream.weight()); assertEquals(0, stream.parent().id()); assertEquals(0, stream.numChildren()); } @Test public void reprioritizeWithNoChangeShouldDoNothing() throws Exception { Http2Stream stream = client.local().createStream(1, false); stream.setPriority(0, DEFAULT_PRIORITY_WEIGHT, false); assertEquals(1, client.connectionStream().numChildren()); assertEquals(stream, child(client.connectionStream(), 1)); assertEquals(DEFAULT_PRIORITY_WEIGHT, stream.weight()); assertEquals(0, stream.parent().id()); assertEquals(0, stream.numChildren()); } @Test public void insertExclusiveShouldAddNewLevel() throws Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); assertEquals(4, client.numActiveStreams()); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamA.id()); assertNotNull(p); assertEquals(0, p.parent().id()); assertEquals(1, p.numChildren()); // Level 2 p = child(p, streamD.id()); assertNotNull(p); assertEquals(streamA.id(), p.parent().id()); assertEquals(2, p.numChildren()); // Level 3 p = child(p, streamB.id()); assertNotNull(p); assertEquals(streamD.id(), p.parent().id()); assertEquals(0, p.numChildren()); p = child(p.parent(), streamC.id()); assertNotNull(p); assertEquals(streamD.id(), p.parent().id()); assertEquals(0, p.numChildren()); } @Test public void existingChildMadeExclusiveShouldNotCreateTreeCycle() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); // Stream C is already dependent on Stream A, but now make that an exclusive dependency streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); assertEquals(4, client.numActiveStreams()); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamA.id()); assertNotNull(p); assertEquals(0, p.parent().id()); assertEquals(1, p.numChildren()); // Level 2 p = child(p, streamC.id()); assertNotNull(p); assertEquals(streamA.id(), p.parent().id()); assertEquals(2, p.numChildren()); // Level 3 p = child(p, streamB.id()); assertNotNull(p); assertEquals(streamC.id(), p.parent().id()); assertEquals(0, p.numChildren()); p = child(p.parent(), streamD.id()); assertNotNull(p); assertEquals(streamC.id(), p.parent().id()); assertEquals(0, p.numChildren()); } @Test public void newExclusiveChildShouldUpdateOldParentCorrectly() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); Http2Stream streamE = client.local().createStream(9, false); Http2Stream streamF = client.local().createStream(11, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); streamF.setPriority(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. streamF.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); assertEquals(6, client.numActiveStreams()); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(2, p.numChildren()); // Level 1 p = child(p, streamE.id()); assertNotNull(p); assertEquals(0, p.parent().id()); assertEquals(0, p.numChildren()); p = child(p.parent(), streamA.id()); assertNotNull(p); assertEquals(0, p.parent().id()); assertEquals(1, p.numChildren()); // Level 2 p = child(p, streamF.id()); assertNotNull(p); assertEquals(streamA.id(), p.parent().id()); assertEquals(2, p.numChildren()); // Level 3 p = child(p, streamB.id()); assertNotNull(p); assertEquals(streamF.id(), p.parent().id()); assertEquals(0, p.numChildren()); p = child(p.parent(), streamC.id()); assertNotNull(p); assertEquals(streamF.id(), p.parent().id()); assertEquals(1, p.numChildren()); // Level 4 p = child(p, streamD.id()); assertNotNull(p); assertEquals(streamC.id(), p.parent().id()); assertEquals(0, p.numChildren()); } @Test public void weightChangeWithNoTreeChangeShouldNotifyListeners() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); assertEquals(4, client.numActiveStreams()); short oldWeight = streamD.weight(); short newWeight = (short) (oldWeight + 1); reset(clientListener); streamD.setPriority(streamD.parent().id(), newWeight, false); verify(clientListener).onWeightChanged(eq(streamD), eq(oldWeight)); assertEquals(streamD.weight(), newWeight); verify(clientListener, never()).onPriorityTreeParentChanging(any(Http2Stream.class), any(Http2Stream.class)); verify(clientListener, never()).onPriorityTreeParentChanged(any(Http2Stream.class), any(Http2Stream.class)); } @Test public void sameNodeDependentShouldNotStackOverflowNorChangePrioritizableForTree() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); boolean[] exclusive = new boolean[] {true, false}; short[] weights = new short[] { DEFAULT_PRIORITY_WEIGHT, 100, 200, streamD.weight() }; assertEquals(4, client.numActiveStreams()); Http2Stream connectionStream = client.connectionStream(); // 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 (int j = 0; j < weights.length; ++j) { for (int i = 0; i < exclusive.length; ++i) { streamD.setPriority(streamA.id(), weights[j], exclusive[i]); } } } @Test public void multipleCircularDependencyShouldUpdatePrioritizable() throws Http2Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); assertEquals(4, client.numActiveStreams()); Http2Stream connectionStream = client.connectionStream(); // Bring B to the root streamA.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, true); // Move all streams to be children of B streamC.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); // Move A back to the root streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, true); // Move all streams to be children of A streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); } @Test public void removeWithPrioritizableDependentsShouldNotRestructureTree() throws Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); // Default removal policy will cause it to be removed immediately. streamB.close(); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamA.id()); assertNotNull(p); assertEquals(client.connectionStream().id(), p.parent().id()); assertEquals(2, p.numChildren()); // Level 2 p = child(p, streamC.id()); assertNotNull(p); assertEquals(streamA.id(), p.parent().id()); assertEquals(0, p.numChildren()); p = child(p.parent(), streamD.id()); assertNotNull(p); assertEquals(streamA.id(), p.parent().id()); assertEquals(0, p.numChildren()); } @Test public void closeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); Http2Stream streamE = client.local().createStream(9, false); Http2Stream streamF = client.local().createStream(11, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamE.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); streamF.setPriority(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 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamE.id()); assertNotNull(p); assertEquals(client.connectionStream().id(), p.parent().id()); assertEquals(0, p.numChildren()); } @Test(expected = Http2Exception.class) public void priorityChangeWithNoPrioritizableDependentsShouldRestructureTree() throws Exception { Http2Stream streamA = client.local().createStream(1, false); Http2Stream streamB = client.local().createStream(3, false); Http2Stream streamC = client.local().createStream(5, false); Http2Stream streamD = client.local().createStream(7, false); Http2Stream streamE = client.local().createStream(9, false); Http2Stream streamF = client.local().createStream(11, false); streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); streamC.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamD.setPriority(streamB.id(), DEFAULT_PRIORITY_WEIGHT, false); streamE.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); streamF.setPriority(streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); // Leave leaf nodes open (E & F) streamA.close(); streamB.close(); streamC.close(); streamD.close(); // Attempt to move F to depend on C, however this should throw an exception because C is closed. streamF.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); } @Test public void circularDependencyShouldRestructureTree() throws Exception { // Using example from http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.3.3 // Initialize all the nodes Http2Stream streamA = client.local().createStream(1, false); verifyParentChanged(streamA, null); Http2Stream streamB = client.local().createStream(3, false); verifyParentChanged(streamB, null); Http2Stream streamC = client.local().createStream(5, false); verifyParentChanged(streamC, null); Http2Stream streamD = client.local().createStream(7, false); verifyParentChanged(streamD, null); Http2Stream streamE = client.local().createStream(9, false); verifyParentChanged(streamE, null); Http2Stream streamF = client.local().createStream(11, false); verifyParentChanged(streamF, null); // Build the tree streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamB), anyShort()); verifyParentChanged(streamB, client.connectionStream()); verifyParentChanging(streamB, client.connectionStream()); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamC), anyShort()); verifyParentChanged(streamC, client.connectionStream()); verifyParentChanging(streamC, client.connectionStream()); streamD.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamD), anyShort()); verifyParentChanged(streamD, client.connectionStream()); verifyParentChanging(streamD, client.connectionStream()); streamE.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamE), anyShort()); verifyParentChanged(streamE, client.connectionStream()); verifyParentChanging(streamE, client.connectionStream()); streamF.setPriority(streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamF), anyShort()); verifyParentChanged(streamF, client.connectionStream()); verifyParentChanging(streamF, client.connectionStream()); assertEquals(6, client.numActiveStreams()); // Non-exclusive re-prioritization of a->d. reset(clientListener); streamA.setPriority(streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamA), anyShort()); verifyParentChanging(Arrays.asList(streamD, streamA), Arrays.asList(client.connectionStream(), streamD)); verifyParentsChanged(Arrays.asList(streamD, streamA), Arrays.asList(streamC, client.connectionStream())); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamD.id()); assertNotNull(p); assertEquals(2, p.numChildren()); // Level 2 p = child(p, streamF.id()); assertNotNull(p); assertEquals(0, p.numChildren()); p = child(p.parent(), streamA.id()); assertNotNull(p); assertEquals(2, p.numChildren()); // Level 3 p = child(p, streamB.id()); assertNotNull(p); assertEquals(0, p.numChildren()); p = child(p.parent(), streamC.id()); assertNotNull(p); assertEquals(1, p.numChildren()); // Level 4; p = child(p, streamE.id()); assertNotNull(p); assertEquals(0, p.numChildren()); } @Test public void circularDependencyWithExclusiveShouldRestructureTree() throws Exception { // Using example from http://tools.ietf.org/html/draft-ietf-httpbis-http2-16#section-5.3.3 // Initialize all the nodes Http2Stream streamA = client.local().createStream(1, false); verifyParentChanged(streamA, null); Http2Stream streamB = client.local().createStream(3, false); verifyParentChanged(streamB, null); Http2Stream streamC = client.local().createStream(5, false); verifyParentChanged(streamC, null); Http2Stream streamD = client.local().createStream(7, false); verifyParentChanged(streamD, null); Http2Stream streamE = client.local().createStream(9, false); verifyParentChanged(streamE, null); Http2Stream streamF = client.local().createStream(11, false); verifyParentChanged(streamF, null); // Build the tree streamB.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamB), anyShort()); verifyParentChanged(streamB, client.connectionStream()); verifyParentChanging(streamB, client.connectionStream()); streamC.setPriority(streamA.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamC), anyShort()); verifyParentChanged(streamC, client.connectionStream()); verifyParentChanging(streamC, client.connectionStream()); streamD.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamD), anyShort()); verifyParentChanged(streamD, client.connectionStream()); verifyParentChanging(streamD, client.connectionStream()); streamE.setPriority(streamC.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamE), anyShort()); verifyParentChanged(streamE, client.connectionStream()); verifyParentChanging(streamE, client.connectionStream()); streamF.setPriority(streamD.id(), DEFAULT_PRIORITY_WEIGHT, false); verify(clientListener, never()).onWeightChanged(eq(streamF), anyShort()); verifyParentChanged(streamF, client.connectionStream()); verifyParentChanging(streamF, client.connectionStream()); assertEquals(6, client.numActiveStreams()); // Exclusive re-prioritization of a->d. reset(clientListener); streamA.setPriority(streamD.id(), DEFAULT_PRIORITY_WEIGHT, true); verify(clientListener, never()).onWeightChanged(eq(streamA), anyShort()); verifyParentChanging(Arrays.asList(streamD, streamA, streamF), Arrays.asList(client.connectionStream(), streamD, streamA)); verifyParentsChanged(Arrays.asList(streamD, streamA, streamF), Arrays.asList(streamC, client.connectionStream(), streamD)); // Level 0 Http2Stream p = client.connectionStream(); assertEquals(1, p.numChildren()); // Level 1 p = child(p, streamD.id()); assertNotNull(p); assertEquals(1, p.numChildren()); // Level 2 p = child(p, streamA.id()); assertNotNull(p); assertEquals(3, p.numChildren()); // Level 3 p = child(p, streamB.id()); assertNotNull(p); assertEquals(0, p.numChildren()); p = child(p.parent(), streamF.id()); assertNotNull(p); assertEquals(0, p.numChildren()); p = child(p.parent(), streamC.id()); assertNotNull(p); assertEquals(1, p.numChildren()); // Level 4; p = child(p, streamE.id()); assertNotNull(p); assertEquals(0, p.numChildren()); } /** * We force {@link #clientListener} methods to all throw a {@link RuntimeException} and verify the following: * <ol> * <li>all listener methods are called for both {@link #clientListener} and {@link #clientListener2} * <li>{@link #clientListener2} is notified after {@link #clientListener} * <li>{@link #clientListener2} methods are all still called despite {@link #clientListener}'s * method throwing a {@link RuntimeException}</li> * </ol> */ @Test public void listenerThrowShouldNotPreventOtherListenersFromBeingNotified() throws Http2Exception { final boolean[] calledArray = new boolean[128]; // The following setup will ensure that clienListener throws exceptions, and marks a value in an array // such that clientListener2 will verify that is is set or fail the test. int methodIndex = 0; doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamAdded(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamAdded(any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamActive(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamActive(any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamHalfClosed(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamHalfClosed(any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamClosed(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamClosed(any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamRemoved(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamRemoved(any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onPriorityTreeParentChanged(any(Http2Stream.class), any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onPriorityTreeParentChanged(any(Http2Stream.class), any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onPriorityTreeParentChanging(any(Http2Stream.class), any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onPriorityTreeParentChanging(any(Http2Stream.class), any(Http2Stream.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onWeightChanged(any(Http2Stream.class), anyShort()); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onWeightChanged(any(Http2Stream.class), anyShort()); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); doAnswer(new ListenerExceptionThrower(calledArray, methodIndex)) .when(clientListener).onStreamAdded(any(Http2Stream.class)); doAnswer(new ListenerVerifyCallAnswer(calledArray, methodIndex++)) .when(clientListener2).onStreamAdded(any(Http2Stream.class)); // Now we add clienListener2 and exercise all listener functionality try { client.addListener(clientListener2); Http2Stream stream = client.local().createIdleStream(3); verify(clientListener).onStreamAdded(any(Http2Stream.class)); verify(clientListener2).onStreamAdded(any(Http2Stream.class)); verify(clientListener, never()).onStreamActive(any(Http2Stream.class)); verify(clientListener2, never()).onStreamActive(any(Http2Stream.class)); stream.open(false); verify(clientListener).onStreamActive(any(Http2Stream.class)); verify(clientListener2).onStreamActive(any(Http2Stream.class)); stream.setPriority(0, (short) (stream.weight() + 1), true); verify(clientListener).onWeightChanged(any(Http2Stream.class), anyShort()); verify(clientListener2).onWeightChanged(any(Http2Stream.class), anyShort()); verify(clientListener).onPriorityTreeParentChanged(any(Http2Stream.class), any(Http2Stream.class)); verify(clientListener2).onPriorityTreeParentChanged(any(Http2Stream.class), any(Http2Stream.class)); verify(clientListener).onPriorityTreeParentChanging(any(Http2Stream.class), any(Http2Stream.class)); verify(clientListener2).onPriorityTreeParentChanging(any(Http2Stream.class), any(Http2Stream.class)); stream.closeLocalSide(); verify(clientListener).onStreamHalfClosed(any(Http2Stream.class)); verify(clientListener2).onStreamHalfClosed(any(Http2Stream.class)); stream.close(); verify(clientListener).onStreamClosed(any(Http2Stream.class)); verify(clientListener2).onStreamClosed(any(Http2Stream.class)); verify(clientListener).onStreamRemoved(any(Http2Stream.class)); verify(clientListener2).onStreamRemoved(any(Http2Stream.class)); client.goAwaySent(client.connectionStream().id(), Http2Error.INTERNAL_ERROR.code(), Unpooled.EMPTY_BUFFER); verify(clientListener).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); verify(clientListener2).onGoAwaySent(anyInt(), anyLong(), any(ByteBuf.class)); client.goAwayReceived(client.connectionStream().id(), Http2Error.INTERNAL_ERROR.code(), Unpooled.EMPTY_BUFFER); verify(clientListener).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); verify(clientListener2).onGoAwayReceived(anyInt(), anyLong(), any(ByteBuf.class)); } finally { client.removeListener(clientListener2); } } private void testRemoveAllStreams() throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final Promise<Void> promise = group.next().newPromise(); client.close(promise).addListener(new FutureListener<Void>() { @Override public void operationComplete(Future<Void> future) throws Exception { assertTrue(promise.isDone()); latch.countDown(); } }); assertTrue(latch.await(2, TimeUnit.SECONDS)); } private void incrementAndGetStreamShouldRespectOverflow(Endpoint<?> endpoint, int streamId) throws Http2Exception { assertTrue(streamId > 0); try { endpoint.createStream(streamId, true); streamId = endpoint.incrementAndGetNextStreamId(); } catch (Throwable t) { fail(); } assertTrue(streamId < 0); endpoint.createStream(streamId, true); } private void incrementAndGetStreamShouldSucceed(Endpoint<?> endpoint) throws Http2Exception { Http2Stream streamA = endpoint.createStream(endpoint.incrementAndGetNextStreamId(), true); Http2Stream streamB = endpoint.createStream(streamA.id() + 2, true); Http2Stream streamC = endpoint.createStream(endpoint.incrementAndGetNextStreamId(), true); assertEquals(streamB.id() + 2, streamC.id()); endpoint.createStream(streamC.id() + 2, true); } private static final class ListenerExceptionThrower implements Answer<Void> { private static final RuntimeException FAKE_EXCEPTION = new RuntimeException("Fake Exception"); private final boolean[] array; private final int index; public ListenerExceptionThrower(boolean[] array, int index) { this.array = array; this.index = index; } @Override public Void answer(InvocationOnMock invocation) throws Throwable { array[index] = true; throw FAKE_EXCEPTION; } } private static final class ListenerVerifyCallAnswer implements Answer<Void> { private final boolean[] array; private final int index; public ListenerVerifyCallAnswer(boolean[] array, int index) { this.array = array; this.index = index; } @Override public Void answer(InvocationOnMock invocation) throws Throwable { assertTrue(array[index]); return null; } } private void verifyParentChanging(List<Http2Stream> expectedArg1, List Other Java examples (source code examples)Here is a short list of links related to this Java DefaultHttp2ConnectionTest.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.