1
0
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:
Jesse Wilson
2012-09-29 12:19:13 -04:00
parent 8fe93f33dc
commit d2fc60ad18
4 changed files with 62 additions and 17 deletions

View File

@@ -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;

View File

@@ -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));
}
}

View File

@@ -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;

View File

@@ -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();