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