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