1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/* 2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2011 The Android Open Source Project 3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License. 6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at 7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * 10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software 11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and 14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License. 15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */ 16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage com.squareup.okhttp.internal.spdy; 17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 18e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport com.squareup.okhttp.internal.Util; 19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException; 20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList; 21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays; 22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.List; 23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.TimeUnit; 24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSink; 26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSource; 27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Okio; 28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Source; 29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.After; 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Test; 31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.TestUtil.headerEntries; 33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.CANCEL; 34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.PROTOCOL_ERROR; 35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE; 36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Settings.PERSIST_VALUE; 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_DATA; 38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_HEADERS; 39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_PING; 40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_RST_STREAM; 41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_SETTINGS; 42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_WINDOW_UPDATE; 43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals; 44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse; 45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue; 46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail; 47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class Http2ConnectionTest { 49a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private static final Variant HTTP_2 = new Http2(); 50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private final MockSpdyPeer peer = new MockSpdyPeer(); 51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @After public void tearDown() throws Exception { 53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.close(); 54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 55e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void serverPingsClientHttp2() throws Exception { 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(false, 2, 3); 61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection(peer, HTTP_2); 66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_PING, ping.type); 70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, ping.streamId); 71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(2, ping.payload1); 72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3, ping.payload2); 73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ping.ack); 74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void clientPingsServerHttp2() throws Exception { 77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 5); 82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Ping ping = connection.ping(); 87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ping.roundTripTime() > 0); 88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ping.roundTripTime() < TimeUnit.SECONDS.toNanos(1)); 89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame pingFrame = peer.takeFrame(); 92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, pingFrame.streamId); 93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(1, pingFrame.payload1); 94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0x4f4b6f6b, pingFrame.payload2); // connection.ping() sets this. 95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(pingFrame.ack); 96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void peerHttp2ServerLowersInitialWindowSize() throws Exception { 99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings initial = new Settings(); 102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller initial.set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 1684); 103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings shouldntImpactConnection = new Settings(); 104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller shouldntImpactConnection.set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 3368); 105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().settings(initial); 107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // ACK 108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().settings(shouldntImpactConnection); 109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // ACK 2 110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // HEADERS 111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Default is 64KiB - 1. 116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(65535, connection.peerSettings.getInitialWindowSize(-1)); 117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Verify the peer received the ACK. 119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame ackFrame = peer.takeFrame(); 120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_SETTINGS, ackFrame.type); 121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, ackFrame.streamId); 122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ackFrame.ack); 123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller ackFrame = peer.takeFrame(); 124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_SETTINGS, ackFrame.type); 125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, ackFrame.streamId); 126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ackFrame.ack); 127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // This stream was created *after* the connection settings were adjusted. 129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), false, true); 130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3368, connection.peerSettings.getInitialWindowSize(DEFAULT_INITIAL_WINDOW_SIZE)); 132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(1684, connection.bytesLeftInWriteWindow); // initial wasn't affected. 133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // New Stream is has the most recent initial window size. 134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3368, stream.bytesLeftInWriteWindow); 135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void peerHttp2ServerZerosCompressionTable() throws Exception { 138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean client = false; // Peer is server, so we are client. 139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings settings = new Settings(); 140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller settings.set(Settings.HEADER_TABLE_SIZE, PERSIST_VALUE, 0); 141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = sendHttp2SettingsAndCheckForAck(client, settings); 143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer's settings were read and applied. 145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.peerSettings.getHeaderTableSize()); 146a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Http2.Reader frameReader = (Http2.Reader) connection.readerRunnable.frameReader; 147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, frameReader.hpackReader.maxDynamicTableByteCount()); 148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // TODO: when supported, check the frameWriter's compression table is unaffected. 149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void peerHttp2ClientDisablesPush() throws Exception { 152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean client = false; // Peer is client, so we are server. 153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings settings = new Settings(); 154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller settings.set(Settings.ENABLE_PUSH, 0, 0); // The peer client disables push. 155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = sendHttp2SettingsAndCheckForAck(client, settings); 157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer's settings were read and applied. 159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(connection.peerSettings.getEnablePush(true)); 160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void peerIncreasesMaxFrameSize() throws Exception { 163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int newMaxFrameSize = 0x4001; 164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings settings = new Settings(); 165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller settings.set(Settings.MAX_FRAME_SIZE, 0, newMaxFrameSize); 166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = sendHttp2SettingsAndCheckForAck(true, settings); 168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer's settings were read and applied. 170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(newMaxFrameSize, connection.peerSettings.getMaxFrameSize(-1)); 171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(newMaxFrameSize, connection.frameWriter.maxDataLength()); 172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void receiveGoAwayHttp2() throws Exception { 175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 3 179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 5 180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().goAway(3, PROTOCOL_ERROR, Util.EMPTY_BYTE_ARRAY); 181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA STREAM 3 184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream1 = connection.newStream(headerEntries("a", "android"), true, true); 189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true); 190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure the GO_AWAY that resets stream2 has been received. 191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller BufferedSink sink1 = Okio.buffer(stream1.getSink()); 192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller BufferedSink sink2 = Okio.buffer(stream2.getSink()); 193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink1.writeUtf8("abc"); 194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink2.writeUtf8("abc"); 196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink2.flush(); 197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IOException expected) { 199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage()); 200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink1.writeUtf8("def"); 202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink1.close(); 203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.newStream(headerEntries("c", "cola"), true, true); 205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (IOException expected) { 207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals("shutdown", expected.getMessage()); 208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(stream1.isOpen()); 210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(stream2.isOpen()); 211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(1, connection.openStreamCount()); 212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame synStream1 = peer.takeFrame(); 215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, synStream1.type); 216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame synStream2 = peer.takeFrame(); 217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, synStream2.type); 218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_PING, ping.type); 220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame data1 = peer.takeFrame(); 221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_DATA, data1.type); 222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3, data1.streamId); 223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(Arrays.equals("abcdef".getBytes("UTF-8"), data1.data)); 224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void readSendsWindowUpdateHttp2() throws Exception { 227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowSize = 100; 230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowUpdateThreshold = 50; 231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Write the mocking script. 233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); 235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0; i < 3; i++) { 236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Send frames of summing to size 50, which is windowUpdateThreshold. 237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 3, data(24), 24); 238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 3, data(25), 25); 239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 3, data(1), 1); 240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // connection WINDOW UPDATE 241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // stream WINDOW UPDATE 242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 3, data(0), 0); 244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Play it back. 247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, windowSize); 249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), false, true); 250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, stream.unacknowledgedBytesRead); 251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Source in = stream.getSource(); 253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer buffer = new Buffer(); 254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller buffer.writeAll(in); 255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(-1, in.read(buffer, 1)); 256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(150, buffer.size()); 257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int i = 0; i < 3; i++) { 261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<Integer> windowUpdateStreamIds = new ArrayList<>(2); 262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller for (int j = 0; j < 2; j++) { 263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame windowUpdate = peer.takeFrame(); 264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_WINDOW_UPDATE, windowUpdate.type); 265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller windowUpdateStreamIds.add(windowUpdate.streamId); 266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(windowUpdateThreshold, windowUpdate.windowSizeIncrement); 267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(windowUpdateStreamIds.contains(0)); // connection 269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(windowUpdateStreamIds.contains(3)); // stream 270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 271e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private Buffer data(int byteCount) { 274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Buffer().write(new byte[byteCount]); 275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void serverSendsEmptyDataClientDoesntSendWindowUpdateHttp2() throws Exception { 278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Write the mocking script. 281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); 283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 3, data(0), 0); 284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Play it back. 287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true); 289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(-1, client.getSource().read(new Buffer(), 1)); 290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Verify the peer received what was expected. 292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3, peer.frameCount()); 295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void clientSendsEmptyDataServerDoesntSendWindowUpdateHttp2() throws Exception { 298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Write the mocking script. 301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA 303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); 304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 305e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 306e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Play it back. 307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream client = connection.newStream(headerEntries("b", "banana"), true, true); 309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller BufferedSink out = Okio.buffer(client.getSink()); 310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.write(Util.EMPTY_BYTE_ARRAY); 311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.flush(); 312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.close(); 313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Verify the peer received what was expected. 315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(3, peer.frameCount()); 318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void maxFrameSizeHonored() throws Exception { 321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 322e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller byte[] buff = new byte[peer.maxOutboundDataLength() + 1]; 324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Arrays.fill(buff, (byte) '*'); 325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); 329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA 330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA 331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 337e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.write(buff); 338e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.flush(); 339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller out.close(); 340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(peer.maxOutboundDataLength(), data.data.length); 345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller data = peer.takeFrame(); 346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(1, data.data.length); 347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void pushPromiseStream() throws Exception { 350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 3, headerEntries("a", "android")); 355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller final List<Header> expectedRequestHeaders = Arrays.asList( 356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_METHOD, "GET"), 357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_SCHEME, "https"), 358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_AUTHORITY, "squareup.com"), 359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_PATH, "/cached") 360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller ); 361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().pushPromise(3, 2, expectedRequestHeaders); 362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller final List<Header> expectedResponseHeaders = Arrays.asList( 363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.RESPONSE_STATUS, "200") 364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller ); 365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(true, 2, expectedResponseHeaders); 366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 3, data(0), 0); 367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller RecordingPushObserver observer = new RecordingPushObserver(); 370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connectionBuilder(peer, HTTP_2) 373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .pushObserver(observer).build(); 374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true); 375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(-1, client.getSource().read(new Buffer(), 1)); 376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(expectedRequestHeaders, observer.takeEvent()); 381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(expectedResponseHeaders, observer.takeEvent()); 382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void doublePushPromise() throws Exception { 385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().pushPromise(3, 2, headerEntries("a", "android")); 389e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_REPLY 390e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().pushPromise(3, 2, headerEntries("b", "banana")); 391e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connectionBuilder(peer, HTTP_2).build(); 396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.newStream(headerEntries("b", "banana"), false, true); 397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(PROTOCOL_ERROR, peer.takeFrame().errorCode); 401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void pushPromiseStreamsAutomaticallyCancel() throws Exception { 404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, false); 405e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().pushPromise(3, 2, Arrays.asList( 408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_METHOD, "GET"), 409e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_SCHEME, "https"), 410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_AUTHORITY, "squareup.com"), 411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.TARGET_PATH, "/cached") 412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller )); 413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(true, 2, Arrays.asList( 414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller new Header(Header.RESPONSE_STATUS, "200") 415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller )); 416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connectionBuilder(peer, HTTP_2) 421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .pushObserver(PushObserver.CANCEL).build(); 422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(2, rstStream.streamId); 427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(CANCEL, rstStream.errorCode); 428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private SpdyConnection sendHttp2SettingsAndCheckForAck(boolean client, Settings settings) 431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throws IOException, InterruptedException { 432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(HTTP_2, client); 433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().settings(settings); 434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // ACK 435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, HTTP_2); 441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received the ACK 443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller MockSpdyPeer.InFrame ackFrame = peer.takeFrame(); 444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_SETTINGS, ackFrame.type); 445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, ackFrame.streamId); 446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(ackFrame.ack); 447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure that settings have been applied before returning. 449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return connection; 450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private SpdyConnection connection(MockSpdyPeer peer, Variant variant) throws IOException { 453e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return connectionBuilder(peer, variant).build(); 454e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 455e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 456e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private SpdyConnection.Builder connectionBuilder(MockSpdyPeer peer, Variant variant) 457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller throws IOException { 458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new SpdyConnection.Builder(true, peer.openSocket()) 459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .pushObserver(IGNORE) 460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller .protocol(variant.getProtocol()); 461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller static final PushObserver IGNORE = new PushObserver() { 464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public boolean onRequest(int streamId, List<Header> requestHeaders) { 466e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 468e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 469e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public boolean onHeaders(int streamId, List<Header> responseHeaders, boolean last) { 470e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 471e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 472e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public boolean onData(int streamId, BufferedSource source, int byteCount, 474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller boolean last) throws IOException { 475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.skip(byteCount); 476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public void onReset(int streamId, ErrorCode errorCode) { 480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller }; 482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private static class RecordingPushObserver implements PushObserver { 484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller final List<Object> events = new ArrayList<>(); 485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller public synchronized Object takeEvent() throws InterruptedException { 487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller while (events.isEmpty()) { 488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller wait(); 489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return events.remove(0); 491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public synchronized boolean onRequest(int streamId, List<Header> requestHeaders) { 494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(2, streamId); 495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller events.add(requestHeaders); 496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller notifyAll(); 497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public synchronized boolean onHeaders( 501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int streamId, List<Header> responseHeaders, boolean last) { 502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(2, streamId); 503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(last); 504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller events.add(responseHeaders); 505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller notifyAll(); 506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public synchronized boolean onData( 510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int streamId, BufferedSource source, int byteCount, boolean last) { 511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller events.add(new AssertionError("onData")); 512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller notifyAll(); 513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return false; 514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Override public synchronized void onReset(int streamId, ErrorCode errorCode) { 517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller events.add(new AssertionError("onReset")); 518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller notifyAll(); 519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller} 522