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