1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-27 04:22:07 +03:00

Merge pull request #86 from square/jwilson/careful

Be more careful around IOExceptions.
This commit is contained in:
Jesse Wilson
2013-01-21 11:42:47 -08:00
8 changed files with 193 additions and 132 deletions

View File

@@ -28,6 +28,7 @@ import java.net.URI;
import java.net.URL;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -313,4 +314,14 @@ public final class Util {
}
return result.toString();
}
public static ThreadFactory newThreadFactory(final String name, final boolean daemon) {
return new ThreadFactory() {
@Override public Thread newThread(Runnable r) {
Thread result = new Thread(r, name);
result.setDaemon(daemon);
return result;
}
};
}
}

View File

@@ -117,11 +117,11 @@ public final class SpdyConnection implements Closeable {
String prefix = builder.client ? "Spdy Client " : "Spdy Server ";
readExecutor = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Threads.newThreadFactory(prefix + "Reader", false));
new SynchronousQueue<Runnable>(), Util.newThreadFactory(prefix + "Reader", false));
writeExecutor = new ThreadPoolExecutor(0, 1, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(), Threads.newThreadFactory(prefix + "Writer", false));
new LinkedBlockingQueue<Runnable>(), Util.newThreadFactory(prefix + "Writer", false));
callbackExecutor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Threads.newThreadFactory(prefix + "Callbacks", false));
new SynchronousQueue<Runnable>(), Util.newThreadFactory(prefix + "Callbacks", false));
readExecutor.execute(new Reader());
}
@@ -317,7 +317,17 @@ public final class SpdyConnection implements Closeable {
* internal executor services.
*/
@Override public void close() throws IOException {
shutdown(GOAWAY_OK);
close(GOAWAY_OK, SpdyStream.RST_CANCEL);
}
private void close(int shutdownStatusCode, int rstStatusCode) throws IOException {
assert (!Thread.holdsLock(this));
IOException thrown = null;
try {
shutdown(shutdownStatusCode);
} catch (IOException e) {
thrown = e;
}
SpdyStream[] streamsToClose = null;
Ping[] pingsToCancel = null;
@@ -335,8 +345,9 @@ public final class SpdyConnection implements Closeable {
if (streamsToClose != null) {
for (SpdyStream stream : streamsToClose) {
try {
stream.close(SpdyStream.RST_CANCEL);
} catch (Throwable ignored) {
stream.close(rstStatusCode);
} catch (IOException e) {
if (thrown != null) thrown = e;
}
}
}
@@ -350,7 +361,17 @@ public final class SpdyConnection implements Closeable {
writeExecutor.shutdown();
callbackExecutor.shutdown();
readExecutor.shutdown();
Util.closeAll(spdyReader, spdyWriter);
try {
spdyReader.close();
} catch (IOException e) {
thrown = e;
}
try {
spdyWriter.close();
} catch (IOException e) {
if (thrown == null) thrown = e;
}
if (thrown != null) throw thrown;
}
public static class Builder {
@@ -389,15 +410,21 @@ public final class SpdyConnection implements Closeable {
private class Reader implements Runnable, SpdyReader.Handler {
@Override public void run() {
int shutdownStatusCode = GOAWAY_INTERNAL_ERROR;
int rstStatusCode = SpdyStream.RST_INTERNAL_ERROR;
try {
while (spdyReader.nextFrame(this)) {
}
} catch (ProtocolException e) {
shutdownLater(GOAWAY_PROTOCOL_ERROR);
shutdownStatusCode = GOAWAY_OK;
rstStatusCode = SpdyStream.RST_CANCEL;
} catch (IOException e) {
throw new RuntimeException(e);
shutdownStatusCode = GOAWAY_PROTOCOL_ERROR;
rstStatusCode = SpdyStream.RST_PROTOCOL_ERROR;
} finally {
Util.closeQuietly(SpdyConnection.this);
try {
close(shutdownStatusCode, rstStatusCode);
} catch (IOException ignored) {
}
}
}
@@ -409,14 +436,9 @@ public final class SpdyConnection implements Closeable {
Util.skipByReading(in, length);
return;
}
try {
dataStream.receiveData(in, length);
if ((flags & SpdyConnection.FLAG_FIN) != 0) {
dataStream.receiveFin();
}
} catch (ProtocolException e) {
Util.skipByReading(in, length);
dataStream.closeLater(SpdyStream.RST_FLOW_CONTROL_ERROR);
dataStream.receiveData(in, length);
if ((flags & SpdyConnection.FLAG_FIN) != 0) {
dataStream.receiveFin();
}
}
@@ -456,13 +478,9 @@ public final class SpdyConnection implements Closeable {
writeSynResetLater(streamId, SpdyStream.RST_INVALID_STREAM);
return;
}
try {
replyStream.receiveReply(nameValueBlock);
if ((flags & SpdyConnection.FLAG_FIN) != 0) {
replyStream.receiveFin();
}
} catch (ProtocolException e) {
replyStream.closeLater(SpdyStream.RST_STREAM_IN_USE);
replyStream.receiveReply(nameValueBlock);
if ((flags & SpdyConnection.FLAG_FIN) != 0) {
replyStream.receiveFin();
}
}
@@ -470,11 +488,7 @@ public final class SpdyConnection implements Closeable {
throws IOException {
SpdyStream replyStream = getStream(streamId);
if (replyStream != null) {
try {
replyStream.receiveHeaders(nameValueBlock);
} catch (ProtocolException e) {
replyStream.closeLater(SpdyStream.RST_PROTOCOL_ERROR);
}
replyStream.receiveHeaders(nameValueBlock);
}
}

View File

@@ -30,7 +30,7 @@ import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
/**
* Read version 2 SPDY frames.
* Read spdy/3 frames.
*/
final class SpdyReader implements Closeable {
static final byte[] DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"

View File

@@ -23,7 +23,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import static java.nio.ByteOrder.BIG_ENDIAN;
import java.util.ArrayList;
@@ -101,6 +100,8 @@ public final class SpdyStream {
SpdyStream(int id, SpdyConnection connection, int flags, int priority, int slot,
List<String> requestHeaders, Settings settings) {
if (connection == null) throw new NullPointerException("connection == null");
if (requestHeaders == null) throw new NullPointerException("requestHeaders == null");
this.id = id;
this.connection = connection;
this.priority = priority;
@@ -124,7 +125,8 @@ public final class SpdyStream {
* Returns true if this stream is open. A stream is open until either:
* <ul>
* <li>A {@code SYN_RESET} frame abnormally terminates the stream.
* <li>Both input and output streams have transmitted all data.
* <li>Both input and output streams have transmitted all data and
* headers.
* </ul>
* Note that the input stream may continue to yield data even after a stream
* reports itself as not open. This is because input data is buffered.
@@ -133,7 +135,7 @@ public final class SpdyStream {
if (rstStatusCode != -1) {
return false;
}
if ((in.finished || in.closed) && (out.finished || out.closed)) {
if ((in.finished || in.closed) && (out.finished || out.closed) && responseHeaders != null) {
return false;
}
return true;
@@ -287,25 +289,39 @@ public final class SpdyStream {
void receiveReply(List<String> strings) throws IOException {
assert (!Thread.holdsLock(SpdyStream.this));
boolean streamInUseError = false;
boolean open = true;
synchronized (this) {
if (!isLocallyInitiated() || responseHeaders != null) {
throw new ProtocolException();
if (isLocallyInitiated() && responseHeaders == null) {
responseHeaders = strings;
open = isOpen();
notifyAll();
} else {
streamInUseError = true;
}
responseHeaders = strings;
notifyAll();
}
if (streamInUseError) {
closeLater(SpdyStream.RST_STREAM_IN_USE);
} else if (!open) {
connection.removeStream(id);
}
}
void receiveHeaders(List<String> headers) throws IOException {
assert (!Thread.holdsLock(SpdyStream.this));
boolean protocolError = false;
synchronized (this) {
if (responseHeaders == null) {
throw new ProtocolException();
if (responseHeaders != null) {
List<String> newHeaders = new ArrayList<String>();
newHeaders.addAll(responseHeaders);
newHeaders.addAll(headers);
this.responseHeaders = newHeaders;
} else {
protocolError = true;
}
List<String> newHeaders = new ArrayList<String>();
newHeaders.addAll(responseHeaders);
newHeaders.addAll(headers);
this.responseHeaders = newHeaders;
}
if (protocolError) {
closeLater(SpdyStream.RST_PROTOCOL_ERROR);
}
}
@@ -513,14 +529,20 @@ public final class SpdyStream {
int limit;
int firstNewByte;
boolean finished;
boolean flowControlError;
synchronized (SpdyStream.this) {
finished = this.finished;
pos = this.pos;
firstNewByte = this.limit;
limit = this.limit;
if (byteCount > buffer.length - available()) {
throw new ProtocolException();
}
flowControlError = byteCount > buffer.length - available();
}
// If the peer sends more data than we can handle, discard it and close the connection.
if (flowControlError) {
Util.skipByReading(in, byteCount);
closeLater(SpdyStream.RST_FLOW_CONTROL_ERROR);
return;
}
// Discard data received after the stream is finished. It's probably a benign race.

View File

@@ -27,7 +27,7 @@ import java.util.List;
import java.util.zip.Deflater;
/**
* Write version 2 SPDY frames.
* Write spdy/3 frames.
*/
final class SpdyWriter implements Closeable {
final DataOutputStream out;

View File

@@ -1,34 +0,0 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.okhttp.internal.spdy;
import java.util.concurrent.ThreadFactory;
final class Threads {
public static ThreadFactory newThreadFactory(final String name, final boolean daemon) {
return new ThreadFactory() {
@Override public Thread newThread(Runnable r) {
Thread result = new Thread(r, name);
result.setDaemon(daemon);
return result;
}
};
}
private Threads() {
}
}

View File

@@ -43,7 +43,7 @@ public final class MockSpdyPeer implements Closeable {
private final BlockingQueue<InFrame> inFrames = new LinkedBlockingQueue<InFrame>();
private int port;
private final Executor executor = Executors.newCachedThreadPool(
Threads.newThreadFactory("MockSpdyPeer", true));
Util.newThreadFactory("MockSpdyPeer", true));
private ServerSocket serverSocket;
private Socket socket;
@@ -52,8 +52,17 @@ public final class MockSpdyPeer implements Closeable {
}
public SpdyWriter sendFrame() {
OutFrame frame = new OutFrame(frameCount++, bytesOut.size());
outFrames.add(frame);
outFrames.add(new OutFrame(frameCount++, bytesOut.size(), Integer.MAX_VALUE));
return spdyWriter;
}
/**
* Sends a frame, truncated to {@code truncateToLength} bytes. This is only
* useful for testing error handling as the truncated frame will be
* malformed.
*/
public SpdyWriter sendTruncatedFrame(int truncateToLength) {
outFrames.add(new OutFrame(frameCount++, bytesOut.size(), truncateToLength));
return spdyWriter;
}
@@ -99,6 +108,7 @@ public final class MockSpdyPeer implements Closeable {
if (nextOutFrame != null && nextOutFrame.sequence == i) {
int start = nextOutFrame.start;
int truncateToLength = nextOutFrame.truncateToLength;
int end;
if (outFramesIterator.hasNext()) {
nextOutFrame = outFramesIterator.next();
@@ -108,7 +118,8 @@ public final class MockSpdyPeer implements Closeable {
}
// write a frame
out.write(outBytes, start, end - start);
int length = Math.min(end - start, truncateToLength);
out.write(outBytes, start, length);
} else {
// read a frame
@@ -117,6 +128,7 @@ public final class MockSpdyPeer implements Closeable {
inFrames.add(inFrame);
}
}
Util.closeQuietly(socket);
}
public Socket openSocket() throws IOException {
@@ -139,10 +151,12 @@ public final class MockSpdyPeer implements Closeable {
private static class OutFrame {
private final int sequence;
private final int start;
private final int truncateToLength;
private OutFrame(int sequence, int start) {
private OutFrame(int sequence, int start, int truncateToLength) {
this.sequence = sequence;
this.start = start;
this.truncateToLength = truncateToLength;
}
}

View File

@@ -16,6 +16,7 @@
package com.squareup.okhttp.internal.spdy;
import com.squareup.okhttp.internal.Util;
import static com.squareup.okhttp.internal.Util.UTF_8;
import static com.squareup.okhttp.internal.spdy.Settings.PERSIST_VALUE;
import static com.squareup.okhttp.internal.spdy.SpdyConnection.FLAG_FIN;
@@ -65,10 +66,10 @@ public final class SpdyConnectionTest {
@Test public void clientCreatesStreamAndServerReplies() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.sendFrame().data(SpdyConnection.FLAG_FIN, 1, "robot".getBytes("UTF-8"));
peer.acceptFrame();
peer.acceptFrame(); // DATA
peer.play();
// play it back
@@ -90,19 +91,21 @@ public final class SpdyConnectionTest {
assertTrue(Arrays.equals("c3po".getBytes("UTF-8"), requestData.data));
}
@Test public void headersOnlyStreamIsClosedImmediately() throws Exception {
peer.acceptFrame(); // SYN STREAM
@Test public void headersOnlyStreamIsClosedAfterReplyHeaders() throws Exception {
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("b", "banana"));
peer.play();
SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build();
connection.newStream(Arrays.asList("a", "android"), false, false);
SpdyStream stream = connection.newStream(Arrays.asList("a", "android"), false, false);
assertEquals(1, connection.openStreamCount());
assertEquals(Arrays.asList("b", "banana"), stream.getResponseHeaders());
assertEquals(0, connection.openStreamCount());
}
@Test public void clientCreatesStreamAndServerRepliesWithFin() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.acceptFrame(); // PING
peer.sendFrame().synReply(FLAG_FIN, 1, Arrays.asList("a", "android"));
peer.sendFrame().ping(0, 1);
@@ -125,7 +128,7 @@ public final class SpdyConnectionTest {
@Test public void serverCreatesStreamAndClientReplies() throws Exception {
// write the mocking script
peer.sendFrame().synStream(0, 2, 0, 5, 129, Arrays.asList("a", "android"));
peer.acceptFrame();
peer.acceptFrame(); // SYN_REPLY
peer.play();
// play it back
@@ -157,7 +160,7 @@ public final class SpdyConnectionTest {
@Test public void replyWithNoData() throws Exception {
// write the mocking script
peer.sendFrame().synStream(0, 2, 0, 0, 0, Arrays.asList("a", "android"));
peer.acceptFrame();
peer.acceptFrame(); // SYN_REPLY
peer.play();
// play it back
@@ -182,7 +185,7 @@ public final class SpdyConnectionTest {
@Test public void noop() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // NOOP
peer.play();
// play it back
@@ -200,7 +203,7 @@ public final class SpdyConnectionTest {
@Test public void serverPingsClient() throws Exception {
// write the mocking script
peer.sendFrame().ping(0, 2);
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -217,7 +220,7 @@ public final class SpdyConnectionTest {
@Test public void clientPingsServer() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 1);
peer.play();
@@ -239,10 +242,10 @@ public final class SpdyConnectionTest {
@Test public void unexpectedPingIsNotReturned() throws Exception {
// write the mocking script
peer.sendFrame().ping(0, 2);
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 3); // This ping will not be returned.
peer.sendFrame().ping(0, 4);
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -263,7 +266,7 @@ public final class SpdyConnectionTest {
settings.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 10);
peer.sendFrame().settings(Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS, settings);
peer.sendFrame().ping(0, 2);
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -360,10 +363,11 @@ public final class SpdyConnectionTest {
@Test public void clientClosesClientOutputStream() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("b", "banana"));
peer.acceptFrame(); // TYPE_DATA
peer.acceptFrame(); // TYPE_DATA with FLAG_FIN
peer.sendFrame().ping(0, 2);
peer.acceptFrame(); // PING response
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 1);
peer.play();
// play it back
@@ -382,6 +386,7 @@ public final class SpdyConnectionTest {
} catch (Exception expected) {
assertEquals("stream closed", expected.getMessage());
}
connection.ping().roundTripTime(); // Ensure that the SYN_REPLY has been received.
assertEquals(0, connection.openStreamCount());
// verify the peer received what was expected
@@ -397,7 +402,7 @@ public final class SpdyConnectionTest {
assertEquals(FLAG_FIN, fin.flags);
MockSpdyPeer.InFrame ping = peer.takeFrame();
assertEquals(TYPE_PING, ping.type);
assertEquals(2, ping.streamId);
assertEquals(1, ping.streamId);
}
@Test public void serverClosesClientOutputStream() throws Exception {
@@ -406,6 +411,7 @@ public final class SpdyConnectionTest {
peer.sendFrame().rstStream(1, SpdyStream.RST_CANCEL);
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 1);
peer.acceptFrame(); // DATA
peer.play();
// play it back
@@ -431,6 +437,10 @@ public final class SpdyConnectionTest {
MockSpdyPeer.InFrame ping = peer.takeFrame();
assertEquals(TYPE_PING, ping.type);
assertEquals(1, ping.streamId);
MockSpdyPeer.InFrame data = peer.takeFrame();
assertEquals(TYPE_DATA, data.type);
assertEquals(1, data.streamId);
assertEquals(FLAG_FIN, data.flags);
}
/**
@@ -523,6 +533,7 @@ public final class SpdyConnectionTest {
@Test public void serverClosesClientInputStream() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("b", "banana"));
peer.sendFrame().data(FLAG_FIN, 1, "square".getBytes(UTF_8));
peer.play();
@@ -543,12 +554,12 @@ public final class SpdyConnectionTest {
@Test public void remoteDoubleSynReply() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.acceptFrame(); // PING
peer.sendFrame().synReply(0, 1, Arrays.asList("b", "banana"));
peer.sendFrame().ping(0, 1);
peer.acceptFrame(); // RST STREAM
peer.acceptFrame(); // RST_STREAM
peer.play();
// play it back
@@ -559,8 +570,8 @@ public final class SpdyConnectionTest {
try {
stream.getInputStream().read();
fail();
} catch (IOException e) {
assertEquals("stream was reset: STREAM_IN_USE", e.getMessage());
} catch (IOException expected) {
assertEquals("stream was reset: STREAM_IN_USE", expected.getMessage());
}
// verify the peer received what was expected
@@ -578,9 +589,9 @@ public final class SpdyConnectionTest {
@Test public void remoteDoubleSynStream() throws Exception {
// write the mocking script
peer.sendFrame().synStream(0, 2, 0, 0, 0, Arrays.asList("a", "android"));
peer.acceptFrame();
peer.acceptFrame(); // SYN_REPLY
peer.sendFrame().synStream(0, 2, 0, 0, 0, Arrays.asList("b", "banana"));
peer.acceptFrame();
peer.acceptFrame(); // RST_STREAM
peer.play();
// play it back
@@ -610,12 +621,12 @@ public final class SpdyConnectionTest {
@Test public void remoteSendsDataAfterInFinished() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.sendFrame().data(SpdyConnection.FLAG_FIN, 1, "robot".getBytes("UTF-8"));
peer.sendFrame().data(SpdyConnection.FLAG_FIN, 1, "c3po".getBytes("UTF-8")); // Ignored.
peer.sendFrame().ping(0, 2); // Ping just to make sure the stream was fastforwarded.
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -635,12 +646,12 @@ public final class SpdyConnectionTest {
@Test public void remoteSendsTooMuchData() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("b", "banana"));
peer.sendFrame().data(0, 1, new byte[64 * 1024 + 1]);
peer.acceptFrame();
peer.acceptFrame(); // RST_STREAM
peer.sendFrame().ping(0, 2); // Ping just to make sure the stream was fastforwarded.
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -663,10 +674,10 @@ public final class SpdyConnectionTest {
@Test public void remoteSendsRefusedStreamBeforeReplyHeaders() throws Exception {
// write the mocking script
peer.acceptFrame();
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().rstStream(1, RST_REFUSED_STREAM);
peer.sendFrame().ping(0, 2);
peer.acceptFrame();
peer.acceptFrame(); // PING
peer.play();
// play it back
@@ -691,8 +702,8 @@ public final class SpdyConnectionTest {
@Test public void receiveGoAway() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM 1
peer.acceptFrame(); // SYN STREAM 3
peer.acceptFrame(); // SYN_STREAM 1
peer.acceptFrame(); // SYN_STREAM 3
peer.sendFrame().goAway(0, 1, GOAWAY_PROTOCOL_ERROR);
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 1);
@@ -736,7 +747,7 @@ public final class SpdyConnectionTest {
@Test public void sendGoAway() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM 1
peer.acceptFrame(); // SYN_STREAM 1
peer.acceptFrame(); // GOAWAY
peer.acceptFrame(); // PING
peer.sendFrame().synStream(0, 2, 0, 0, 0, Arrays.asList("b", "b")); // Should be ignored!
@@ -748,8 +759,8 @@ public final class SpdyConnectionTest {
connection.newStream(Arrays.asList("a", "android"), true, true);
Ping ping = connection.ping();
connection.shutdown(GOAWAY_PROTOCOL_ERROR);
ping.roundTripTime(); // Ensure that the SYN STREAM has been received.
assertEquals(1, connection.openStreamCount());
ping.roundTripTime(); // Prevent the peer from exiting prematurely.
// verify the peer received what was expected
MockSpdyPeer.InFrame synStream1 = peer.takeFrame();
@@ -785,9 +796,9 @@ public final class SpdyConnectionTest {
@Test public void close() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.acceptFrame(); // GOAWAY
peer.acceptFrame(); // RST STREAM
peer.acceptFrame(); // RST_STREAM
peer.play();
// play it back
@@ -840,8 +851,10 @@ public final class SpdyConnectionTest {
@Test public void readTimeoutExpires() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.acceptFrame(); // PING
peer.sendFrame().ping(0, 1);
peer.play();
// play it back
@@ -858,6 +871,7 @@ public final class SpdyConnectionTest {
long elapsedNanos = System.nanoTime() - startNanos;
assertEquals(1000d, TimeUnit.NANOSECONDS.toMillis(elapsedNanos), 200d /* 200ms delta */);
assertEquals(1, connection.openStreamCount());
connection.ping().roundTripTime(); // Prevent the peer from exiting prematurely.
// verify the peer received what was expected
MockSpdyPeer.InFrame synStream = peer.takeFrame();
@@ -866,7 +880,7 @@ public final class SpdyConnectionTest {
@Test public void headers() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.acceptFrame(); // PING
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.sendFrame().headers(0, 1, Arrays.asList("c", "c3po"));
@@ -888,10 +902,10 @@ public final class SpdyConnectionTest {
@Test public void headersBeforeReply() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.acceptFrame(); // PING
peer.sendFrame().headers(0, 1, Arrays.asList("c", "c3po"));
peer.acceptFrame(); // RST STREAM
peer.acceptFrame(); // RST_STREAM
peer.sendFrame().ping(0, 1);
peer.play();
@@ -902,8 +916,8 @@ public final class SpdyConnectionTest {
try {
stream.getResponseHeaders();
fail();
} catch (IOException e) {
assertEquals("stream was reset: PROTOCOL_ERROR", e.getMessage());
} catch (IOException expected) {
assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage());
}
// verify the peer received what was expected
@@ -918,7 +932,7 @@ public final class SpdyConnectionTest {
@Test public void readSendsWindowUpdate() throws Exception {
// Write the mocking script.
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
for (int i = 0; i < 3; i++) {
peer.sendFrame().data(0, 1, new byte[WINDOW_UPDATE_THRESHOLD]);
@@ -954,7 +968,7 @@ public final class SpdyConnectionTest {
@Test public void writeAwaitsWindowUpdate() throws Exception {
// Write the mocking script. This accepts more data frames than necessary!
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // SYN_STREAM
for (int i = 0; i < Settings.DEFAULT_INITIAL_WINDOW_SIZE / 1024; i++) {
peer.acceptFrame(); // DATA
}
@@ -980,6 +994,26 @@ public final class SpdyConnectionTest {
assertEquals(TYPE_DATA, data.type);
}
@Test public void testTruncatedDataFrame() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN_STREAM
peer.sendFrame().synReply(0, 1, Arrays.asList("a", "android"));
peer.sendTruncatedFrame(8 + 100).data(0, 1, new byte[1024]);
peer.play();
// play it back
SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build();
SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true);
assertEquals(Arrays.asList("a", "android"), stream.getResponseHeaders());
InputStream in = stream.getInputStream();
try {
Util.readFully(in, new byte[101]);
fail();
} catch (IOException expected) {
assertEquals("stream was reset: PROTOCOL_ERROR", expected.getMessage());
}
}
private void writeAndClose(SpdyStream stream, String data) throws IOException {
OutputStream out = stream.getOutputStream();
out.write(data.getBytes("UTF-8"));