diff --git a/src/main/java/libcore/net/spdy/SpdyConnection.java b/src/main/java/libcore/net/spdy/SpdyConnection.java index 565062b6b..8ca79e50f 100644 --- a/src/main/java/libcore/net/spdy/SpdyConnection.java +++ b/src/main/java/libcore/net/spdy/SpdyConnection.java @@ -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(); - pings.put(pingId, ping); - writePingLater(pingId, ping); + int pingId; + synchronized (this) { + pingId = nextPingId; + nextPingId += 2; + if (pings == null) pings = new HashMap(); + 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; diff --git a/src/main/java/libcore/net/spdy/SpdyReader.java b/src/main/java/libcore/net/spdy/SpdyReader.java index d241abf90..fa64b4680 100644 --- a/src/main/java/libcore/net/spdy/SpdyReader.java +++ b/src/main/java/libcore/net/spdy/SpdyReader.java @@ -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)); + } } diff --git a/src/main/java/libcore/net/spdy/SpdyWriter.java b/src/main/java/libcore/net/spdy/SpdyWriter.java index 134d2b037..b62011cec 100644 --- a/src/main/java/libcore/net/spdy/SpdyWriter.java +++ b/src/main/java/libcore/net/spdy/SpdyWriter.java @@ -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; diff --git a/src/test/java/libcore/net/spdy/SpdyConnectionTest.java b/src/test/java/libcore/net/spdy/SpdyConnectionTest.java index 229d02c99..d51e8c476 100644 --- a/src/test/java/libcore/net/spdy/SpdyConnectionTest.java +++ b/src/test/java/libcore/net/spdy/SpdyConnectionTest.java @@ -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();