Home | History | Annotate | Download | only in framed
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.squareup.okhttp.internal.framed;
     17 
     18 import com.squareup.okhttp.internal.Util;
     19 import java.io.IOException;
     20 import java.io.InterruptedIOException;
     21 import java.net.Socket;
     22 import java.util.ArrayList;
     23 import java.util.Arrays;
     24 import java.util.List;
     25 import java.util.Random;
     26 import java.util.concurrent.CountDownLatch;
     27 import java.util.concurrent.TimeUnit;
     28 import java.util.concurrent.atomic.AtomicInteger;
     29 import okio.AsyncTimeout;
     30 import okio.Buffer;
     31 import okio.BufferedSink;
     32 import okio.ByteString;
     33 import okio.Okio;
     34 import okio.Sink;
     35 import okio.Source;
     36 import org.junit.After;
     37 import org.junit.Test;
     38 
     39 import static com.squareup.okhttp.TestUtil.headerEntries;
     40 import static com.squareup.okhttp.internal.framed.ErrorCode.CANCEL;
     41 import static com.squareup.okhttp.internal.framed.ErrorCode.INTERNAL_ERROR;
     42 import static com.squareup.okhttp.internal.framed.ErrorCode.INVALID_STREAM;
     43 import static com.squareup.okhttp.internal.framed.ErrorCode.PROTOCOL_ERROR;
     44 import static com.squareup.okhttp.internal.framed.ErrorCode.REFUSED_STREAM;
     45 import static com.squareup.okhttp.internal.framed.ErrorCode.STREAM_IN_USE;
     46 import static com.squareup.okhttp.internal.framed.Settings.DEFAULT_INITIAL_WINDOW_SIZE;
     47 import static com.squareup.okhttp.internal.framed.Settings.PERSIST_VALUE;
     48 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_DATA;
     49 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_GOAWAY;
     50 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_HEADERS;
     51 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_PING;
     52 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_RST_STREAM;
     53 import static com.squareup.okhttp.internal.framed.Spdy3.TYPE_WINDOW_UPDATE;
     54 import static org.junit.Assert.assertEquals;
     55 import static org.junit.Assert.assertFalse;
     56 import static org.junit.Assert.assertTrue;
     57 import static org.junit.Assert.fail;
     58 
     59 public final class Spdy3ConnectionTest {
     60   private static final Variant SPDY3 = new Spdy3();
     61   private final MockSpdyPeer peer = new MockSpdyPeer();
     62 
     63   @After public void tearDown() throws Exception {
     64     peer.close();
     65   }
     66 
     67   @Test public void clientCreatesStreamAndServerReplies() throws Exception {
     68     // write the mocking script
     69     peer.acceptFrame(); // SYN_STREAM
     70     peer.sendFrame()
     71         .synReply(false, 1, headerEntries("a", "android"));
     72     peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5);
     73     peer.acceptFrame(); // DATA
     74     peer.play();
     75 
     76     // play it back
     77     FramedConnection connection = connection(peer, SPDY3);
     78     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
     79     assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
     80     assertStreamData("robot", stream.getSource());
     81     BufferedSink out = Okio.buffer(stream.getSink());
     82     out.writeUtf8("c3po");
     83     out.close();
     84     assertEquals(0, connection.openStreamCount());
     85 
     86     // verify the peer received what was expected
     87     MockSpdyPeer.InFrame synStream = peer.takeFrame();
     88     assertEquals(TYPE_HEADERS, synStream.type);
     89     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
     90     assertFalse(synStream.inFinished);
     91     assertFalse(synStream.outFinished);
     92     assertEquals(1, synStream.streamId);
     93     assertEquals(0, synStream.associatedStreamId);
     94     assertEquals(headerEntries("b", "banana"), synStream.headerBlock);
     95     MockSpdyPeer.InFrame requestData = peer.takeFrame();
     96     assertTrue(Arrays.equals("c3po".getBytes("UTF-8"), requestData.data));
     97   }
     98 
     99   @Test public void headersOnlyStreamIsClosedAfterReplyHeaders() throws Exception {
    100     peer.acceptFrame(); // SYN_STREAM
    101     peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
    102     peer.acceptFrame(); // PING
    103     peer.sendFrame().ping(true, 1, 0);
    104     peer.play();
    105 
    106     FramedConnection connection = connection(peer, SPDY3);
    107     FramedStream stream = connection.newStream(headerEntries("a", "android"), false, false);
    108     assertEquals(1, connection.openStreamCount());
    109     assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders());
    110     connection.ping().roundTripTime(); // Ensure that inFinished has been received.
    111     assertEquals(0, connection.openStreamCount());
    112   }
    113 
    114   @Test public void clientCreatesStreamAndServerRepliesWithFin() throws Exception {
    115     // write the mocking script
    116     peer.acceptFrame(); // SYN_STREAM
    117     peer.acceptFrame(); // PING
    118     peer.sendFrame().synReply(true, 1, headerEntries("a", "android"));
    119     peer.sendFrame().ping(true, 1, 0);
    120     peer.play();
    121 
    122     // play it back
    123     FramedConnection connection = connection(peer, SPDY3);
    124     connection.newStream(headerEntries("b", "banana"), false, true);
    125     assertEquals(1, connection.openStreamCount());
    126     connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received.
    127     assertEquals(0, connection.openStreamCount());
    128 
    129     // verify the peer received what was expected
    130     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    131     assertEquals(TYPE_HEADERS, synStream.type);
    132     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    133     MockSpdyPeer.InFrame ping = peer.takeFrame();
    134     assertEquals(TYPE_PING, ping.type);
    135   }
    136 
    137   @Test public void serverCreatesStreamAndClientReplies() throws Exception {
    138     final List<Header> pushHeaders = headerEntries(
    139         ":scheme", "https",
    140         ":host", "localhost:8888",
    141         ":method", "GET",
    142         ":path", "/index.html",
    143         ":status", "200",
    144         ":version", "HTTP/1.1",
    145         "content-type", "text/html");
    146     // write the mocking script
    147     peer.sendFrame().synStream(false, false, 2, 0, pushHeaders);
    148     peer.acceptFrame(); // SYN_REPLY
    149     peer.play();
    150 
    151     // play it back
    152     final AtomicInteger receiveCount = new AtomicInteger();
    153     FramedConnection.Listener handler = new FramedConnection.Listener() {
    154       @Override public void onStream(FramedStream stream) throws IOException {
    155         receiveCount.incrementAndGet();
    156         assertEquals(pushHeaders, stream.getRequestHeaders());
    157         assertEquals(null, stream.getErrorCode());
    158         stream.reply(headerEntries("b", "banana"), true);
    159       }
    160     };
    161     new FramedConnection.Builder(true)
    162         .socket(peer.openSocket())
    163         .listener(handler)
    164         .build();
    165 
    166     // verify the peer received what was expected
    167     MockSpdyPeer.InFrame reply = peer.takeFrame();
    168     assertEquals(TYPE_HEADERS, reply.type);
    169     assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode);
    170     assertFalse(reply.inFinished);
    171     assertEquals(2, reply.streamId);
    172     assertEquals(headerEntries("b", "banana"), reply.headerBlock);
    173     assertEquals(1, receiveCount.get());
    174   }
    175 
    176   @Test public void replyWithNoData() throws Exception {
    177     // write the mocking script
    178     peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android"));
    179     peer.acceptFrame(); // SYN_REPLY
    180     peer.play();
    181 
    182     // play it back
    183     final AtomicInteger receiveCount = new AtomicInteger();
    184     FramedConnection.Listener listener = new FramedConnection.Listener() {
    185       @Override public void onStream(FramedStream stream) throws IOException {
    186         stream.reply(headerEntries("b", "banana"), false);
    187         receiveCount.incrementAndGet();
    188       }
    189     };
    190 
    191     connectionBuilder(peer, SPDY3).listener(listener).build();
    192 
    193     // verify the peer received what was expected
    194     MockSpdyPeer.InFrame reply = peer.takeFrame();
    195     assertEquals(TYPE_HEADERS, reply.type);
    196     assertTrue(reply.inFinished);
    197     assertEquals(headerEntries("b", "banana"), reply.headerBlock);
    198     assertEquals(1, receiveCount.get());
    199     assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode);
    200   }
    201 
    202   @Test public void serverPingsClient() throws Exception {
    203     // write the mocking script
    204     peer.sendFrame().ping(false, 2, 0);
    205     peer.acceptFrame(); // PING
    206     peer.play();
    207 
    208     // play it back
    209     connection(peer, SPDY3);
    210 
    211     // verify the peer received what was expected
    212     MockSpdyPeer.InFrame ping = peer.takeFrame();
    213     assertEquals(0, ping.streamId);
    214     assertEquals(2, ping.payload1);
    215     assertEquals(0, ping.payload2); // ignored in spdy!
    216     assertTrue(ping.ack);
    217   }
    218 
    219   @Test public void clientPingsServer() throws Exception {
    220     // write the mocking script
    221     peer.acceptFrame(); // PING
    222     peer.sendFrame().ping(true, 1, 5); // payload2 ignored in spdy!
    223     peer.play();
    224 
    225     // play it back
    226     FramedConnection connection = connection(peer, SPDY3);
    227     Ping ping = connection.ping();
    228     assertTrue(ping.roundTripTime() > 0);
    229     assertTrue(ping.roundTripTime() < TimeUnit.SECONDS.toNanos(1));
    230 
    231     // verify the peer received what was expected
    232     MockSpdyPeer.InFrame pingFrame = peer.takeFrame();
    233     assertEquals(TYPE_PING, pingFrame.type);
    234     assertEquals(1, pingFrame.payload1);
    235     assertEquals(0, pingFrame.payload2);
    236     assertFalse(pingFrame.ack);
    237   }
    238 
    239   @Test public void unexpectedPingIsNotReturned() throws Exception {
    240     // write the mocking script
    241     peer.sendFrame().ping(false, 2, 0);
    242     peer.acceptFrame(); // PING
    243     peer.sendFrame().ping(true, 3, 0); // This ping will not be returned.
    244     peer.sendFrame().ping(false, 4, 0);
    245     peer.acceptFrame(); // PING
    246     peer.play();
    247 
    248     // play it back
    249     connection(peer, SPDY3);
    250 
    251     // verify the peer received what was expected
    252     MockSpdyPeer.InFrame ping2 = peer.takeFrame();
    253     assertEquals(2, ping2.payload1);
    254     MockSpdyPeer.InFrame ping4 = peer.takeFrame();
    255     assertEquals(4, ping4.payload1);
    256   }
    257 
    258   @Test public void serverSendsSettingsToClient() throws Exception {
    259     // write the mocking script
    260     final Settings settings = new Settings();
    261     settings.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 10);
    262     peer.sendFrame().settings(settings);
    263     peer.sendFrame().ping(false, 2, 0);
    264     peer.acceptFrame(); // PING
    265     peer.play();
    266 
    267     // play it back
    268     final AtomicInteger maxConcurrentStreams = new AtomicInteger();
    269     FramedConnection.Listener listener = new FramedConnection.Listener() {
    270       @Override public void onStream(FramedStream stream) throws IOException {
    271         throw new AssertionError();
    272       }
    273       @Override public void onSettings(FramedConnection connection) {
    274         maxConcurrentStreams.set(connection.maxConcurrentStreams());
    275       }
    276     };
    277     FramedConnection connection = connectionBuilder(peer, SPDY3)
    278         .listener(listener)
    279         .build();
    280 
    281     peer.takeFrame(); // Guarantees that the peer Settings frame has been processed.
    282     synchronized (connection) {
    283       assertEquals(10, connection.peerSettings.getMaxConcurrentStreams(-1));
    284     }
    285     assertEquals(10, maxConcurrentStreams.get());
    286   }
    287 
    288   @Test public void multipleSettingsFramesAreMerged() throws Exception {
    289     // write the mocking script
    290     Settings settings1 = new Settings();
    291     settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100);
    292     settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200);
    293     settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300);
    294     peer.sendFrame().settings(settings1);
    295     Settings settings2 = new Settings();
    296     settings2.set(Settings.DOWNLOAD_BANDWIDTH, 0, 400);
    297     settings2.set(Settings.DOWNLOAD_RETRANS_RATE, PERSIST_VALUE, 500);
    298     settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600);
    299     peer.sendFrame().settings(settings2);
    300     peer.sendFrame().ping(false, 2, 0);
    301     peer.acceptFrame();
    302     peer.play();
    303 
    304     // play it back
    305     FramedConnection connection = connection(peer, SPDY3);
    306 
    307     peer.takeFrame(); // Guarantees that the Settings frame has been processed.
    308     synchronized (connection) {
    309       assertEquals(100, connection.peerSettings.getUploadBandwidth(-1));
    310       assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.UPLOAD_BANDWIDTH));
    311       assertEquals(400, connection.peerSettings.getDownloadBandwidth(-1));
    312       assertEquals(0, connection.peerSettings.flags(Settings.DOWNLOAD_BANDWIDTH));
    313       assertEquals(500, connection.peerSettings.getDownloadRetransRate(-1));
    314       assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.DOWNLOAD_RETRANS_RATE));
    315       assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1));
    316       assertEquals(PERSIST_VALUE, connection.peerSettings.flags(Settings.MAX_CONCURRENT_STREAMS));
    317     }
    318   }
    319 
    320   @Test public void clearSettingsBeforeMerge() throws Exception {
    321     // write the mocking script
    322     Settings settings1 = new Settings();
    323     settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100);
    324     settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200);
    325     settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300);
    326     peer.sendFrame().settings(settings1);
    327     peer.sendFrame().ping(false, 2, 0);
    328     peer.acceptFrame();
    329     peer.play();
    330 
    331     // play it back
    332     FramedConnection connection = connection(peer, SPDY3);
    333 
    334     peer.takeFrame(); // Guarantees that the Settings frame has been processed.
    335 
    336     // fake a settings frame with clear flag set.
    337     Settings settings2 = new Settings();
    338     settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600);
    339     connection.readerRunnable.settings(true, settings2);
    340 
    341     synchronized (connection) {
    342       assertEquals(-1, connection.peerSettings.getUploadBandwidth(-1));
    343       assertEquals(-1, connection.peerSettings.getDownloadBandwidth(-1));
    344       assertEquals(-1, connection.peerSettings.getDownloadRetransRate(-1));
    345       assertEquals(600, connection.peerSettings.getMaxConcurrentStreams(-1));
    346     }
    347   }
    348 
    349   @Test public void bogusDataFrameDoesNotDisruptConnection() throws Exception {
    350     // write the mocking script
    351     peer.sendFrame().data(true, 41, new Buffer().writeUtf8("bogus"), 5);
    352     peer.acceptFrame(); // RST_STREAM
    353     peer.sendFrame().ping(false, 2, 0);
    354     peer.acceptFrame(); // PING
    355     peer.play();
    356 
    357     // play it back
    358     connection(peer, SPDY3);
    359 
    360     // verify the peer received what was expected
    361     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    362     assertEquals(TYPE_RST_STREAM, rstStream.type);
    363     assertEquals(41, rstStream.streamId);
    364     assertEquals(INVALID_STREAM, rstStream.errorCode);
    365     MockSpdyPeer.InFrame ping = peer.takeFrame();
    366     assertEquals(2, ping.payload1);
    367   }
    368 
    369   @Test public void bogusReplyFrameDoesNotDisruptConnection() throws Exception {
    370     // write the mocking script
    371     peer.sendFrame().synReply(false, 41, headerEntries("a", "android"));
    372     peer.acceptFrame(); // RST_STREAM
    373     peer.sendFrame().ping(false, 2, 0);
    374     peer.acceptFrame(); // PING
    375     peer.play();
    376 
    377     // play it back
    378     connection(peer, SPDY3);
    379 
    380     // verify the peer received what was expected
    381     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    382     assertEquals(TYPE_RST_STREAM, rstStream.type);
    383     assertEquals(41, rstStream.streamId);
    384     assertEquals(INVALID_STREAM, rstStream.errorCode);
    385     MockSpdyPeer.InFrame ping = peer.takeFrame();
    386     assertEquals(2, ping.payload1);
    387   }
    388 
    389   @Test public void clientClosesClientOutputStream() throws Exception {
    390     // write the mocking script
    391     peer.acceptFrame(); // SYN_STREAM
    392     peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
    393     peer.acceptFrame(); // TYPE_DATA
    394     peer.acceptFrame(); // TYPE_DATA with FLAG_FIN
    395     peer.acceptFrame(); // PING
    396     peer.sendFrame().ping(true, 1, 0);
    397     peer.play();
    398 
    399     // play it back
    400     FramedConnection connection = connection(peer, SPDY3);
    401     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, false);
    402     BufferedSink out = Okio.buffer(stream.getSink());
    403     out.writeUtf8("square");
    404     out.flush();
    405     assertEquals(1, connection.openStreamCount());
    406     out.close();
    407     try {
    408       out.writeUtf8("round");
    409       fail();
    410     } catch (Exception expected) {
    411       assertEquals("closed", expected.getMessage());
    412     }
    413     connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received.
    414     assertEquals(0, connection.openStreamCount());
    415 
    416     // verify the peer received what was expected
    417     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    418     assertEquals(TYPE_HEADERS, synStream.type);
    419     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    420     assertFalse(synStream.inFinished);
    421     assertTrue(synStream.outFinished);
    422     MockSpdyPeer.InFrame data = peer.takeFrame();
    423     assertEquals(TYPE_DATA, data.type);
    424     assertFalse(data.inFinished);
    425     assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data));
    426     MockSpdyPeer.InFrame fin = peer.takeFrame();
    427     assertEquals(TYPE_DATA, fin.type);
    428     assertTrue(fin.inFinished);
    429     MockSpdyPeer.InFrame ping = peer.takeFrame();
    430     assertEquals(TYPE_PING, ping.type);
    431     assertEquals(1, ping.payload1);
    432   }
    433 
    434   @Test public void serverClosesClientOutputStream() throws Exception {
    435     // write the mocking script
    436     peer.acceptFrame(); // SYN_STREAM
    437     peer.sendFrame().rstStream(1, CANCEL);
    438     peer.acceptFrame(); // PING
    439     peer.sendFrame().ping(true, 1, 0);
    440     peer.play();
    441 
    442     // play it back
    443     FramedConnection connection = connection(peer, SPDY3);
    444     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true);
    445     BufferedSink out = Okio.buffer(stream.getSink());
    446     connection.ping().roundTripTime(); // Ensure that the RST_CANCEL has been received.
    447     try {
    448       out.writeUtf8("square");
    449       out.flush();
    450       fail();
    451     } catch (IOException expected) {
    452       assertEquals("stream was reset: CANCEL", expected.getMessage());
    453     }
    454     try {
    455       out.close();
    456       fail();
    457     } catch (IOException expected) {
    458       // Close throws because buffered data wasn't flushed.
    459     }
    460     assertEquals(0, connection.openStreamCount());
    461 
    462     // verify the peer received what was expected
    463     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    464     assertEquals(TYPE_HEADERS, synStream.type);
    465     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    466     assertFalse(synStream.inFinished);
    467     assertFalse(synStream.outFinished);
    468     MockSpdyPeer.InFrame ping = peer.takeFrame();
    469     assertEquals(TYPE_PING, ping.type);
    470     assertEquals(1, ping.payload1);
    471   }
    472 
    473   /**
    474    * Test that the client sends a RST_STREAM if doing so won't disrupt the
    475    * output stream.
    476    */
    477   @Test public void clientClosesClientInputStream() throws Exception {
    478     // write the mocking script
    479     peer.acceptFrame(); // SYN_STREAM
    480     peer.acceptFrame(); // RST_STREAM
    481     peer.play();
    482 
    483     // play it back
    484     FramedConnection connection = connection(peer, SPDY3);
    485     FramedStream stream = connection.newStream(headerEntries("a", "android"), false, true);
    486     Source in = stream.getSource();
    487     BufferedSink out = Okio.buffer(stream.getSink());
    488     in.close();
    489     try {
    490       in.read(new Buffer(), 1);
    491       fail();
    492     } catch (IOException expected) {
    493       assertEquals("stream closed", expected.getMessage());
    494     }
    495     try {
    496       out.writeUtf8("a");
    497       out.flush();
    498       fail();
    499     } catch (IOException expected) {
    500       assertEquals("stream finished", expected.getMessage());
    501     }
    502     assertEquals(0, connection.openStreamCount());
    503 
    504     // verify the peer received what was expected
    505     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    506     assertEquals(TYPE_HEADERS, synStream.type);
    507     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    508     assertTrue(synStream.inFinished);
    509     assertFalse(synStream.outFinished);
    510     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    511     assertEquals(TYPE_RST_STREAM, rstStream.type);
    512     assertEquals(CANCEL, rstStream.errorCode);
    513   }
    514 
    515   /**
    516    * Test that the client doesn't send a RST_STREAM if doing so will disrupt
    517    * the output stream.
    518    */
    519   @Test public void clientClosesClientInputStreamIfOutputStreamIsClosed() throws Exception {
    520     // write the mocking script
    521     peer.acceptFrame(); // SYN_STREAM
    522     peer.acceptFrame(); // DATA
    523     peer.acceptFrame(); // DATA with FLAG_FIN
    524     peer.acceptFrame(); // RST_STREAM
    525     peer.play();
    526 
    527     // play it back
    528     FramedConnection connection = connection(peer, SPDY3);
    529     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true);
    530     Source source = stream.getSource();
    531     BufferedSink out = Okio.buffer(stream.getSink());
    532     source.close();
    533     try {
    534       source.read(new Buffer(), 1);
    535       fail();
    536     } catch (IOException expected) {
    537       assertEquals("stream closed", expected.getMessage());
    538     }
    539     out.writeUtf8("square");
    540     out.flush();
    541     out.close();
    542     assertEquals(0, connection.openStreamCount());
    543 
    544     // verify the peer received what was expected
    545     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    546     assertEquals(TYPE_HEADERS, synStream.type);
    547     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    548     assertFalse(synStream.inFinished);
    549     assertFalse(synStream.outFinished);
    550     MockSpdyPeer.InFrame data = peer.takeFrame();
    551     assertEquals(TYPE_DATA, data.type);
    552     assertTrue(Arrays.equals("square".getBytes("UTF-8"), data.data));
    553     MockSpdyPeer.InFrame fin = peer.takeFrame();
    554     assertEquals(TYPE_DATA, fin.type);
    555     assertTrue(fin.inFinished);
    556     assertFalse(fin.outFinished);
    557     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    558     assertEquals(TYPE_RST_STREAM, rstStream.type);
    559     assertEquals(CANCEL, rstStream.errorCode);
    560   }
    561 
    562   @Test public void serverClosesClientInputStream() throws Exception {
    563     // write the mocking script
    564     peer.acceptFrame(); // SYN_STREAM
    565     peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
    566     peer.sendFrame().data(true, 1, new Buffer().writeUtf8("square"), 6);
    567     peer.acceptFrame(); // PING
    568     peer.sendFrame().ping(true, 1, 0);
    569     peer.play();
    570 
    571     // play it back
    572     FramedConnection connection = connection(peer, SPDY3);
    573     FramedStream stream = connection.newStream(headerEntries("a", "android"), false, true);
    574     Source source = stream.getSource();
    575     assertStreamData("square", source);
    576     connection.ping().roundTripTime(); // Ensure that inFinished has been received.
    577     assertEquals(0, connection.openStreamCount());
    578 
    579     // verify the peer received what was expected
    580     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    581     assertEquals(TYPE_HEADERS, synStream.type);
    582     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    583     assertTrue(synStream.inFinished);
    584     assertFalse(synStream.outFinished);
    585   }
    586 
    587   @Test public void remoteDoubleSynReply() throws Exception {
    588     // write the mocking script
    589     peer.acceptFrame(); // SYN_STREAM
    590     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
    591     peer.acceptFrame(); // PING
    592     peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
    593     peer.sendFrame().ping(true, 1, 0);
    594     peer.acceptFrame(); // RST_STREAM
    595     peer.play();
    596 
    597     // play it back
    598     FramedConnection connection = connection(peer, SPDY3);
    599     FramedStream stream = connection.newStream(headerEntries("c", "cola"), true, true);
    600     assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
    601     connection.ping().roundTripTime(); // Ensure that the 2nd SYN REPLY has been received.
    602     try {
    603       stream.getSource().read(new Buffer(), 1);
    604       fail();
    605     } catch (IOException expected) {
    606       assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage());
    607     }
    608 
    609     // verify the peer received what was expected
    610     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    611     assertEquals(TYPE_HEADERS, synStream.type);
    612     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    613     MockSpdyPeer.InFrame ping = peer.takeFrame();
    614     assertEquals(TYPE_PING, ping.type);
    615     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    616     assertEquals(TYPE_RST_STREAM, rstStream.type);
    617     assertEquals(1, rstStream.streamId);
    618     assertEquals(STREAM_IN_USE, rstStream.errorCode);
    619   }
    620 
    621   @Test public void remoteDoubleSynStream() throws Exception {
    622     // write the mocking script
    623     peer.sendFrame().synStream(false, false, 2, 0, headerEntries("a", "android"));
    624     peer.acceptFrame(); // SYN_REPLY
    625     peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "banana"));
    626     peer.acceptFrame(); // RST_STREAM
    627     peer.play();
    628 
    629     // play it back
    630     final AtomicInteger receiveCount = new AtomicInteger();
    631     FramedConnection.Listener listener = new FramedConnection.Listener() {
    632       @Override public void onStream(FramedStream stream) throws IOException {
    633         receiveCount.incrementAndGet();
    634         assertEquals(headerEntries("a", "android"), stream.getRequestHeaders());
    635         assertEquals(null, stream.getErrorCode());
    636         stream.reply(headerEntries("c", "cola"), true);
    637       }
    638     };
    639     new FramedConnection.Builder(true)
    640         .socket(peer.openSocket())
    641         .listener(listener)
    642         .build();
    643 
    644     // verify the peer received what was expected
    645     MockSpdyPeer.InFrame reply = peer.takeFrame();
    646     assertEquals(TYPE_HEADERS, reply.type);
    647     assertEquals(HeadersMode.SPDY_REPLY, reply.headersMode);
    648     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    649     assertEquals(TYPE_RST_STREAM, rstStream.type);
    650     assertEquals(2, rstStream.streamId);
    651     assertEquals(PROTOCOL_ERROR, rstStream.errorCode);
    652     assertEquals(1, receiveCount.intValue());
    653   }
    654 
    655   @Test public void remoteSendsDataAfterInFinished() throws Exception {
    656     // write the mocking script
    657     peer.acceptFrame(); // SYN_STREAM
    658     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
    659     peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5);
    660     peer.sendFrame().data(true, 1, new Buffer().writeUtf8("c3po"), 4); // Ignored.
    661     peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded.
    662     peer.acceptFrame(); // PING
    663     peer.play();
    664 
    665     // play it back
    666     FramedConnection connection = connection(peer, SPDY3);
    667     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
    668     assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
    669     assertStreamData("robot", stream.getSource());
    670 
    671     // verify the peer received what was expected
    672     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    673     assertEquals(TYPE_HEADERS, synStream.type);
    674     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    675     MockSpdyPeer.InFrame ping = peer.takeFrame();
    676     assertEquals(TYPE_PING, ping.type);
    677     assertEquals(2, ping.payload1);
    678   }
    679 
    680   @Test public void clientDoesNotLimitFlowControl() throws Exception {
    681     int dataLength = 64 * 1024 + 1;
    682     // write the mocking script
    683     peer.acceptFrame(); // SYN_STREAM
    684     peer.sendFrame().synReply(false, 1, headerEntries("b", "banana"));
    685     peer.sendFrame().data(false, 1, new Buffer().write(new byte[dataLength]), dataLength);
    686     peer.sendFrame().ping(false, 2, 0); // Ping just to make sure the stream was fastforwarded.
    687     peer.acceptFrame(); // PING
    688     peer.play();
    689 
    690     // play it back
    691     FramedConnection connection = connection(peer, SPDY3);
    692     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true);
    693     assertEquals(headerEntries("b", "banana"), stream.getResponseHeaders());
    694 
    695     // verify the peer received what was expected
    696     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    697     assertEquals(TYPE_HEADERS, synStream.type);
    698     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    699     MockSpdyPeer.InFrame ping = peer.takeFrame();
    700     assertEquals(TYPE_PING, ping.type);
    701     assertEquals(2, ping.payload1);
    702   }
    703 
    704   @Test public void remoteSendsRefusedStreamBeforeReplyHeaders() throws Exception {
    705     // write the mocking script
    706     peer.acceptFrame(); // SYN_STREAM
    707     peer.sendFrame().rstStream(1, REFUSED_STREAM);
    708     peer.sendFrame().ping(false, 2, 0);
    709     peer.acceptFrame(); // PING
    710     peer.play();
    711 
    712     // play it back
    713     FramedConnection connection = connection(peer, SPDY3);
    714     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true);
    715     try {
    716       stream.getResponseHeaders();
    717       fail();
    718     } catch (IOException expected) {
    719       assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage());
    720     }
    721     assertEquals(0, connection.openStreamCount());
    722 
    723     // verify the peer received what was expected
    724     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    725     assertEquals(TYPE_HEADERS, synStream.type);
    726     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    727     MockSpdyPeer.InFrame ping = peer.takeFrame();
    728     assertEquals(TYPE_PING, ping.type);
    729     assertEquals(2, ping.payload1);
    730   }
    731 
    732   @Test public void receiveGoAway() throws Exception {
    733     peer.setVariantAndClient(SPDY3, false);
    734 
    735     // write the mocking script
    736     peer.acceptFrame(); // SYN_STREAM 1
    737     peer.acceptFrame(); // SYN_STREAM 3
    738     peer.acceptFrame(); // PING.
    739     peer.sendFrame().goAway(1, PROTOCOL_ERROR, Util.EMPTY_BYTE_ARRAY);
    740     peer.sendFrame().ping(true, 1, 0);
    741     peer.acceptFrame(); // DATA STREAM 1
    742     peer.play();
    743 
    744     // play it back
    745     FramedConnection connection = connection(peer, SPDY3);
    746     FramedStream stream1 = connection.newStream(headerEntries("a", "android"), true, true);
    747     FramedStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true);
    748     connection.ping().roundTripTime(); // Ensure the GO_AWAY that resets stream2 has been received.
    749     BufferedSink sink1 = Okio.buffer(stream1.getSink());
    750     BufferedSink sink2 = Okio.buffer(stream2.getSink());
    751     sink1.writeUtf8("abc");
    752     try {
    753       sink2.writeUtf8("abc");
    754       sink2.flush();
    755       fail();
    756     } catch (IOException expected) {
    757       assertEquals("stream was reset: REFUSED_STREAM", expected.getMessage());
    758     }
    759     sink1.writeUtf8("def");
    760     sink1.close();
    761     try {
    762       connection.newStream(headerEntries("c", "cola"), true, true);
    763       fail();
    764     } catch (IOException expected) {
    765       assertEquals("shutdown", expected.getMessage());
    766     }
    767     assertTrue(stream1.isOpen());
    768     assertFalse(stream2.isOpen());
    769     assertEquals(1, connection.openStreamCount());
    770 
    771     // verify the peer received what was expected
    772     MockSpdyPeer.InFrame synStream1 = peer.takeFrame();
    773     assertEquals(TYPE_HEADERS, synStream1.type);
    774     MockSpdyPeer.InFrame synStream2 = peer.takeFrame();
    775     assertEquals(TYPE_HEADERS, synStream2.type);
    776     MockSpdyPeer.InFrame ping = peer.takeFrame();
    777     assertEquals(TYPE_PING, ping.type);
    778     MockSpdyPeer.InFrame data1 = peer.takeFrame();
    779     assertEquals(TYPE_DATA, data1.type);
    780     assertEquals(1, data1.streamId);
    781     assertTrue(Arrays.equals("abcdef".getBytes("UTF-8"), data1.data));
    782   }
    783 
    784   @Test public void sendGoAway() throws Exception {
    785     // write the mocking script
    786     peer.acceptFrame(); // SYN_STREAM 1
    787     peer.acceptFrame(); // GOAWAY
    788     peer.acceptFrame(); // PING
    789     peer.sendFrame().synStream(false, false, 2, 0, headerEntries("b", "b")); // Should be ignored!
    790     peer.sendFrame().ping(true, 1, 0);
    791     peer.play();
    792 
    793     // play it back
    794     FramedConnection connection = connection(peer, SPDY3);
    795     connection.newStream(headerEntries("a", "android"), true, true);
    796     Ping ping = connection.ping();
    797     connection.shutdown(PROTOCOL_ERROR);
    798     assertEquals(1, connection.openStreamCount());
    799     ping.roundTripTime(); // Prevent the peer from exiting prematurely.
    800 
    801     // verify the peer received what was expected
    802     MockSpdyPeer.InFrame synStream1 = peer.takeFrame();
    803     assertEquals(TYPE_HEADERS, synStream1.type);
    804     MockSpdyPeer.InFrame pingFrame = peer.takeFrame();
    805     assertEquals(TYPE_PING, pingFrame.type);
    806     MockSpdyPeer.InFrame goaway = peer.takeFrame();
    807     assertEquals(TYPE_GOAWAY, goaway.type);
    808     assertEquals(0, goaway.streamId);
    809     assertEquals(PROTOCOL_ERROR, goaway.errorCode);
    810   }
    811 
    812   @Test public void noPingsAfterShutdown() throws Exception {
    813     // write the mocking script
    814     peer.acceptFrame(); // GOAWAY
    815     peer.play();
    816 
    817     // play it back
    818     FramedConnection connection = connection(peer, SPDY3);
    819     connection.shutdown(INTERNAL_ERROR);
    820     try {
    821       connection.ping();
    822       fail();
    823     } catch (IOException expected) {
    824       assertEquals("shutdown", expected.getMessage());
    825     }
    826 
    827     // verify the peer received what was expected
    828     MockSpdyPeer.InFrame goaway = peer.takeFrame();
    829     assertEquals(TYPE_GOAWAY, goaway.type);
    830     assertEquals(INTERNAL_ERROR, goaway.errorCode);
    831   }
    832 
    833   @Test public void close() throws Exception {
    834     // write the mocking script
    835     peer.acceptFrame(); // SYN_STREAM
    836     peer.acceptFrame(); // GOAWAY
    837     peer.acceptFrame(); // RST_STREAM
    838     peer.play();
    839 
    840     // play it back
    841     FramedConnection connection = connection(peer, SPDY3);
    842     FramedStream stream = connection.newStream(headerEntries("a", "android"), true, true);
    843     assertEquals(1, connection.openStreamCount());
    844     connection.close();
    845     assertEquals(0, connection.openStreamCount());
    846     try {
    847       connection.newStream(headerEntries("b", "banana"), true, true);
    848       fail();
    849     } catch (IOException expected) {
    850       assertEquals("shutdown", expected.getMessage());
    851     }
    852     BufferedSink sink = Okio.buffer(stream.getSink());
    853     try {
    854       sink.writeByte(0);
    855       sink.flush();
    856       fail();
    857     } catch (IOException expected) {
    858       assertEquals("stream was reset: CANCEL", expected.getMessage());
    859     }
    860     try {
    861       stream.getSource().read(new Buffer(), 1);
    862       fail();
    863     } catch (IOException expected) {
    864       assertEquals("stream was reset: CANCEL", expected.getMessage());
    865     }
    866 
    867     // verify the peer received what was expected
    868     MockSpdyPeer.InFrame synStream = peer.takeFrame();
    869     assertEquals(TYPE_HEADERS, synStream.type);
    870     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
    871     MockSpdyPeer.InFrame goaway = peer.takeFrame();
    872     assertEquals(TYPE_GOAWAY, goaway.type);
    873     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
    874     assertEquals(TYPE_RST_STREAM, rstStream.type);
    875     assertEquals(1, rstStream.streamId);
    876   }
    877 
    878   @Test public void closeCancelsPings() throws Exception {
    879     // write the mocking script
    880     peer.acceptFrame(); // PING
    881     peer.acceptFrame(); // GOAWAY
    882     peer.play();
    883 
    884     // play it back
    885     FramedConnection connection = connection(peer, SPDY3);
    886     Ping ping = connection.ping();
    887     connection.close();
    888     assertEquals(-1, ping.roundTripTime());
    889   }
    890 
    891   @Test public void getResponseHeadersTimesOut() throws Exception {
    892     // write the mocking script
    893     peer.acceptFrame(); // SYN_STREAM
    894     peer.acceptFrame(); // RST_STREAM
    895     peer.play();
    896 
    897     // play it back
    898     FramedConnection connection = connection(peer, SPDY3);
    899     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
    900     stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS);
    901     long startNanos = System.nanoTime();
    902     try {
    903       stream.getResponseHeaders();
    904       fail();
    905     } catch (InterruptedIOException expected) {
    906     }
    907     long elapsedNanos = System.nanoTime() - startNanos;
    908     awaitWatchdogIdle();
    909     assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */);
    910     assertEquals(0, connection.openStreamCount());
    911 
    912     // verify the peer received what was expected
    913     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
    914     assertEquals(TYPE_RST_STREAM, peer.takeFrame().type);
    915   }
    916 
    917   @Test public void readTimesOut() throws Exception {
    918     // write the mocking script
    919     peer.acceptFrame(); // SYN_STREAM
    920     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
    921     peer.acceptFrame(); // RST_STREAM
    922     peer.play();
    923 
    924     // play it back
    925     FramedConnection connection = connection(peer, SPDY3);
    926     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
    927     stream.readTimeout().timeout(500, TimeUnit.MILLISECONDS);
    928     Source source = stream.getSource();
    929     long startNanos = System.nanoTime();
    930     try {
    931       source.read(new Buffer(), 1);
    932       fail();
    933     } catch (InterruptedIOException expected) {
    934     }
    935     long elapsedNanos = System.nanoTime() - startNanos;
    936     awaitWatchdogIdle();
    937     assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */);
    938     assertEquals(0, connection.openStreamCount());
    939 
    940     // verify the peer received what was expected
    941     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
    942     assertEquals(TYPE_RST_STREAM, peer.takeFrame().type);
    943   }
    944 
    945   @Test public void writeTimesOutAwaitingStreamWindow() throws Exception {
    946     // Set the peer's receive window to 5 bytes!
    947     Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5);
    948 
    949     // write the mocking script
    950     peer.sendFrame().settings(peerSettings);
    951     peer.acceptFrame(); // PING
    952     peer.sendFrame().ping(true, 1, 0);
    953     peer.acceptFrame(); // SYN_STREAM
    954     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
    955     peer.acceptFrame(); // DATA
    956     peer.acceptFrame(); // RST_STREAM
    957     peer.play();
    958 
    959     // play it back
    960     FramedConnection connection = connection(peer, SPDY3);
    961     connection.ping().roundTripTime(); // Make sure settings have been received.
    962     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
    963     Sink sink = stream.getSink();
    964     sink.write(new Buffer().writeUtf8("abcde"), 5);
    965     stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS);
    966     long startNanos = System.nanoTime();
    967     sink.write(new Buffer().writeUtf8("f"), 1);
    968     try {
    969       sink.flush(); // This will time out waiting on the write window.
    970       fail();
    971     } catch (InterruptedIOException expected) {
    972     }
    973     long elapsedNanos = System.nanoTime() - startNanos;
    974     awaitWatchdogIdle();
    975     assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */);
    976     assertEquals(0, connection.openStreamCount());
    977 
    978     // verify the peer received what was expected
    979     assertEquals(TYPE_PING, peer.takeFrame().type);
    980     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
    981     assertEquals(TYPE_DATA, peer.takeFrame().type);
    982     assertEquals(TYPE_RST_STREAM, peer.takeFrame().type);
    983   }
    984 
    985   @Test public void writeTimesOutAwaitingConnectionWindow() throws Exception {
    986     // Set the peer's receive window to 5 bytes. Give the stream 5 bytes back, so only the
    987     // connection-level window is applicable.
    988     Settings peerSettings = new Settings().set(Settings.INITIAL_WINDOW_SIZE, PERSIST_VALUE, 5);
    989 
    990     // write the mocking script
    991     peer.sendFrame().settings(peerSettings);
    992     peer.acceptFrame(); // SYN_STREAM
    993     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
    994     peer.sendFrame().windowUpdate(1, 5);
    995     peer.acceptFrame(); // PING
    996     peer.sendFrame().ping(true, 1, 0);
    997     peer.acceptFrame(); // DATA
    998     peer.acceptFrame(); // RST_STREAM
    999     peer.play();
   1000 
   1001     // play it back
   1002     FramedConnection connection = connection(peer, SPDY3);
   1003     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1004     connection.ping().roundTripTime(); // Make sure the window update has been received.
   1005     Sink sink = stream.getSink();
   1006     stream.writeTimeout().timeout(500, TimeUnit.MILLISECONDS);
   1007     sink.write(new Buffer().writeUtf8("abcdef"), 6);
   1008     long startNanos = System.nanoTime();
   1009     try {
   1010       sink.flush(); // This will time out waiting on the write window.
   1011       fail();
   1012     } catch (InterruptedIOException expected) {
   1013     }
   1014     long elapsedNanos = System.nanoTime() - startNanos;
   1015     awaitWatchdogIdle();
   1016     assertEquals(500d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */);
   1017     assertEquals(0, connection.openStreamCount());
   1018 
   1019     // verify the peer received what was expected
   1020     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
   1021     assertEquals(TYPE_PING, peer.takeFrame().type);
   1022     assertEquals(TYPE_DATA, peer.takeFrame().type);
   1023     assertEquals(TYPE_RST_STREAM, peer.takeFrame().type);
   1024   }
   1025 
   1026   @Test public void outgoingWritesAreBatched() throws Exception {
   1027     // write the mocking script
   1028     peer.acceptFrame(); // SYN_STREAM
   1029     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1030     peer.acceptFrame(); // DATA
   1031     peer.play();
   1032 
   1033     // play it back
   1034     FramedConnection connection = connection(peer, SPDY3);
   1035     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1036 
   1037     // two outgoing writes
   1038     Sink sink = stream.getSink();
   1039     sink.write(new Buffer().writeUtf8("abcde"), 5);
   1040     sink.write(new Buffer().writeUtf8("fghij"), 5);
   1041     sink.close();
   1042 
   1043     // verify the peer received one incoming frame
   1044     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
   1045     MockSpdyPeer.InFrame data = peer.takeFrame();
   1046     assertEquals(TYPE_DATA, data.type);
   1047     assertTrue(Arrays.equals("abcdefghij".getBytes("UTF-8"), data.data));
   1048     assertTrue(data.inFinished);
   1049   }
   1050 
   1051   @Test public void headers() throws Exception {
   1052     // write the mocking script
   1053     peer.acceptFrame(); // SYN_STREAM
   1054     peer.acceptFrame(); // PING
   1055     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1056     peer.sendFrame().headers(1, headerEntries("c", "c3po"));
   1057     peer.sendFrame().ping(true, 1, 0);
   1058     peer.play();
   1059 
   1060     // play it back
   1061     FramedConnection connection = connection(peer, SPDY3);
   1062     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1063     connection.ping().roundTripTime(); // Ensure that the HEADERS has been received.
   1064     assertEquals(headerEntries("a", "android", "c", "c3po"), stream.getResponseHeaders());
   1065 
   1066     // verify the peer received what was expected
   1067     MockSpdyPeer.InFrame synStream = peer.takeFrame();
   1068     assertEquals(TYPE_HEADERS, synStream.type);
   1069     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
   1070     MockSpdyPeer.InFrame ping = peer.takeFrame();
   1071     assertEquals(TYPE_PING, ping.type);
   1072   }
   1073 
   1074   @Test public void headersBeforeReply() throws Exception {
   1075     // write the mocking script
   1076     peer.acceptFrame(); // SYN_STREAM
   1077     peer.acceptFrame(); // PING
   1078     peer.sendFrame().headers(1, headerEntries("c", "c3po"));
   1079     peer.acceptFrame(); // RST_STREAM
   1080     peer.sendFrame().ping(true, 1, 0);
   1081     peer.play();
   1082 
   1083     // play it back
   1084     FramedConnection connection = connection(peer, SPDY3);
   1085     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1086     connection.ping().roundTripTime(); // Ensure that the HEADERS has been received.
   1087     try {
   1088       stream.getResponseHeaders();
   1089       fail();
   1090     } catch (IOException expected) {
   1091       assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage());
   1092     }
   1093 
   1094     // verify the peer received what was expected
   1095     MockSpdyPeer.InFrame synStream = peer.takeFrame();
   1096     assertEquals(TYPE_HEADERS, synStream.type);
   1097     assertEquals(HeadersMode.SPDY_SYN_STREAM, synStream.headersMode);
   1098     MockSpdyPeer.InFrame ping = peer.takeFrame();
   1099     assertEquals(TYPE_PING, ping.type);
   1100     MockSpdyPeer.InFrame rstStream = peer.takeFrame();
   1101     assertEquals(TYPE_RST_STREAM, rstStream.type);
   1102     assertEquals(PROTOCOL_ERROR, rstStream.errorCode);
   1103   }
   1104 
   1105   @Test public void readSendsWindowUpdate() throws Exception {
   1106     peer.setVariantAndClient(SPDY3, false);
   1107 
   1108     int windowSize = 100;
   1109     int windowUpdateThreshold = 50;
   1110 
   1111     // Write the mocking script.
   1112     peer.acceptFrame(); // SYN_STREAM
   1113     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1114     for (int i = 0; i < 3; i++) {
   1115       // Send frames of summing to size 50, which is windowUpdateThreshold.
   1116       peer.sendFrame().data(false, 1, data(24), 24);
   1117       peer.sendFrame().data(false, 1, data(25), 25);
   1118       peer.sendFrame().data(false, 1, data(1), 1);
   1119       peer.acceptFrame(); // connection WINDOW UPDATE
   1120       peer.acceptFrame(); // stream WINDOW UPDATE
   1121     }
   1122     peer.sendFrame().data(true, 1, data(0), 0);
   1123     peer.play();
   1124 
   1125     // Play it back.
   1126     FramedConnection connection = connection(peer, SPDY3);
   1127     connection.okHttpSettings.set(Settings.INITIAL_WINDOW_SIZE, 0, windowSize);
   1128     FramedStream stream = connection.newStream(headerEntries("b", "banana"), false, true);
   1129     assertEquals(0, stream.unacknowledgedBytesRead);
   1130     assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
   1131     Source in = stream.getSource();
   1132     Buffer buffer = new Buffer();
   1133     buffer.writeAll(in);
   1134     assertEquals(-1, in.read(buffer, 1));
   1135     assertEquals(150, buffer.size());
   1136 
   1137     MockSpdyPeer.InFrame synStream = peer.takeFrame();
   1138     assertEquals(TYPE_HEADERS, synStream.type);
   1139     for (int i = 0; i < 3; i++) {
   1140       List<Integer> windowUpdateStreamIds = new ArrayList<>(2);
   1141       for (int j = 0; j < 2; j++) {
   1142         MockSpdyPeer.InFrame windowUpdate = peer.takeFrame();
   1143         assertEquals(TYPE_WINDOW_UPDATE, windowUpdate.type);
   1144         windowUpdateStreamIds.add(windowUpdate.streamId);
   1145         assertEquals(windowUpdateThreshold, windowUpdate.windowSizeIncrement);
   1146       }
   1147       assertTrue(windowUpdateStreamIds.contains(0)); // connection
   1148       assertTrue(windowUpdateStreamIds.contains(1)); // stream
   1149     }
   1150   }
   1151 
   1152   private Buffer data(int byteCount) {
   1153     return new Buffer().write(new byte[byteCount]);
   1154   }
   1155 
   1156   @Test public void serverSendsEmptyDataClientDoesntSendWindowUpdate() throws Exception {
   1157     peer.setVariantAndClient(SPDY3, false);
   1158 
   1159     // Write the mocking script.
   1160     peer.acceptFrame(); // SYN_STREAM
   1161     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1162     peer.sendFrame().data(true, 1, data(0), 0);
   1163     peer.play();
   1164 
   1165     // Play it back.
   1166     FramedConnection connection = connection(peer, SPDY3);
   1167     FramedStream client = connection.newStream(headerEntries("b", "banana"), false, true);
   1168     assertEquals(-1, client.getSource().read(new Buffer(), 1));
   1169 
   1170     // Verify the peer received what was expected.
   1171     MockSpdyPeer.InFrame synStream = peer.takeFrame();
   1172     assertEquals(TYPE_HEADERS, synStream.type);
   1173     assertEquals(3, peer.frameCount());
   1174   }
   1175 
   1176   @Test public void clientSendsEmptyDataServerDoesntSendWindowUpdate() throws Exception {
   1177     peer.setVariantAndClient(SPDY3, false);
   1178 
   1179     // Write the mocking script.
   1180     peer.acceptFrame(); // SYN_STREAM
   1181     peer.acceptFrame(); // DATA
   1182     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1183     peer.play();
   1184 
   1185     // Play it back.
   1186     FramedConnection connection = connection(peer, SPDY3);
   1187     FramedStream client = connection.newStream(headerEntries("b", "banana"), true, true);
   1188     BufferedSink out = Okio.buffer(client.getSink());
   1189     out.write(Util.EMPTY_BYTE_ARRAY);
   1190     out.flush();
   1191     out.close();
   1192 
   1193     // Verify the peer received what was expected.
   1194     assertEquals(TYPE_HEADERS, peer.takeFrame().type);
   1195     assertEquals(TYPE_DATA, peer.takeFrame().type);
   1196     assertEquals(3, peer.frameCount());
   1197   }
   1198 
   1199   @Test public void testTruncatedDataFrame() throws Exception {
   1200     // write the mocking script
   1201     peer.acceptFrame(); // SYN_STREAM
   1202     peer.sendFrame().synReply(false, 1, headerEntries("a", "android"));
   1203     peer.sendFrame().data(false, 1, data(1024), 1024);
   1204     peer.truncateLastFrame(8 + 100);
   1205     peer.play();
   1206 
   1207     // play it back
   1208     FramedConnection connection = connection(peer, SPDY3);
   1209     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1210     assertEquals(headerEntries("a", "android"), stream.getResponseHeaders());
   1211     Source in = stream.getSource();
   1212     try {
   1213       Okio.buffer(in).readByteString(101);
   1214       fail();
   1215     } catch (IOException expected) {
   1216       assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage());
   1217     }
   1218   }
   1219 
   1220   @Test public void blockedStreamDoesntStarveNewStream() throws Exception {
   1221     int framesThatFillWindow = roundUp(DEFAULT_INITIAL_WINDOW_SIZE, peer.maxOutboundDataLength());
   1222 
   1223     // Write the mocking script. This accepts more data frames than necessary!
   1224     peer.acceptFrame(); // SYN_STREAM on stream 1
   1225     for (int i = 0; i < framesThatFillWindow; i++) {
   1226       peer.acceptFrame(); // DATA on stream 1
   1227     }
   1228     peer.acceptFrame(); // SYN_STREAM on stream 2
   1229     peer.acceptFrame(); // DATA on stream 2
   1230     peer.play();
   1231 
   1232     // Play it back.
   1233     FramedConnection connection = connection(peer, SPDY3);
   1234     FramedStream stream1 = connection.newStream(headerEntries("a", "apple"), true, true);
   1235     BufferedSink out1 = Okio.buffer(stream1.getSink());
   1236     out1.write(new byte[DEFAULT_INITIAL_WINDOW_SIZE]);
   1237     out1.flush();
   1238 
   1239     // Check that we've filled the window for both the stream and also the connection.
   1240     assertEquals(0, connection.bytesLeftInWriteWindow);
   1241     assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow);
   1242 
   1243     // receiving a window update on the the connection will unblock new streams.
   1244     connection.readerRunnable.windowUpdate(0, 3);
   1245 
   1246     assertEquals(3, connection.bytesLeftInWriteWindow);
   1247     assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow);
   1248 
   1249     // Another stream should be able to send data even though 1 is blocked.
   1250     FramedStream stream2 = connection.newStream(headerEntries("b", "banana"), true, true);
   1251     BufferedSink out2 = Okio.buffer(stream2.getSink());
   1252     out2.writeUtf8("foo");
   1253     out2.flush();
   1254 
   1255     assertEquals(0, connection.bytesLeftInWriteWindow);
   1256     assertEquals(0, connection.getStream(1).bytesLeftInWriteWindow);
   1257     assertEquals(DEFAULT_INITIAL_WINDOW_SIZE - 3, connection.getStream(3).bytesLeftInWriteWindow);
   1258   }
   1259 
   1260   /** https://github.com/square/okhttp/issues/333 */
   1261   @Test public void headerBlockHasTrailingCompressedBytes512() throws Exception {
   1262     // This specially-formatted frame has trailing deflated bytes after the name value block.
   1263     String frame = "gAMAAgAAAgkAAAABeLvjxqfCYgAAAAD//2IAAAAA//9iAAAAAP//YgQAAAD//2IAAAAA//9iAAAAAP/"
   1264         + "/YgAAAAD//2IEAAAA//9KBAAAAP//YgAAAAD//2IAAAAA//9iAAAAAP//sgEAAAD//2IAAAAA\n//9iBAAAAP//Y"
   1265         + "gIAAAD//2IGAAAA//9iAQAAAP//YgUAAAD//2IDAAAA//9iBwAAAP//4gAAAAD//+IEAAAA///iAgAAAP//4gYAA"
   1266         + "AD//+IBAAAA///iBQAAAP//4gMAAAD//+IHAAAA//8SAAAAAP//EgQAAAD//xICAAAA//8SBgAAAP//EgEAAAD//"
   1267         + "xIFAAAA//8SAwAAAP//EgcAAAD//5IAAAAA//+SBAAAAP//kgIAAAD//5IGAAAA//+SAQAAAP//kgUAAAD//5IDA"
   1268         + "AAA//+SBwAAAP//UgAAAAD//1IEAAAA//9SAgAAAP//UgYAAAD//1IBAAAA//9SBQAAAP//UgMAAAD//1IHAAAA/"
   1269         + "//SAAAAAP//0gQAAAD//9ICAAAA///SBgAAAP//0gEAAAD//9IFAAAA///SAwAAAP//0gcAAAD//zIAAAAA//8yB"
   1270         + "AAAAP//MgIAAAD//zIGAAAA//8yAQAAAP//MgUAAAD//zIDAAAA//8yBwAAAP//sgAAAAD//7IEAAAA//+yAgAAA"
   1271         + "P//sgYAAAD//w==";
   1272     headerBlockHasTrailingCompressedBytes(frame, 60);
   1273   }
   1274 
   1275   @Test public void headerBlockHasTrailingCompressedBytes2048() throws Exception {
   1276     // This specially-formatted frame has trailing deflated bytes after the name value block.
   1277     String frame = "gAMAAgAAB/sAAAABeLvjxqfCAqYjRhAGJmxGxUQAAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA"
   1278         + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/"
   1279         + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ"
   1280         + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD"
   1281         + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o"
   1282         + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA"
   1283         + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9"
   1284         + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA"
   1285         + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/"
   1286         + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ"
   1287         + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD"
   1288         + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o"
   1289         + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA"
   1290         + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9"
   1291         + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA"
   1292         + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/"
   1293         + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ"
   1294         + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD"
   1295         + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o"
   1296         + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA"
   1297         + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9"
   1298         + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA"
   1299         + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/"
   1300         + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ"
   1301         + "AAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD"
   1302         + "//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0o"
   1303         + "EAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAA"
   1304         + "A//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9"
   1305         + "KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAA"
   1306         + "AAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP/"
   1307         + "/SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQAAAD//0oEAAAA//9KBAAAAP//SgQ"
   1308         + "AAAD//0oEAAAA//8=";
   1309     headerBlockHasTrailingCompressedBytes(frame, 289);
   1310   }
   1311 
   1312   private void headerBlockHasTrailingCompressedBytes(String frame, int length) throws IOException {
   1313     // write the mocking script
   1314     peer.acceptFrame(); // SYN_STREAM
   1315     byte[] trailingCompressedBytes = ByteString.decodeBase64(frame).toByteArray();
   1316     trailingCompressedBytes[11] = 1; // Set SPDY/3 stream ID to 3.
   1317     peer.sendFrame(trailingCompressedBytes);
   1318     peer.sendFrame().data(true, 1, new Buffer().writeUtf8("robot"), 5);
   1319     peer.acceptFrame(); // DATA
   1320     peer.play();
   1321 
   1322     // play it back
   1323     FramedConnection connection = connection(peer, SPDY3);
   1324     FramedStream stream = connection.newStream(headerEntries("b", "banana"), true, true);
   1325     assertEquals("a", stream.getResponseHeaders().get(0).name.utf8());
   1326     assertEquals(length, stream.getResponseHeaders().get(0).value.size());
   1327     assertStreamData("robot", stream.getSource());
   1328   }
   1329 
   1330   @Test public void socketExceptionWhileWritingHeaders() throws Exception {
   1331     peer.acceptFrame(); // SYN_STREAM.
   1332     peer.play();
   1333 
   1334     String longString = ByteString.of(randomBytes(2048)).base64();
   1335     Socket socket = peer.openSocket();
   1336     FramedConnection connection = new FramedConnection.Builder(true)
   1337         .socket(socket)
   1338         .protocol(SPDY3.getProtocol())
   1339         .build();
   1340     socket.shutdownOutput();
   1341     try {
   1342       connection.newStream(headerEntries("a", longString), false, true);
   1343       fail();
   1344     } catch (IOException expected) {
   1345     }
   1346     try {
   1347       connection.newStream(headerEntries("b", longString), false, true);
   1348       fail();
   1349     } catch (IOException expected) {
   1350     }
   1351   }
   1352 
   1353   private byte[] randomBytes(int length) {
   1354     byte[] bytes = new byte[length];
   1355     new Random(0).nextBytes(bytes);
   1356     return bytes;
   1357   }
   1358 
   1359   private FramedConnection connection(MockSpdyPeer peer, Variant variant) throws IOException {
   1360     return connectionBuilder(peer, variant).build();
   1361   }
   1362 
   1363   private FramedConnection.Builder connectionBuilder(MockSpdyPeer peer, Variant variant)
   1364       throws IOException {
   1365     return new FramedConnection.Builder(true)
   1366         .socket(peer.openSocket())
   1367         .protocol(variant.getProtocol());
   1368   }
   1369 
   1370   private void assertStreamData(String expected, Source source) throws IOException {
   1371     String actual = Okio.buffer(source).readUtf8();
   1372     assertEquals(expected, actual);
   1373   }
   1374 
   1375   /** Interrupts the current thread after {@code delayMillis}. */
   1376   private void interruptAfterDelay(final long delayMillis) {
   1377     final Thread toInterrupt = Thread.currentThread();
   1378     new Thread("interrupting cow") {
   1379       @Override public void run() {
   1380         try {
   1381           Thread.sleep(delayMillis);
   1382           toInterrupt.interrupt();
   1383         } catch (InterruptedException e) {
   1384           throw new AssertionError();
   1385         }
   1386       }
   1387     }.start();
   1388   }
   1389 
   1390   /**
   1391    * Returns true when all work currently in progress by the watchdog have completed. This method
   1392    * creates more work for the watchdog and waits for that work to be executed. When it is, we know
   1393    * work that preceded this call is complete.
   1394    */
   1395   private void awaitWatchdogIdle() throws InterruptedException {
   1396     final CountDownLatch latch = new CountDownLatch(1);
   1397     AsyncTimeout watchdogJob = new AsyncTimeout() {
   1398       @Override protected void timedOut() {
   1399         latch.countDown();
   1400       }
   1401     };
   1402     watchdogJob.deadlineNanoTime(System.nanoTime()); // Due immediately!
   1403     watchdogJob.enter();
   1404     latch.await();
   1405   }
   1406 
   1407   static int roundUp(int num, int divisor) {
   1408     return (num + divisor - 1) / divisor;
   1409   }
   1410 }
   1411