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    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