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 */ 1671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerpackage com.squareup.okhttp.internal.framed; 173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport com.squareup.okhttp.internal.Util; 193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.IOException; 203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.io.InterruptedIOException; 2171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport java.net.Socket; 223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.ArrayList; 233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.Arrays; 243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.List; 2571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport java.util.Random; 26a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.util.concurrent.CountDownLatch; 273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.TimeUnit; 283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport java.util.concurrent.atomic.AtomicInteger; 29a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.AsyncTimeout; 30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Buffer; 313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.BufferedSink; 323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.ByteString; 333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Okio; 34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Sink; 353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport okio.Source; 363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.After; 373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport org.junit.Test; 383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.TestUtil.headerEntries; 4071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.CANCEL; 4171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.INTERNAL_ERROR; 4271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.INVALID_STREAM; 4371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.PROTOCOL_ERROR; 4471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.REFUSED_STREAM; 4571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.ErrorCode.STREAM_IN_USE; 4671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Settings.DEFAULT_INITIAL_WINDOW_SIZE; 4771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Settings.PERSIST_VALUE; 4871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_DATA; 4971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_GOAWAY; 5071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_HEADERS; 5171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_PING; 5271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_RST_STREAM; 5371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fullerimport static com.squareup.okhttp.internal.framed.Spdy3.TYPE_WINDOW_UPDATE; 543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertEquals; 553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertFalse; 563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.assertTrue; 573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fullerimport static org.junit.Assert.fail; 583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class Spdy3ConnectionTest { 603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private static final Variant SPDY3 = new Spdy3(); 613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private final MockSpdyPeer peer = new MockSpdyPeer(); 623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @After public void tearDown() throws Exception { 643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.close(); 653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientCreatesStreamAndServerReplies() throws Exception { 683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame() 713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .synReply(false, 1, headerEntries("a", "android")); 72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 7771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 7871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("c3po"); 833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, synStream.streamId); 933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, synStream.associatedStreamId); 943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), synStream.headerBlock); 953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame requestData = peer.takeFrame(); 963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("c3po".getBytes("UTF-8"), requestData.data)); 973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headersOnlyStreamIsClosedAfterReplyHeaders() throws Exception { 1003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 1043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 10771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), false, false); 1083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 1093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders()); 110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure that inFinished has been received. 1113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 1123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientCreatesStreamAndServerRepliesWithFin() throws Exception { 1153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 1163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 1183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(true, 1, headerEntries("a", "android")); 1193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 1203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 12371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 1243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("b", "banana"), false, true); 1253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 1263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received. 1273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 1283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 1313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 1323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 1333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 1343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 1353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverCreatesStreamAndClientReplies() throws Exception { 1383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final List<Header> pushHeaders = headerEntries( 1393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":scheme", "https", 1403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":host", "localhost:8888", 1413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":method", "GET", 1423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":path", "/index.html", 1433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":status", "200", 1443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ":version", "HTTP/1.1", 1453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller "content-type", "text/html"); 1463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, pushHeaders); 1483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 1493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 1523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 1536c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection.Listener handler = new FramedConnection.Listener() { 1546c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void onStream(FramedStream stream) throws IOException { 1553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 1563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(pushHeaders, stream.getRequestHeaders()); 1573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(null, stream.getErrorCode()); 1583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("b", "banana"), true); 1593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 1616c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer new FramedConnection.Builder(true) 1626c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .socket(peer.openSocket()) 1636c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .listener(handler) 1646c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .build(); 1653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 1683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 1693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 1703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(reply.inFinished); 1713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, reply.streamId); 1723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), reply.headerBlock); 1733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.get()); 1743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void replyWithNoData() throws Exception { 1773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android")); 1793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 1803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 1813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 1833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 1846c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection.Listener listener = new FramedConnection.Listener() { 1856c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void onStream(FramedStream stream) throws IOException { 1863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("b", "banana"), false); 1873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 1883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 1903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1916c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer connectionBuilder(peer, SPDY3).listener(listener).build(); 1923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 1943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 1953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 1963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(reply.inFinished); 1973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), reply.headerBlock); 1983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.get()); 1993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 2003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverPingsClient() throws Exception { 2033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 2103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 2133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, ping.streamId); 2143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 2153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, ping.payload2); // ignored in spdy! 2163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.ack); 2173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientPingsServer() throws Exception { 2203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 5); // payload2 ignored in spdy! 2233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 22671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 2273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 2283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.roundTripTime() > 0); 2293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(ping.roundTripTime() < TimeUnit.SECONDS.toNanos(1)); 2303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame pingFrame = peer.takeFrame(); 2333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, pingFrame.type); 2343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, pingFrame.payload1); 2353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, pingFrame.payload2); 2363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(pingFrame.ack); 2373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void unexpectedPingIsNotReturned() throws Exception { 2403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 3, 0); // This ping will not be returned. 2443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 4, 0); 2453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 2503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 2523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping2 = peer.takeFrame(); 2533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping2.payload1); 2543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping4 = peer.takeFrame(); 2553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(4, ping4.payload1); 2563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverSendsSettingsToClient() throws Exception { 2593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2606c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer final Settings settings = new Settings(); 2613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 10); 2623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings); 2633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 2643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 2653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 2663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 2686c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer final AtomicInteger maxConcurrentStreams = new AtomicInteger(); 2696c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection.Listener listener = new FramedConnection.Listener() { 2706c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void onStream(FramedStream stream) throws IOException { 2716c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer throw new AssertionError(); 2726c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 2736c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void onSettings(FramedConnection connection) { 2746c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer maxConcurrentStreams.set(connection.maxConcurrentStreams()); 2756c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer } 2766c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer }; 2776c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection connection = connectionBuilder(peer, SPDY3) 2786c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .listener(listener) 2796c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .build(); 2803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.takeFrame(); // Guarantees that the peer Settings frame has been processed. 2823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller synchronized (connection) { 2833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(10, connection.peerSettings.getMaxConcurrentStreams(-1)); 2843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2856c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer assertEquals(10, maxConcurrentStreams.get()); 2863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 2873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 2883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void multipleSettingsFramesAreMerged() throws Exception { 2893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 2903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Settings settings1 = new Settings(); 2913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100); 2923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200); 2933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300); 2943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings1); 2953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Settings settings2 = new Settings(); 2963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.DOWNLOAD_BANDWIDTH, 0, 400); 2973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.DOWNLOAD_RETRANS_RATE, PERSIST_VALUE, 500); 2983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600); 2993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().settings(settings2); 3003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 3013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); 3023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 30571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 3063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.takeFrame(); // Guarantees that the Settings frame has been processed. 3083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller synchronized (connection) { 3093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(100, connection.peerSettings.getUploadBandwidth(-1)); 3103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.UPLOAD_BANDWIDTH)); 3113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(400, connection.peerSettings.getDownloadBandwidth(-1)); 3123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.peerSettings.flags(Settings.DOWNLOAD_BANDWIDTH)); 3133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(500, connection.peerSettings.getDownloadRetransRate(-1)); 3143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.DOWNLOAD_RETRANS_RATE)); 3153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1)); 3163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.MAX_CONCURRENT_STREAMS)); 3173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 320c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller @Test public void clearSettingsBeforeMerge() throws Exception { 321c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // write the mocking script 322c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller Settings settings1 = new Settings(); 323c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100); 324c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200); 325c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300); 326c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.sendFrame().settings(settings1); 327c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.sendFrame().ping(false, 2, 0); 328c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.acceptFrame(); 329c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.play(); 330c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 331c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // play it back 33271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 333c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 334c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller peer.takeFrame(); // Guarantees that the Settings frame has been processed. 335c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 336c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller // fake a settings frame with clear flag set. 337c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller Settings settings2 = new Settings(); 338c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600); 339c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller connection.readerRunnable.settings(true, settings2); 340c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 341c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller synchronized (connection) { 342c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getUploadBandwidth(-1)); 343c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getDownloadBandwidth(-1)); 344c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(-1, connection.peerSettings.getDownloadRetransRate(-1)); 345c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1)); 346c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller } 347c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller } 348c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller 3493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void bogusDataFrameDoesNotDisruptConnection() throws Exception { 3503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 41, new Buffer().writeUtf8("bogus"), 5); 3523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 3533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 3543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 3583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 3593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 3613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 3623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 3633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(41, rstStream.streamId); 3643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INVALID_STREAM, rstStream.errorCode); 3653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 3663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 3673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void bogusReplyFrameDoesNotDisruptConnection() throws Exception { 3703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 3713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 41, headerEntries("a", "android")); 3723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 3733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 3743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 3783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection(peer, SPDY3); 3793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 3813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 3823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 3833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(41, rstStream.streamId); 3843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INVALID_STREAM, rstStream.errorCode); 3853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 3863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 3873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 3883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientOutputStream() throws Exception { 3903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 3913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 3923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 3933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // TYPE_DATA 3943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // TYPE_DATA with FLAG_FIN 3953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 3963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 3973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 3983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 3993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 40071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 40171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, false); 4023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 4033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 4043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 4053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 4063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 4073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("round"); 4093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (Exception expected) { 4113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("closed", expected.getMessage()); 4123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received. 4143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 4153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 4173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 4183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 4193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 4203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 4213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.outFinished); 4223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 4233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data.type); 4243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(data.inFinished); 4253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data)); 4263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame fin = peer.takeFrame(); 4273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, fin.type); 4283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(fin.inFinished); 4293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 4303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 4313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, ping.payload1); 4323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverClosesClientOutputStream() throws Exception { 4353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 4363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 4373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().rstStream(1, CANCEL); 4383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 4393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 4403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 4413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 44371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 44471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true); 4453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 4463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the RST_CANCEL has been received. 4473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 4493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 4503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 4533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 4563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Close throws because buffered data wasn't flushed. 4593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 4613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 4633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 4643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 4653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 4663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 4673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 4683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 4693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 4703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, ping.payload1); 4713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 4743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Test that the client sends a RST_STREAM if doing so won't disrupt the 4753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * output stream. 4763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 4773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientInputStream() throws Exception { 4783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 4793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 4803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 4813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 4823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 4833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 48471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 48571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), false, true); 4863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 4873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 4883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller in.close(); 4893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller in.read(new Buffer(), 1); 4913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 4933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream closed", expected.getMessage()); 4943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 4953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 4963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("a"); 4973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 4983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 4993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 5003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream finished", expected.getMessage()); 5013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 5033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.inFinished); 5093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 5103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 5113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 5123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(CANCEL, rstStream.errorCode); 5133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** 5163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * Test that the client doesn't send a RST_STREAM if doing so will disrupt 5173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller * the output stream. 5183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller */ 5193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientClosesClientInputStreamIfOutputStreamIsClosed() throws Exception { 5203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 5233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA with FLAG_FIN 5243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 5253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 52871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 52971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true); 5303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source source = stream.getSource(); 5313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(stream.getSink()); 5323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller source.close(); 5333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.read(new Buffer(), 1); 5353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 5363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 5373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream closed", expected.getMessage()); 5383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.writeUtf8("square"); 5403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 5413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 5423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 5433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.inFinished); 5493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 5503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 5513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data.type); 5523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data)); 5533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame fin = peer.takeFrame(); 5543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, fin.type); 5553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(fin.inFinished); 5563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(fin.outFinished); 5573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 5583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 5593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(CANCEL, rstStream.errorCode); 5603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverClosesClientInputStream() throws Exception { 5633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("square"), 6); 567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // PING 568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().ping(true, 1, 0); 5693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 57271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 57371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), false, true); 5743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source source = stream.getSource(); 5753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("square", source); 576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure that inFinished has been received. 5773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 5783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 5803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 5813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 5823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 5833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(synStream.inFinished); 5843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertFalse(synStream.outFinished); 5853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 5863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteDoubleSynReply() throws Exception { 5883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 5893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 5903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 5913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 5923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 5933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 5943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 5953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 5963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 5973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 59871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 59971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("c", "cola"), true, true); 6003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 6013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received. 6023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getSource().read(new Buffer(), 1); 6043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 6053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 6063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage()); 6073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 6113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 6123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 6133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 6143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 6153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 6163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 6173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, rstStream.streamId); 6183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(STREAM_IN_USE, rstStream.errorCode); 6193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteDoubleSynStream() throws Exception { 6223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android")); 6243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_REPLY 625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "banana")); 6263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 6273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 6303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final AtomicInteger receiveCount = new AtomicInteger(); 6316c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection.Listener listener = new FramedConnection.Listener() { 6326c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer @Override public void onStream(FramedStream stream) throws IOException { 6333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller receiveCount.incrementAndGet(); 6343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getRequestHeaders()); 6353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(null, stream.getErrorCode()); 6363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.reply(headerEntries("c", "cola"), true); 6373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }; 6396c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer new FramedConnection.Builder(true) 6406c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .socket(peer.openSocket()) 6416c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .listener(listener) 6426c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .build(); 6433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame reply = peer.takeFrame(); 6463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, reply.type); 6473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode); 6483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 6493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 6503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, rstStream.streamId); 6513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, rstStream.errorCode); 6523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, receiveCount.intValue()); 6533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteSendsDataAfterInFinished() throws Exception { 6563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 6573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 6583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("c3po"), 4); // Ignored. 6613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. 6623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 6633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 66671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 66771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 6683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 6693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 6703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 6733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 6743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 6753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 6763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 6773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 6783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 6793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientDoesNotLimitFlowControl() throws Exception { 681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int dataLength = 64 * 1024 + 1; 6823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 6833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 6843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("b", "banana")); 685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, new Buffer().write(new byte[dataLength]), dataLength); 6863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded. 6873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 6883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 6893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 69171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 69271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true); 6933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders()); 6943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 6953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 6963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 6973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 6983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 6993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 7003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 7013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 7023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void remoteSendsRefusedStreamBeforeReplyHeaders() throws Exception { 7053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 7073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().rstStream(1, REFUSED_STREAM); 7083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(false, 2, 0); 7093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 7103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 71371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 71471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true); 7153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 7163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.getResponseHeaders(); 7173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 7183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 7193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage()); 7203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 7223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 7243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 7253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 7263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 7273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 7283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 7293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(2, ping.payload1); 7303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void receiveGoAway() throws Exception { 733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 7343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1 7373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 3 738d6ed2ae912c016acf709fced5b66d542875deb7cjwilson peer.acceptFrame(); // PING. 7393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().goAway(1, PROTOCOL_ERROR, Util.EMPTY_BYTE_ARRAY); 7403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 7413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA STREAM 1 7423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 74571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 74671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream1 = connection.newStream(headerEntries("a", "android"), true, true); 74771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true); 748e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Ensure the GO_AWAY that resets stream2 has been received. 7493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink1 = Okio.buffer(stream1.getSink()); 7503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink2 = Okio.buffer(stream2.getSink()); 7513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.writeUtf8("abc"); 7523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 7533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink2.writeUtf8("abc"); 7543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink2.flush(); 7553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 7563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 7573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage()); 7583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.writeUtf8("def"); 7603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink1.close(); 7613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 7623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("c", "cola"), true, true); 7633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 7643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 7653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 7663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertTrue(stream1.isOpen()); 768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertFalse(stream2.isOpen()); 7693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 7703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 7723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream1 = peer.takeFrame(); 7733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream1.type); 7743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream2 = peer.takeFrame(); 7753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream2.type); 7763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 7773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 7783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame data1 = peer.takeFrame(); 7793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, data1.type); 7803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, data1.streamId); 7813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(Arrays.equals("abcdef".getBytes("UTF-8"), data1.data)); 7823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 7833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void sendGoAway() throws Exception { 7853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 7863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1 7873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 7883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "b")); // Should be ignored! 7903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 7913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 7923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 7933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 79471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 7953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("a", "android"), true, true); 7963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 7973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.shutdown(PROTOCOL_ERROR); 7983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 7993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller ping.roundTripTime(); // Prevent the peer from exiting prematurely. 8003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 8023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream1 = peer.takeFrame(); 8033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream1.type); 8043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame pingFrame = peer.takeFrame(); 8053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, pingFrame.type); 8063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 8073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 8083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, goaway.streamId); 8093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, goaway.errorCode); 8103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void noPingsAfterShutdown() throws Exception { 8133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 8153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 8163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 81871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 8193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.shutdown(INTERNAL_ERROR); 8203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping(); 8223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 8253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 8283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 8293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 8303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(INTERNAL_ERROR, goaway.errorCode); 8313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8333c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void close() throws Exception { 8343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 8363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 8373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 8383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 8393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 84171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 84271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true); 8433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, connection.openStreamCount()); 8443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.close(); 8453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.openStreamCount()); 8463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.newStream(headerEntries("b", "banana"), true, true); 8483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("shutdown", expected.getMessage()); 8513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink sink = Okio.buffer(stream.getSink()); 8533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 8543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.writeByte(0); 8553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller sink.flush(); 8563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 8593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 861e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getSource().read(new Buffer(), 1); 8623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 8633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 8643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: CANCEL", expected.getMessage()); 8653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 8683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 8693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 8703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 8713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame goaway = peer.takeFrame(); 8723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_GOAWAY, goaway.type); 8733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 8743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 8753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(1, rstStream.streamId); 8763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void closeCancelsPings() throws Exception { 8793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 8803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 8813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // GOAWAY 8823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 8833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 8843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 88571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 8863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Ping ping = connection.ping(); 8873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.close(); 8883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(-1, ping.roundTripTime()); 8893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 8903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void getResponseHeadersTimesOut() throws Exception { 892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 89871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 89971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS); 901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long startNanos = System.nanoTime(); 902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.getResponseHeaders(); 904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 908a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void readTimesOut() throws Exception { 9183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 9193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 9203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.play(); 923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // play it back 92571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 92671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS); 928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Source source = stream.getSource(); 929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long startNanos = System.nanoTime(); 930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller try { 931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller source.read(new Buffer(), 1); 932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller fail(); 933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 936a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // verify the peer received what was expected 941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } 944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller @Test public void writeTimesOutAwaitingStreamWindow() throws Exception { 946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Set the peer's receive window to 5 bytes! 947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5); 948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller 949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // write the mocking script 950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().settings(peerSettings); 9513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 9523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // SYN_STREAM 954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // DATA 956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.acceptFrame(); // RST_STREAM 9573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 9583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 9593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 96071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.ping().roundTripTime(); // Make sure settings have been received. 96271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Sink sink = stream.getSink(); 964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller sink.write(new Buffer().writeUtf8("abcde"), 5); 965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS); 9663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long startNanos = System.nanoTime(); 967a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("f"), 1); 9683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 969a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.flush(); // This will time out waiting on the write window. 9703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller } catch (InterruptedIOException expected) { 9723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 9733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller long elapsedNanos = System.nanoTime() - startNanos; 974a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller awaitWatchdogIdle(); 975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(0, connection.openStreamCount()); 9773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 9783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_PING, peer.takeFrame().type); 980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 9833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 9843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 9857aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller @Test public void writeTimesOutAwaitingConnectionWindow() throws Exception { 9867aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller // Set the peer's receive window to 5 bytes. Give the stream 5 bytes back, so only the 9877aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller // connection-level window is applicable. 9887aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5); 9897aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller 9907aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller // write the mocking script 9917aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.sendFrame().settings(peerSettings); 9927aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.acceptFrame(); // SYN_STREAM 9937aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 9947aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.sendFrame().windowUpdate(1, 5); 9957aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.acceptFrame(); // PING 9967aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.sendFrame().ping(true, 1, 0); 9977aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.acceptFrame(); // DATA 9987aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.acceptFrame(); // RST_STREAM 9997aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller peer.play(); 10007aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller 10017aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller // play it back 100271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 100371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 10047aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller connection.ping().roundTripTime(); // Make sure the window update has been received. 10057aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller Sink sink = stream.getSink(); 10067aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS); 10077aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller sink.write(new Buffer().writeUtf8("abcdef"), 6); 10087aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller long startNanos = System.nanoTime(); 10097aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller try { 10107aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller sink.flush(); // This will time out waiting on the write window. 10117aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller fail(); 10127aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller } catch (InterruptedIOException expected) { 10137aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller } 10147aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller long elapsedNanos = System.nanoTime() - startNanos; 10157aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller awaitWatchdogIdle(); 10167aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */); 10177aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(0, connection.openStreamCount()); 10187aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller 10197aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller // verify the peer received what was expected 10207aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 10217aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(TYPE_PING, peer.takeFrame().type); 10227aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 10237aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller assertEquals(TYPE_RST_STREAM, peer.takeFrame().type); 10247aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller } 10257aeaaefc891f6221f4b2cce536b1c1e816e09794Neil Fuller 1026a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Test public void outgoingWritesAreBatched() throws Exception { 1027a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // write the mocking script 1028a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.acceptFrame(); // SYN_STREAM 1029a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1030a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.acceptFrame(); // DATA 1031a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller peer.play(); 1032a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1033a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // play it back 103471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 103571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 1036a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1037a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // two outgoing writes 1038a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink sink = stream.getSink(); 1039a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("abcde"), 5); 1040a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.write(new Buffer().writeUtf8("fghij"), 5); 1041a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller sink.close(); 1042a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 1043a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // verify the peer received one incoming frame 1044a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 1045a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller MockSpdyPeer.InFrame data = peer.takeFrame(); 1046a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertEquals(TYPE_DATA, data.type); 1047a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertTrue(Arrays.equals("abcdefghij".getBytes("UTF-8"), data.data)); 1048a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller assertTrue(data.inFinished); 1049a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1050a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 10513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headers() throws Exception { 10523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 10533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 10543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 10553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 10563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().headers(1, headerEntries("c", "c3po")); 10573c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 10583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 10593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 106171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 106271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 10633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. 10643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android", "c", "c3po"), stream.getResponseHeaders()); 10653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 10673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 10683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 10693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 10703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 10713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 10723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 10733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headersBeforeReply() throws Exception { 10753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 10763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 10773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // PING 10783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().headers(1, headerEntries("c", "c3po")); 10793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // RST_STREAM 10803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().ping(true, 1, 0); 10813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 10823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 108471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 108571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 10863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.ping().roundTripTime(); // Ensure that the HEADERS has been received. 10873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 10883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller stream.getResponseHeaders(); 10893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 10903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 10913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage()); 10923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 10933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 10943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // verify the peer received what was expected 10953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 10963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 10973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode); 10983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame ping = peer.takeFrame(); 10993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_PING, ping.type); 11003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame rstStream = peer.takeFrame(); 11013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_RST_STREAM, rstStream.type); 11023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(PROTOCOL_ERROR, rstStream.errorCode); 11033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void readSendsWindowUpdate() throws Exception { 1106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 11073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowSize = 100; 1109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int windowUpdateThreshold = 50; 11103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. 11123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 11133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 11143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < 3; i++) { 1115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller // Send frames of summing to size 50, which is windowUpdateThreshold. 1116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(24), 24); 1117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(25), 25); 1118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(1), 1); 11193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // connection WINDOW UPDATE 11203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // stream WINDOW UPDATE 11213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 1122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, data(0), 0); 11233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 112671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 1127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller connection.okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, windowSize); 112871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), false, true); 11293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, stream.unacknowledgedBytesRead); 11303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 11313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 1132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller Buffer buffer = new Buffer(); 1133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller buffer.writeAll(in); 11343c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(-1, in.read(buffer, 1)); 1135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(150, buffer.size()); 11363c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 11383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 11393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < 3; i++) { 1140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller List<Integer> windowUpdateStreamIds = new ArrayList<>(2); 11413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int j = 0; j < 2; j++) { 11423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame windowUpdate = peer.takeFrame(); 11433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_WINDOW_UPDATE, windowUpdate.type); 11443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller windowUpdateStreamIds.add(windowUpdate.streamId); 11453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(windowUpdateThreshold, windowUpdate.windowSizeIncrement); 11463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(windowUpdateStreamIds.contains(0)); // connection 11483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertTrue(windowUpdateStreamIds.contains(1)); // stream 11493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11503c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller private Buffer data(int byteCount) { 1153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller return new Buffer().write(new byte[byteCount]); 11543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void serverSendsEmptyDataClientDoesntSendWindowUpdate() 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.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, data(0), 0); 11633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 116671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 116771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream client = connection.newStream(headerEntries("b", "banana"), false, true); 1168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller assertEquals(-1, client.getSource().read(new Buffer(), 1)); 11693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Verify the peer received what was expected. 11713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller MockSpdyPeer.InFrame synStream = peer.takeFrame(); 11723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, synStream.type); 11733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, peer.frameCount()); 11743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void clientSendsEmptyDataServerDoesntSendWindowUpdate() throws Exception { 1177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.setVariantAndClient(SPDY3, false); 11783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. 11803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 11813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 11823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 11833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 11843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 118671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 118771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream client = connection.newStream(headerEntries("b", "banana"), true, true); 11883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out = Okio.buffer(client.getSink()); 11893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.write(Util.EMPTY_BYTE_ARRAY); 11903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.flush(); 11913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out.close(); 11923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Verify the peer received what was expected. 11943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_HEADERS, peer.takeFrame().type); 11953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(TYPE_DATA, peer.takeFrame().type); 11963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, peer.frameCount()); 11973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 11983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 11993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void testTruncatedDataFrame() throws Exception { 12003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 12013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 12023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.sendFrame().synReply(false, 1, headerEntries("a", "android")); 1203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(false, 1, data(1024), 1024); 1204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.truncateLastFrame(8 + 100); 12053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 12063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 120871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 120971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 12103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(headerEntries("a", "android"), stream.getResponseHeaders()); 12113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Source in = stream.getSource(); 12123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 12133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Okio.buffer(in).readByteString(101); 12143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller fail(); 12153c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (IOException expected) { 12163c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage()); 12173c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12183c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void blockedStreamDoesntStarveNewStream() throws Exception { 1221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller int framesThatFillWindow = roundUp(DEFAULT_INITIAL_WINDOW_SIZE, peer.maxOutboundDataLength()); 12223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12233c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Write the mocking script. This accepts more data frames than necessary! 12243c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM on stream 1 12253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller for (int i = 0; i < framesThatFillWindow; i++) { 12263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA on stream 1 12273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM on stream 2 12293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA on stream 2 12303c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 12313c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12323c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Play it back. 123371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 123471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream1 = connection.newStream(headerEntries("a", "apple"), true, true); 12353c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out1 = Okio.buffer(stream1.getSink()); 1236c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller out1.write(new byte[DEFAULT_INITIAL_WINDOW_SIZE]); 12373c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out1.flush(); 12383c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12393c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Check that we've filled the window for both the stream and also the connection. 12403c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.bytesLeftInWriteWindow); 12413c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 12423c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12433c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // receiving a window update on the the connection will unblock new streams. 12443c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller connection.readerRunnable.windowUpdate(0, 3); 12453c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12463c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(3, connection.bytesLeftInWriteWindow); 12473c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 12483c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12493c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // Another stream should be able to send data even though 1 is blocked. 125071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true); 12513c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller BufferedSink out2 = Okio.buffer(stream2.getSink()); 12523c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out2.writeUtf8("foo"); 12533c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller out2.flush(); 12543c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12553c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.bytesLeftInWriteWindow); 12563c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow); 1257c6bd683320121544811f481709b3fdbcbe9b3866Neil Fuller assertEquals(DEFAULT_INITIAL_WINDOW_SIZE - 3, connection.getStream(3).bytesLeftInWriteWindow); 12583c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12593c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** https://github.com/square/okhttp/issues/333 */ 12613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headerBlockHasTrailingCompressedBytes512() throws Exception { 12623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This specially-formatted frame has trailing deflated bytes after the name value block. 12633c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String frame = "gAMAAgAAAgkAAAABeLvjxqfCYgAAAAD//2IAAAAA//9iAAAAAP//YgQAAAD//2IAAAAA//9iAAAAAP/" 12643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/YgAAAAD//2IEAAAA//9KBAAAAP//YgAAAAD//2IAAAAA//9iAAAAAP//sgEAAAD//2IAAAAA\n//9iBAAAAP//Y" 12653c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "gIAAAD//2IGAAAA//9iAQAAAP//YgUAAAD//2IDAAAA//9iBwAAAP//4gAAAAD//+IEAAAA///iAgAAAP//4gYAA" 12663c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AD//+IBAAAA///iBQAAAP//4gMAAAD//+IHAAAA//8SAAAAAP//EgQAAAD//xICAAAA//8SBgAAAP//EgEAAAD//" 12673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "xIFAAAA//8SAwAAAP//EgcAAAD//5IAAAAA//+SBAAAAP//kgIAAAD//5IGAAAA//+SAQAAAP//kgUAAAD//5IDA" 12683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAA//+SBwAAAP//UgAAAAD//1IEAAAA//9SAgAAAP//UgYAAAD//1IBAAAA//9SBQAAAP//UgMAAAD//1IHAAAA/" 12693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//SAAAAAP//0gQAAAD//9ICAAAA///SBgAAAP//0gEAAAD//9IFAAAA///SAwAAAP//0gcAAAD//zIAAAAA//8yB" 12703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAAP//MgIAAAD//zIGAAAA//8yAQAAAP//MgUAAAD//zIDAAAA//8yBwAAAP//sgAAAAD//7IEAAAA//+yAgAAA" 12713c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "P//sgYAAAD//w=="; 12723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller headerBlockHasTrailingCompressedBytes(frame, 60); 12733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 12743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 12753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Test public void headerBlockHasTrailingCompressedBytes2048() throws Exception { 12763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // This specially-formatted frame has trailing deflated bytes after the name value block. 12773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller String frame = "gAMAAgAAB/sAAAABeLvjxqfCAqYjRhAGJmxGxUQAAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12903c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12913c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12923c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 12933c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 12943c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 12953c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 12963c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 12973c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 12983c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 12993c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 13003c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 13013c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD" 13023c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o" 13033c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA" 13043c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9" 13053c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA" 13063c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/" 13073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ" 13083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller + "AAAD//0oEAAAA//8="; 13093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller headerBlockHasTrailingCompressedBytes(frame, 289); 13103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13113c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13123c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void headerBlockHasTrailingCompressedBytes(String frame, int length) throws IOException { 13133c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // write the mocking script 13143c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // SYN_STREAM 1315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller byte[] trailingCompressedBytes = ByteString.decodeBase64(frame).toByteArray(); 1316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller trailingCompressedBytes[11] = 1; // Set SPDY/3 stream ID to 3. 1317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame(trailingCompressedBytes); 1318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5); 13193c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.acceptFrame(); // DATA 13203c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller peer.play(); 13213c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13223c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller // play it back 132371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedConnection connection = connection(peer, SPDY3); 132471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true); 13253c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals("a", stream.getResponseHeaders().get(0).name.utf8()); 13263c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(length, stream.getResponseHeaders().get(0).value.size()); 13273c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertStreamData("robot", stream.getSource()); 13283c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13293c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 133071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller @Test public void socketExceptionWhileWritingHeaders() throws Exception { 133171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller peer.acceptFrame(); // SYN_STREAM. 133271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller peer.play(); 133371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller 133471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller String longString = ByteString.of(randomBytes(2048)).base64(); 133571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller Socket socket = peer.openSocket(); 13366c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer FramedConnection connection = new FramedConnection.Builder(true) 13376c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .socket(socket) 133871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller .protocol(SPDY3.getProtocol()) 133971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller .build(); 134071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller socket.shutdownOutput(); 134171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller try { 134271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller connection.newStream(headerEntries("a", longString), false, true); 134371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller fail(); 134471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } catch (IOException expected) { 134571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 134671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller try { 134771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller connection.newStream(headerEntries("b", longString), false, true); 134871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller fail(); 134971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } catch (IOException expected) { 135071b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 135171b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 135271b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller 135371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller private byte[] randomBytes(int length) { 135471b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller byte[] bytes = new byte[length]; 135571b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller new Random(0).nextBytes(bytes); 135671b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller return bytes; 135771b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller } 135871b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller 135971b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller private FramedConnection connection(MockSpdyPeer peer, Variant variant) throws IOException { 13603c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return connectionBuilder(peer, variant).build(); 13613c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13623c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 136371b9f47b26fb57ac3e436a19519c6e3ec70e86ebNeil Fuller private FramedConnection.Builder connectionBuilder(MockSpdyPeer peer, Variant variant) 13643c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throws IOException { 13656c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer return new FramedConnection.Builder(true) 13666c251e20f00c7574b217bd4351ac81666f574380Tobias Thierer .socket(peer.openSocket()) 13673c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller .protocol(variant.getProtocol()); 13683c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13693c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13703c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void assertStreamData(String expected, Source source) throws IOException { 1371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller String actual = Okio.buffer(source).readUtf8(); 13723c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller assertEquals(expected, actual); 13733c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13743c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 13753c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller /** Interrupts the current thread after {@code delayMillis}. */ 13763c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller private void interruptAfterDelay(final long delayMillis) { 13773c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller final Thread toInterrupt = Thread.currentThread(); 13783c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller new Thread("interrupting cow") { 13793c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller @Override public void run() { 13803c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller try { 13813c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller Thread.sleep(delayMillis); 13823c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller toInterrupt.interrupt(); 13833c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } catch (InterruptedException e) { 13843c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller throw new AssertionError(); 13853c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13863c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13873c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller }.start(); 13883c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 13893c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller 1390a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 1391a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Returns true when all work currently in progress by the watchdog have completed. This method 1392a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * creates more work for the watchdog and waits for that work to be executed. When it is, we know 1393a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * work that preceded this call is complete. 1394a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 1395a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller private void awaitWatchdogIdle() throws InterruptedException { 1396a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller final CountDownLatch latch = new CountDownLatch(1); 1397a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller AsyncTimeout watchdogJob = new AsyncTimeout() { 1398a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override protected void timedOut() { 1399a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller latch.countDown(); 1400a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1401a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller }; 1402a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller watchdogJob.deadlineNanoTime(System.nanoTime()); // Due immediately! 1403a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller watchdogJob.enter(); 1404a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller latch.await(); 1405a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 1406a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 14073c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller static int roundUp(int num, int divisor) { 14083c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller return (num + divisor - 1) / divisor; 14093c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller } 14103c938a3f6b61ce5e2dba0d039b03fe73b89fd26cNeil Fuller} 1411