mirror of
https://github.com/square/okhttp.git
synced 2026-01-27 04:22:07 +03:00
Support noop frames. Make ping sending a blocking (synchronous) call.
Although blocking calls don't fit the SPDY model very well, they make exception transparency easy. In this case, a NOOP or a PING will throw if it cannot be sent. This is good.
This commit is contained in:
@@ -204,13 +204,16 @@ public final class SpdyConnection implements Closeable {
|
||||
* Sends a ping frame to the peer. Use the returned object to await the
|
||||
* ping's response and observe its round trip time.
|
||||
*/
|
||||
public synchronized Ping ping() {
|
||||
public Ping ping() throws IOException {
|
||||
Ping ping = new Ping();
|
||||
int pingId = nextPingId;
|
||||
nextPingId += 2;
|
||||
if (pings == null) pings = new HashMap<Integer, Ping>();
|
||||
pings.put(pingId, ping);
|
||||
writePingLater(pingId, ping);
|
||||
int pingId;
|
||||
synchronized (this) {
|
||||
pingId = nextPingId;
|
||||
nextPingId += 2;
|
||||
if (pings == null) pings = new HashMap<Integer, Ping>();
|
||||
pings.put(pingId, ping);
|
||||
}
|
||||
writePing(pingId, ping);
|
||||
return ping;
|
||||
}
|
||||
|
||||
@@ -226,10 +229,9 @@ public final class SpdyConnection implements Closeable {
|
||||
}
|
||||
|
||||
private void writePing(int id, Ping ping) throws IOException {
|
||||
// Observe the sent time immediately before performing I/O.
|
||||
if (ping != null) ping.send();
|
||||
|
||||
synchronized (spdyWriter) {
|
||||
// Observe the sent time immediately before performing I/O.
|
||||
if (ping != null) ping.send();
|
||||
spdyWriter.ping(0, id);
|
||||
}
|
||||
}
|
||||
@@ -238,6 +240,15 @@ public final class SpdyConnection implements Closeable {
|
||||
return pings != null ? pings.remove(id) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a noop frame to the peer.
|
||||
*/
|
||||
public void noop() throws IOException {
|
||||
synchronized (spdyWriter) {
|
||||
spdyWriter.noop();
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
synchronized (spdyWriter) {
|
||||
spdyWriter.out.flush();
|
||||
@@ -365,7 +376,7 @@ public final class SpdyConnection implements Closeable {
|
||||
return true;
|
||||
|
||||
case SpdyConnection.TYPE_NOOP:
|
||||
throw new UnsupportedOperationException();
|
||||
return true;
|
||||
|
||||
case SpdyConnection.TYPE_PING:
|
||||
int id = spdyReader.id;
|
||||
|
||||
@@ -115,6 +115,10 @@ final class SpdyReader {
|
||||
readSettings();
|
||||
return SpdyConnection.TYPE_SETTINGS;
|
||||
|
||||
case SpdyConnection.TYPE_NOOP:
|
||||
if (length != 0) throw ioException("TYPE_NOOP length: %d != 0", length);
|
||||
return SpdyConnection.TYPE_NOOP;
|
||||
|
||||
case SpdyConnection.TYPE_PING:
|
||||
readPing();
|
||||
return SpdyConnection.TYPE_PING;
|
||||
@@ -226,14 +230,14 @@ final class SpdyReader {
|
||||
}
|
||||
|
||||
private void readPing() throws IOException {
|
||||
if (length != 4) throw ioException("TYPE_PING length: %d != 4", length);
|
||||
id = in.readInt();
|
||||
}
|
||||
|
||||
private void readSettings() throws IOException {
|
||||
int numberOfEntries = in.readInt();
|
||||
if (length != 4 + 8 * numberOfEntries) {
|
||||
throw new IOException("TYPE_SETTINGS frame length is inconsistent: "
|
||||
+ length + " != 4 + 8 * " + numberOfEntries);
|
||||
throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries);
|
||||
}
|
||||
settings = new Settings();
|
||||
for (int i = 0; i < numberOfEntries; i++) {
|
||||
@@ -247,4 +251,8 @@ final class SpdyReader {
|
||||
settings.set(id, idFlags, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static IOException ioException(String message, Object... args) throws IOException {
|
||||
throw new IOException(String.format(message, args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,6 +122,15 @@ final class SpdyWriter {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void noop() throws IOException {
|
||||
int type = SpdyConnection.TYPE_NOOP;
|
||||
int length = 0;
|
||||
int flags = 0;
|
||||
out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
|
||||
out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
public void ping(int flags, int id) throws IOException {
|
||||
int type = SpdyConnection.TYPE_PING;
|
||||
int length = 4;
|
||||
|
||||
@@ -27,6 +27,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import static libcore.net.spdy.Settings.PERSIST_VALUE;
|
||||
import static libcore.net.spdy.SpdyConnection.FLAG_FIN;
|
||||
import static libcore.net.spdy.SpdyConnection.TYPE_NOOP;
|
||||
import static libcore.net.spdy.SpdyConnection.TYPE_PING;
|
||||
import static libcore.net.spdy.SpdyConnection.TYPE_RST_STREAM;
|
||||
import static libcore.net.spdy.SpdyConnection.TYPE_SYN_REPLY;
|
||||
@@ -125,6 +126,24 @@ public final class SpdyConnectionTest extends TestCase {
|
||||
assertEquals(1, receiveCount.get());
|
||||
}
|
||||
|
||||
public void testNoop() throws Exception {
|
||||
// write the mocking script
|
||||
peer.acceptFrame();
|
||||
peer.play();
|
||||
|
||||
// play it back
|
||||
SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket())
|
||||
.handler(REJECT_INCOMING_STREAMS)
|
||||
.build();
|
||||
connection.noop();
|
||||
|
||||
// verify the peer received what was expected
|
||||
MockSpdyPeer.InFrame ping = peer.takeFrame();
|
||||
assertEquals(TYPE_NOOP, ping.reader.type);
|
||||
assertEquals(0, ping.reader.flags);
|
||||
assertEquals(0, ping.reader.length);
|
||||
}
|
||||
|
||||
public void testServerPingsClient() throws Exception {
|
||||
// write the mocking script
|
||||
peer.sendFrame().ping(0, 2);
|
||||
@@ -187,10 +206,9 @@ public final class SpdyConnectionTest extends TestCase {
|
||||
|
||||
public void testServerSendsSettingsToClient() throws Exception {
|
||||
// write the mocking script
|
||||
SpdyWriter newStream = peer.sendFrame();
|
||||
Settings settings = new Settings();
|
||||
settings.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 10);
|
||||
newStream.settings(Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS, settings);
|
||||
peer.sendFrame().settings(Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS, settings);
|
||||
peer.sendFrame().ping(0, 2);
|
||||
peer.acceptFrame();
|
||||
peer.play();
|
||||
@@ -208,17 +226,16 @@ public final class SpdyConnectionTest extends TestCase {
|
||||
|
||||
public void testMultipleSettingsFramesAreMerged() throws Exception {
|
||||
// write the mocking script
|
||||
SpdyWriter newStream = peer.sendFrame();
|
||||
Settings settings1 = new Settings();
|
||||
settings1.set(Settings.UPLOAD_BANDWIDTH, PERSIST_VALUE, 100);
|
||||
settings1.set(Settings.DOWNLOAD_BANDWIDTH, PERSIST_VALUE, 200);
|
||||
settings1.set(Settings.DOWNLOAD_RETRANS_RATE, 0, 300);
|
||||
newStream.settings(0, settings1);
|
||||
peer.sendFrame().settings(0, settings1);
|
||||
Settings settings2 = new Settings();
|
||||
settings2.set(Settings.DOWNLOAD_BANDWIDTH, 0, 400);
|
||||
settings2.set(Settings.DOWNLOAD_RETRANS_RATE, PERSIST_VALUE, 500);
|
||||
settings2.set(Settings.MAX_CONCURRENT_STREAMS, PERSIST_VALUE, 600);
|
||||
newStream.settings(0, settings2);
|
||||
peer.sendFrame().settings(0, settings2);
|
||||
peer.sendFrame().ping(0, 2);
|
||||
peer.acceptFrame();
|
||||
peer.play();
|
||||
|
||||
Reference in New Issue
Block a user