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:
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user