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

Merge pull request #77 from square/jwilson/headers

Support the HEADERS frame.
This commit is contained in:
Jake Wharton
2013-01-01 19:27:22 -08:00
6 changed files with 112 additions and 3 deletions

View File

@@ -427,6 +427,18 @@ public final class SpdyConnection implements Closeable {
}
}
@Override public void headers(int flags, int streamId, List<String> nameValueBlock)
throws IOException {
SpdyStream replyStream = getStream(streamId);
if (replyStream != null) {
try {
replyStream.receiveHeaders(nameValueBlock);
} catch (ProtocolException e) {
replyStream.closeLater(SpdyStream.RST_PROTOCOL_ERROR);
}
}
}
@Override public void rstStream(int flags, int streamId, int statusCode) {
SpdyStream rstStream = removeStream(streamId);
if (rstStream != null) {

View File

@@ -118,8 +118,8 @@ final class SpdyReader implements Closeable {
return true;
case SpdyConnection.TYPE_HEADERS:
Streams.skipByReading(in, length);
throw new UnsupportedOperationException("TODO");
readHeaders(handler, flags, length);
return true;
default:
throw new IOException("Unexpected frame");
@@ -158,6 +158,14 @@ final class SpdyReader implements Closeable {
handler.rstStream(flags, streamId, statusCode);
}
private void readHeaders(Handler handler, int flags, int length) throws IOException {
int w1 = in.readInt();
in.readShort(); // unused
int streamId = w1 & 0x7fffffff;
List<String> nameValueBlock = readNameValueBlock(length - 6);
handler.headers(flags, streamId, nameValueBlock);
}
private DataInputStream newNameValueBlockStream() {
// Limit the inflater input stream to only those bytes in the Name/Value block.
final InputStream throttleStream = new InputStream() {
@@ -269,11 +277,11 @@ final class SpdyReader implements Closeable {
void synStream(int flags, int streamId, int associatedStreamId, int priority,
List<String> nameValueBlock);
void synReply(int flags, int streamId, List<String> nameValueBlock) throws IOException;
void headers(int flags, int streamId, List<String> nameValueBlock) throws IOException;
void rstStream(int flags, int streamId, int statusCode);
void settings(int flags, Settings settings);
void noop();
void ping(int flags, int streamId);
void goAway(int flags, int lastGoodStreamId);
// TODO: headers
}
}

View File

@@ -26,6 +26,7 @@ import java.io.OutputStream;
import java.net.ProtocolException;
import java.net.SocketTimeoutException;
import static java.nio.ByteOrder.BIG_ENDIAN;
import java.util.ArrayList;
import java.util.List;
/**
@@ -271,6 +272,19 @@ public final class SpdyStream {
}
}
void receiveHeaders(List<String> headers) throws IOException {
assert (!Thread.holdsLock(SpdyStream.this));
synchronized (this) {
if (responseHeaders == null) {
throw new ProtocolException();
}
List<String> newHeaders = new ArrayList<String>();
newHeaders.addAll(responseHeaders);
newHeaders.addAll(headers);
this.responseHeaders = newHeaders;
}
}
void receiveData(InputStream in, int length) throws IOException {
assert (!Thread.holdsLock(SpdyStream.this));
this.in.receive(in, length);

View File

@@ -75,6 +75,21 @@ final class SpdyWriter implements Closeable {
out.flush();
}
public synchronized void headers(
int flags, int streamId, List<String> nameValueBlock) throws IOException {
writeNameValueBlockToBuffer(nameValueBlock);
int type = SpdyConnection.TYPE_HEADERS;
int length = nameValueBlockBuffer.size() + 6;
int unused = 0;
out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
out.writeInt(streamId & 0x7fffffff);
out.writeShort(unused);
nameValueBlockBuffer.writeTo(out);
out.flush();
}
public synchronized void synReset(int streamId, int statusCode) throws IOException {
int flags = 0;
int type = SpdyConnection.TYPE_RST_STREAM;

View File

@@ -172,6 +172,14 @@ public final class MockSpdyPeer {
this.nameValueBlock = nameValueBlock;
}
@Override public void headers(int flags, int streamId, List<String> nameValueBlock) {
if (this.type != -1) throw new IllegalStateException();
this.type = SpdyConnection.TYPE_HEADERS;
this.streamId = streamId;
this.flags = flags;
this.nameValueBlock = nameValueBlock;
}
@Override public void data(int flags, int streamId, InputStream in, int length)
throws IOException {
if (this.type != -1) throw new IllegalStateException();

View File

@@ -849,6 +849,58 @@ public final class SpdyConnectionTest {
assertEquals(TYPE_SYN_STREAM, synStream.type);
}
@Test public void headers() throws Exception {
// write the mocking script
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"));
peer.sendFrame().ping(0, 1);
peer.play();
// play it back
SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build();
SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true);
connection.ping().roundTripTime(); // Ensure that the HEADERS has been received.
assertEquals(Arrays.asList("a", "android", "c", "c3po"), stream.getResponseHeaders());
// verify the peer received what was expected
MockSpdyPeer.InFrame synStream = peer.takeFrame();
assertEquals(TYPE_SYN_STREAM, synStream.type);
MockSpdyPeer.InFrame ping = peer.takeFrame();
assertEquals(TYPE_PING, ping.type);
}
@Test public void headersBeforeReply() throws Exception {
// write the mocking script
peer.acceptFrame(); // SYN STREAM
peer.acceptFrame(); // PING
peer.sendFrame().headers(0, 1, Arrays.asList("c", "c3po"));
peer.acceptFrame(); // RST STREAM
peer.sendFrame().ping(0, 1);
peer.play();
// play it back
SpdyConnection connection = new SpdyConnection.Builder(true, peer.openSocket()).build();
SpdyStream stream = connection.newStream(Arrays.asList("b", "banana"), true, true);
connection.ping().roundTripTime(); // Ensure that the HEADERS has been received.
try {
stream.getResponseHeaders();
fail();
} catch (IOException e) {
assertEquals("stream was reset: PROTOCOL_ERROR", e.getMessage());
}
// verify the peer received what was expected
MockSpdyPeer.InFrame synStream = peer.takeFrame();
assertEquals(TYPE_SYN_STREAM, synStream.type);
MockSpdyPeer.InFrame ping = peer.takeFrame();
assertEquals(TYPE_PING, ping.type);
MockSpdyPeer.InFrame rstStream = peer.takeFrame();
assertEquals(TYPE_RST_STREAM, rstStream.type);
assertEquals(RST_PROTOCOL_ERROR, rstStream.statusCode);
}
private void writeAndClose(SpdyStream stream, String data) throws IOException {
OutputStream out = stream.getOutputStream();
out.write(data.getBytes("UTF-8"));