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