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