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