13c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller/* 23c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Copyright (C) 2011 The Android Open Source Project 33c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 43c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 53c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * you may not use this file except in compliance with the License. 63c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * You may obtain a copy of the License at 73c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 83c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 93c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * 103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Unless required by applicable law or agreed to in writing, software 113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * See the License for the specific language governing permissions and 143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * limitations under the License. 153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerpackage com.squareup.okhttp.internal.spdy; 173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Util; 193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException; 203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.InterruptedIOException; 213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList; 223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays; 233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List; 24a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.util.concurrent.CountDownLatch; 253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.TimeUnit; 263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.atomic.AtomicInteger; 27a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.AsyncTimeout; 28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink; 303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.ByteString; 313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio; 32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Sink; 333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source; 343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.After; 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test; 363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.TestUtil.headerEntries; 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.CANCEL; 393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.INTERNAL_ERROR; 403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.INVALID_STREAM; 413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.PROTOCOL_ERROR; 423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.REFUSED_STREAM; 433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.ErrorCode.STREAM_IN_USE; 44c6bd683320121544811f481709b3fdbcbe9b3866Neil Fullerimport static com.squareup.okhttp.internal.spdy.Settings.DEFAULT_INITIAL_WINDOW_SIZE; 453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Settings.PERSIST_VALUE; 463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_DATA; 473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_GOAWAY; 483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_HEADERS; 493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_PING; 503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_RST_STREAM; 513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static com.squareup.okhttp.internal.spdy.Spdy3.TYPE_WINDOW_UPDATE; 523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals; 533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertFalse; 543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertTrue; 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.fail; 563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class Spdy3ConnectionTest { 583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final Variant SPDY3 = new Spdy3(); 593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final MockSpdyPeer peer = new MockSpdyPeer(); 603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @After public void tearDown() throws Exception { 623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.close(); 633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientCreatesStreamAndServerReplies() throws Exception { 663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame() 693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .synReply(false, 1, headerEntries("a", "android")); 70e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("c3po"); 813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, synStream.streamId); 913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, synStream.associatedStreamId); 923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), synStream.headerBlock); 933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame requestData = peer.takeFrame(); 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("c3po".getBytes("UTF-8"), requestData.data)); 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headersOnlyStreamIsClosedAfterReplyHeaders() throws Exception { 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 1023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), false, false); 1063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 1073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders()); 108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure that inFinished has been received. 1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 1103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientCreatesStreamAndServerRepliesWithFin() throws Exception { 1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(true, 1, headerEntries("a", "android")); 1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("b", "banana"), false, true); 1233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received. 1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverCreatesStreamAndClientReplies() throws Exception { 1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final List<Header> pushHeaders = headerEntries( 1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":scheme", "https", 1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":host", "localhost:8888", 1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":method", "GET", 1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":path", "/index.html", 1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":status", "200", 1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":version", "HTTP/1.1", 1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller "content-type", "text/html"); 1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, pushHeaders); 1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 1473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller IncomingStreamHandler handler = new IncomingStreamHandler() { 1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void receive(SpdyStream stream) throws IOException { 1533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 1543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(pushHeaders, stream.getRequestHeaders()); 1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(null, stream.getErrorCode()); 1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("b", "banana"), true); 1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller new SpdyConnection.Builder(true, peer.openSocket()).handler(handler).build(); 1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 1633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 1643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(reply.inFinished); 1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, reply.streamId); 1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), reply.headerBlock); 1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.get()); 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void replyWithNoData() throws Exception { 1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android")); 1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 1783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller IncomingStreamHandler handler = new IncomingStreamHandler() { 1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void receive(SpdyStream stream) throws IOException { 1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("b", "banana"), false); 1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 1853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connectionBuilder(peer, SPDY3).handler(handler).build(); 1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 1913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(reply.inFinished); 1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), reply.headerBlock); 1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.get()); 1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverPingsClient() throws Exception { 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, ping.streamId); 2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, ping.payload2); // ignored in spdy! 2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.ack); 2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientPingsServer() throws Exception { 2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 5); // payload2 ignored in spdy! 2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.roundTripTime() > 0); 2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.roundTripTime() < TimeUnit.SECONDS.toNanos(1)); 2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame pingFrame = peer.takeFrame(); 2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, pingFrame.type); 2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, pingFrame.payload1); 2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, pingFrame.payload2); 2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(pingFrame.ack); 2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void unexpectedPingIsNotReturned() throws Exception { 2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 3, 0); // This ping will not be returned. 2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 4, 0); 2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 2453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping2 = peer.takeFrame(); 2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping2.payload1); 2493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping4 = peer.takeFrame(); 2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(4, ping4.payload1); 2513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverSendsSettingsToClient() throws Exception { 2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Settings settings = new Settings(); 2563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 10); 2573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings); 2583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 2643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.takeFrame(); // Guarantees that the peer Settings frame has been processed. 2663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller synchronized (connection) { 2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(10, connection.peerSettings.getMaxConcurrentStreams(-1)); 2683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void multipleSettingsFramesAreMerged() throws Exception { 2723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Settings settings1 = new Settings(); 2743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100); 2753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200); 2763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300); 2773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings1); 2783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Settings settings2 = new Settings(); 2793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.DOWNLOAD_BANDWIDTH, 0, 400); 2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.DOWNLOAD_RETRANS_RATE, PERSIST_VALUE, 500); 2813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600); 2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings2); 2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); 2853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.takeFrame(); // Guarantees that the Settings frame has been processed. 2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller synchronized (connection) { 2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(100, connection.peerSettings.getUploadBandwidth(-1)); 2933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.UPLOAD_BANDWIDTH)); 2943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(400, connection.peerSettings.getDownloadBandwidth(-1)); 2953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.peerSettings.flags(Settings.DOWNLOAD_BANDWIDTH)); 2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(500, connection.peerSettings.getDownloadRetransRate(-1)); 2973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.DOWNLOAD_RETRANS_RATE)); 2983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1)); 2993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.MAX_CONCURRENT_STREAMS)); 3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 303c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller @Test public void clearSettingsBeforeMerge() throws Exception { 304c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // write the mocking script 305c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller Settings settings1 = new Settings(); 306c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100); 307c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200); 308c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300); 309c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.sendFrame().settings(settings1); 310c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.sendFrame().ping(false, 2, 0); 311c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.acceptFrame(); 312c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.play(); 313c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 314c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // play it back 315c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller SpdyConnection connection = connection(peer, SPDY3); 316c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 317c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.takeFrame(); // Guarantees that the Settings frame has been processed. 318c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 319c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // fake a settings frame with clear flag set. 320c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller Settings settings2 = new Settings(); 321c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600); 322c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller connection.readerRunnable.settings(true, settings2); 323c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 324c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller synchronized (connection) { 325c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getUploadBandwidth(-1)); 326c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getDownloadBandwidth(-1)); 327c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getDownloadRetransRate(-1)); 328c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1)); 329c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller } 330c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller } 331c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 3323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void bogusDataFrameDoesNotDisruptConnection() throws Exception { 3333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 41, new Buffer().writeUtf8("bogus"), 5); 3353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 3363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 3373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 3413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 3423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 3443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 3453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 3463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(41, rstStream.streamId); 3473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INVALID_STREAM, rstStream.errorCode); 3483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void bogusReplyFrameDoesNotDisruptConnection() throws Exception { 3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 41, headerEntries("a", "android")); 3553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 3563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 3613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 3623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 3643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 3663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(41, rstStream.streamId); 3673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INVALID_STREAM, rstStream.errorCode); 3683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 3703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientOutputStream() throws Exception { 3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 3743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 3763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // TYPE_DATA 3773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // TYPE_DATA with FLAG_FIN 3783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 3843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, false); 3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 3863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 3883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 3893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 3903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 3913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("round"); 3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 3933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (Exception expected) { 3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("closed", expected.getMessage()); 3953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received. 3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 3983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 4003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 4013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.outFinished); 4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data.type); 4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(data.inFinished); 4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data)); 4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame fin = peer.takeFrame(); 4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, fin.type); 4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(fin.inFinished); 4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 4133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 4143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, ping.payload1); 4153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverClosesClientOutputStream() throws Exception { 4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 4193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 4203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().rstStream(1, CANCEL); 4213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 4223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 4233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 4243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true); 4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the RST_CANCEL has been received. 4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close throws because buffered data wasn't flushed. 4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 4443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 4463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 4483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 4493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 4513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 4533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, ping.payload1); 4543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 4573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Test that the client sends a RST_STREAM if doing so won't disrupt the 4583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * output stream. 4593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 4603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientInputStream() throws Exception { 4613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 4623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 4633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 4643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 4653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 4683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), false, true); 4693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 4703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 4713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller in.close(); 4723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller in.read(new Buffer(), 1); 4743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream closed", expected.getMessage()); 4773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("a"); 4803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream finished", expected.getMessage()); 4843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 4863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 4883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 4893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 4903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 4913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.inFinished); 4923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 4933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 4943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 4953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(CANCEL, rstStream.errorCode); 4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 4993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Test that the client doesn't send a RST_STREAM if doing so will disrupt 5003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the output stream. 5013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 5023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientInputStreamIfOutputStreamIsClosed() throws Exception { 5033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 5063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA with FLAG_FIN 5073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 5113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 5123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true); 5133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source source = stream.getSource(); 5143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 5153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller source.close(); 5163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.read(new Buffer(), 1); 5183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 5193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 5203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream closed", expected.getMessage()); 5213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 5233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 5243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 5253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 5263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 5323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 5333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 5343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data.type); 5353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data)); 5363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame fin = peer.takeFrame(); 5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, fin.type); 5383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(fin.inFinished); 5393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(fin.outFinished); 5403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(CANCEL, rstStream.errorCode); 5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverClosesClientInputStream() throws Exception { 5463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("square"), 6); 550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 5553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 5563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), false, true); 5573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source source = stream.getSource(); 5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("square", source); 559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure that inFinished has been received. 5603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 5613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.inFinished); 5673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 5683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteDoubleSynReply() throws Exception { 5713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 5763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 5783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 5813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 5823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("c", "cola"), true, true); 5833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 5843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received. 5853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getSource().read(new Buffer(), 1); 5873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 5883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 5893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage()); 5903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 5973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 5983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 5993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 6003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, rstStream.streamId); 6013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(STREAM_IN_USE, rstStream.errorCode); 6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteDoubleSynStream() throws Exception { 6053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android")); 6073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "banana")); 6093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 6103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 6133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 6143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller IncomingStreamHandler handler = new IncomingStreamHandler() { 6153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void receive(SpdyStream stream) throws IOException { 6163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getRequestHeaders()); 6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(null, stream.getErrorCode()); 6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("c", "cola"), true); 6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 6223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller new SpdyConnection.Builder(true, peer.openSocket()).handler(handler).build(); 6233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 6263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 6273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 6283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 6293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, rstStream.streamId); 6313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, rstStream.errorCode); 6323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.intValue()); 6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteSendsDataAfterInFinished() throws Exception { 6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 640e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("c3po"), 4); // Ignored. 6413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. 6423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 6463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 6533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 6553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 6583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientDoesNotLimitFlowControl() throws Exception { 661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int dataLength = 64 * 1024 + 1; 6623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 6633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 6643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, new Buffer().write(new byte[dataLength]), dataLength); 6663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. 6673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 6683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 6713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 6723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true); 6733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders()); 6743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 6773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 6783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 6793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 6803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 6813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 6823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteSendsRefusedStreamBeforeReplyHeaders() throws Exception { 6853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 6863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 6873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().rstStream(1, REFUSED_STREAM); 6883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 6893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 6903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 6933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 6943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true); 6953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 6963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.getResponseHeaders(); 6973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 6983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 6993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage()); 7003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 7023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 7043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 7053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 7063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 7073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 7083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 7093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 7103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void receiveGoAway() throws Exception { 713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 7143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1 7173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 3 718e9689138ba98723f93ddf1bc74bbf66d332dfd76jwilson peer.acceptFrame(); // PING. 7193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().goAway(1, PROTOCOL_ERROR, Util.EMPTY_BYTE_ARRAY); 7203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 7213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA STREAM 1 7223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 7263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream1 = connection.newStream(headerEntries("a", "android"), true, true); 7273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true); 728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure the GO_AWAY that resets stream2 has been received. 7293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink1 = Okio.buffer(stream1.getSink()); 7303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink2 = Okio.buffer(stream2.getSink()); 7313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.writeUtf8("abc"); 7323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 7333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink2.writeUtf8("abc"); 7343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink2.flush(); 7353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 7363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 7373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage()); 7383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.writeUtf8("def"); 7403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.close(); 7413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 7423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("c", "cola"), true, true); 7433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 7443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 7453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 7463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(stream1.isOpen()); 748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(stream2.isOpen()); 7493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 7503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 7523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream1 = peer.takeFrame(); 7533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream1.type); 7543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream2 = peer.takeFrame(); 7553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream2.type); 7563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 7573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 7583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data1 = peer.takeFrame(); 7593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data1.type); 7603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, data1.streamId); 7613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("abcdef".getBytes("UTF-8"), data1.data)); 7623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void sendGoAway() throws Exception { 7653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1 7673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 7683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "b")); // Should be ignored! 7703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 7713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 7743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 7753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("a", "android"), true, true); 7763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 7773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.shutdown(PROTOCOL_ERROR); 7783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 7793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ping.roundTripTime(); // Prevent the peer from exiting prematurely. 7803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 7823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream1 = peer.takeFrame(); 7833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream1.type); 7843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame pingFrame = peer.takeFrame(); 7853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, pingFrame.type); 7863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 7873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 7883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, goaway.streamId); 7893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, goaway.errorCode); 7903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void noPingsAfterShutdown() throws Exception { 7933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 7953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 7983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 7993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.shutdown(INTERNAL_ERROR); 8003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping(); 8023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 8053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 8083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 8093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 8103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INTERNAL_ERROR, goaway.errorCode); 8113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void close() throws Exception { 8143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 8163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 8173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 8183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 8193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 8213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 8223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("a", "android"), true, true); 8233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 8243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.close(); 8253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 8263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("b", "banana"), true, true); 8283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 8313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink = Okio.buffer(stream.getSink()); 8333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.writeByte(0); 8353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.flush(); 8363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 8393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 841e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getSource().read(new Buffer(), 1); 8423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 8453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 8483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 8493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 8503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 8513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 8523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 8533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 8543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 8553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, rstStream.streamId); 8563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void closeCancelsPings() throws Exception { 8593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 8613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 8623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 8633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 8653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 8663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 8673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.close(); 8683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(-1, ping.roundTripTime()); 8693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void getResponseHeadersTimesOut() throws Exception { 872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 873e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 874e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS); 881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long startNanos = System.nanoTime(); 882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getResponseHeaders(); 884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 888a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void readTimesOut() throws Exception { 8983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 9003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS); 908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Source source = stream.getSource(); 909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long startNanos = System.nanoTime(); 910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.read(new Buffer(), 1); 912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 916a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void writeTimesOutAwaitingStreamWindow() throws Exception { 926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Set the peer's receive window to 5 bytes! 927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5); 928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().settings(peerSettings); 9313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 9323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA 936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 9373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 9383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 9393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 9403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Make sure settings have been received. 9423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Sink sink = stream.getSink(); 944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink.write(new Buffer().writeUtf8("abcde"), 5); 945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS); 9463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long startNanos = System.nanoTime(); 947a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("f"), 1); 9483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 949a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.flush(); // This will time out waiting on the write window. 9503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 9523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 9533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 954a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 9573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 9583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_PING, peer.takeFrame().type); 960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 9633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 9643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 965781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Test public void writeTimesOutAwaitingConnectionWindow() throws Exception { 966781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // Set the peer's receive window to 5 bytes. Give the stream 5 bytes back, so only the 967781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // connection-level window is applicable. 968781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5); 969781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 970781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // write the mocking script 971781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.sendFrame().settings(peerSettings); 972781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.acceptFrame(); // SYN_STREAM 973781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 974781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.sendFrame().windowUpdate(1, 5); 975781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.acceptFrame(); // PING 976781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.sendFrame().ping(true, 1, 0); 977781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.acceptFrame(); // DATA 978781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.acceptFrame(); // RST_STREAM 979781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller peer.play(); 980781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 981781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // play it back 982781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 983781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 984781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller connection.ping().roundTripTime(); // Make sure the window update has been received. 985781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller Sink sink = stream.getSink(); 986781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS); 987781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller sink.write(new Buffer().writeUtf8("abcdef"), 6); 988781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller long startNanos = System.nanoTime(); 989781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller try { 990781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller sink.flush(); // This will time out waiting on the write window. 991781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller fail(); 992781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } catch (InterruptedIOException expected) { 993781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 994781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 995781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller awaitWatchdogIdle(); 996781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 997781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(0, connection.openStreamCount()); 998781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 999781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // verify the peer received what was expected 1000781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 1001781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(TYPE_PING, peer.takeFrame().type); 1002781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 1003781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 1004781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 1005781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 1006a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void outgoingWritesAreBatched() throws Exception { 1007a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // write the mocking script 1008a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.acceptFrame(); // SYN_STREAM 1009a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1010a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.acceptFrame(); // DATA 1011a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.play(); 1012a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1013a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // play it back 1014a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SpdyConnection connection = connection(peer, SPDY3); 1015a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 1016a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1017a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // two outgoing writes 1018a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink sink = stream.getSink(); 1019a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("abcde"), 5); 1020a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("fghij"), 5); 1021a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.close(); 1022a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1023a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // verify the peer received one incoming frame 1024a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 1025a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 1026a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(TYPE_DATA, data.type); 1027a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertTrue(Arrays.equals("abcdefghij".getBytes("UTF-8"), data.data)); 1028a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertTrue(data.inFinished); 1029a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1030a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 10313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headers() throws Exception { 10323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 10333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 10343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 10353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 10363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().headers(1, headerEntries("c", "c3po")); 10373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 10383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 10393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 10413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 10423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 10433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. 10443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android", "c", "c3po"), stream.getResponseHeaders()); 10453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 10473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 10483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 10493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 10503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 10513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 10523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 10533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headersBeforeReply() throws Exception { 10553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 10563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 10573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 10583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().headers(1, headerEntries("c", "c3po")); 10593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 10603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 10613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 10623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 10643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 10653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 10663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. 10673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 10683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.getResponseHeaders(); 10693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 10703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 10713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage()); 10723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 10733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 10753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 10763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 10773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 10783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 10793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 10803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 10813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 10823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, rstStream.errorCode); 10833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 10843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void readSendsWindowUpdate() throws Exception { 1086e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 10873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1088e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowSize = 100; 1089e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowUpdateThreshold = 50; 10903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. 10923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 10933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 10943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < 3; i++) { 1095e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Send frames of summing to size 50, which is windowUpdateThreshold. 1096e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(24), 24); 1097e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(25), 25); 1098e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(1), 1); 10993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // connection WINDOW UPDATE 11003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // stream WINDOW UPDATE 11013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, data(0), 0); 11033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 1106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 1107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, windowSize); 11083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), false, true); 11093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, stream.unacknowledgedBytesRead); 11103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 11113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 1112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer buffer = new Buffer(); 1113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller buffer.writeAll(in); 11143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(-1, in.read(buffer, 1)); 1115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(150, buffer.size()); 11163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 11183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 11193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < 3; i++) { 1120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<Integer> windowUpdateStreamIds = new ArrayList<>(2); 11213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int j = 0; j < 2; j++) { 11223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame windowUpdate = peer.takeFrame(); 11233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_WINDOW_UPDATE, windowUpdate.type); 11243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller windowUpdateStreamIds.add(windowUpdate.streamId); 11253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(windowUpdateThreshold, windowUpdate.windowSizeIncrement); 11263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(windowUpdateStreamIds.contains(0)); // connection 11283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(windowUpdateStreamIds.contains(1)); // stream 11293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private Buffer data(int byteCount) { 1133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Buffer().write(new byte[byteCount]); 11343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverSendsEmptyDataClientDoesntSendWindowUpdate() throws Exception { 1137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 11383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. 11403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 11413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, data(0), 0); 11433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 1146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 11473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream client = connection.newStream(headerEntries("b", "banana"), false, true); 1148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(-1, client.getSource().read(new Buffer(), 1)); 11493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Verify the peer received what was expected. 11513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 11523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 11533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, peer.frameCount()); 11543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientSendsEmptyDataServerDoesntSendWindowUpdate() throws Exception { 1157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 11583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. 11603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 11613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 11623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 11633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 1166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 11673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream client = connection.newStream(headerEntries("b", "banana"), true, true); 11683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(client.getSink()); 11693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.write(Util.EMPTY_BYTE_ARRAY); 11703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 11713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 11723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Verify the peer received what was expected. 11743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 11753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 11763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, peer.frameCount()); 11773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void testTruncatedDataFrame() throws Exception { 11803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 11813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 11823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(1024), 1024); 1184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.truncateLastFrame(8 + 100); 11853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 11883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 11893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 11903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 11913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 11923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 11933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Okio.buffer(in).readByteString(101); 11943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 11953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 11963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage()); 11973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void blockedStreamDoesntStarveNewStream() throws Exception { 1201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int framesThatFillWindow = roundUp(DEFAULT_INITIAL_WINDOW_SIZE, peer.maxOutboundDataLength()); 12023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. This accepts more data frames than necessary! 12043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM on stream 1 12053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < framesThatFillWindow; i++) { 12063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA on stream 1 12073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM on stream 2 12093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA on stream 2 12103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 12113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 12133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 12143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream1 = connection.newStream(headerEntries("a", "apple"), true, true); 12153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out1 = Okio.buffer(stream1.getSink()); 1216c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller out1.write(new byte[DEFAULT_INITIAL_WINDOW_SIZE]); 12173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out1.flush(); 12183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Check that we've filled the window for both the stream and also the connection. 12203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.bytesLeftInWriteWindow); 12213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 12223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // receiving a window update on the the connection will unblock new streams. 12243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.readerRunnable.windowUpdate(0, 3); 12253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, connection.bytesLeftInWriteWindow); 12273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 12283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Another stream should be able to send data even though 1 is blocked. 12303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true); 12313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out2 = Okio.buffer(stream2.getSink()); 12323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out2.writeUtf8("foo"); 12333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out2.flush(); 12343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.bytesLeftInWriteWindow); 12363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 1237c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(DEFAULT_INITIAL_WINDOW_SIZE - 3, connection.getStream(3).bytesLeftInWriteWindow); 12383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** https://github.com/square/okhttp/issues/333 */ 12413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headerBlockHasTrailingCompressedBytes512() throws Exception { 12423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This specially-formatted frame has trailing deflated bytes after the name value block. 12433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String frame = "gAMAAgAAAgkAAAABeLvjxqfCYgAAAAD//2IAAAAA//9iAAAAAP//YgQAAAD//2IAAAAA//9iAAAAAP/" 12443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/YgAAAAD//2IEAAAA//9KBAAAAP//YgAAAAD//2IAAAAA//9iAAAAAP//sgEAAAD//2IAAAAA\n//9iBAAAAP//Y" 12453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "gIAAAD//2IGAAAA//9iAQAAAP//YgUAAAD//2IDAAAA//9iBwAAAP//4gAAAAD//+IEAAAA///iAgAAAP//4gYAA" 12463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AD//+IBAAAA///iBQAAAP//4gMAAAD//+IHAAAA//8SAAAAAP//EgQAAAD//xICAAAA//8SBgAAAP//EgEAAAD//" 12473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "xIFAAAA//8SAwAAAP//EgcAAAD//5IAAAAA//+SBAAAAP//kgIAAAD//5IGAAAA//+SAQAAAP//kgUAAAD//5IDA" 12483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAA//+SBwAAAP//UgAAAAD//1IEAAAA//9SAgAAAP//UgYAAAD//1IBAAAA//9SBQAAAP//UgMAAAD//1IHAAAA/" 12493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//SAAAAAP//0gQAAAD//9ICAAAA///SBgAAAP//0gEAAAD//9IFAAAA///SAwAAAP//0gcAAAD//zIAAAAA//8yB" 12503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAAP//MgIAAAD//zIGAAAA//8yAQAAAP//MgUAAAD//zIDAAAA//8yBwAAAP//sgAAAAD//7IEAAAA//+yAgAAA" 12513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "P//sgYAAAD//w=="; 12523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller headerBlockHasTrailingCompressedBytes(frame, 60); 12533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headerBlockHasTrailingCompressedBytes2048() throws Exception { 12563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This specially-formatted frame has trailing deflated bytes after the name value block. 12573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String frame = "gAMAAgAAB/sAAAABeLvjxqfCAqYjRhAGJmxGxUQAAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//8="; 12893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller headerBlockHasTrailingCompressedBytes(frame, 289); 12903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void headerBlockHasTrailingCompressedBytes(String frame, int length) throws IOException { 12933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 12943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller byte[] trailingCompressedBytes = ByteString.decodeBase64(frame).toByteArray(); 1296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller trailingCompressedBytes[11] = 1; // Set SPDY/3 stream ID to 3. 1297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame(trailingCompressedBytes); 1298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 12993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 13003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 13013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 13033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyConnection connection = connection(peer, SPDY3); 13043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller SpdyStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 13053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("a", stream.getResponseHeaders().get(0).name.utf8()); 13063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(length, stream.getResponseHeaders().get(0).value.size()); 13073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 13083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private SpdyConnection connection(MockSpdyPeer peer, Variant variant) throws IOException { 13113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return connectionBuilder(peer, variant).build(); 13123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private SpdyConnection.Builder connectionBuilder(MockSpdyPeer peer, Variant variant) 13153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throws IOException { 13163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return new SpdyConnection.Builder(true, peer.openSocket()) 13173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .protocol(variant.getProtocol()); 13183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void assertStreamData(String expected, Source source) throws IOException { 1321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller String actual = Okio.buffer(source).readUtf8(); 13223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(expected, actual); 13233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void assertFlushBlocks(BufferedSink out) throws IOException { 13263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller interruptAfterDelay(500); 13273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 13283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 13293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 13303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (InterruptedIOException expected) { 13313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Interrupts the current thread after {@code delayMillis}. */ 13353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void interruptAfterDelay(final long delayMillis) { 13363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final Thread toInterrupt = Thread.currentThread(); 13373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller new Thread("interrupting cow") { 13383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void run() { 13393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 13403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Thread.sleep(delayMillis); 13413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller toInterrupt.interrupt(); 13423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (InterruptedException e) { 13433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new AssertionError(); 13443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }.start(); 13473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1349a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 1350a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Returns true when all work currently in progress by the watchdog have completed. This method 1351a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * creates more work for the watchdog and waits for that work to be executed. When it is, we know 1352a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * work that preceded this call is complete. 1353a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 1354a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private void awaitWatchdogIdle() throws InterruptedException { 1355a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller final CountDownLatch latch = new CountDownLatch(1); 1356a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller AsyncTimeout watchdogJob = new AsyncTimeout() { 1357a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override protected void timedOut() { 1358a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller latch.countDown(); 1359a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1360a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller }; 1361a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller watchdogJob.deadlineNanoTime(System.nanoTime()); // Due immediately! 1362a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller watchdogJob.enter(); 1363a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller latch.await(); 1364a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1365a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 13663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller static int roundUp(int num, int divisor) { 13673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return (num + divisor - 1) / divisor; 13683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller} 1370