1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-18 20:40:58 +03:00

Update to HTTP/2 Draft 10 and HPACK Draft 6.

This commit is contained in:
Adrian Cole
2014-04-15 11:47:04 -04:00
committed by Square
parent 2dd62d46cb
commit b03026067b
20 changed files with 1382 additions and 791 deletions

View File

@@ -24,9 +24,9 @@ import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class HttpOverHttp20Draft09Test extends HttpOverSpdyTest {
public class HttpOverHttp20Draft10Test extends HttpOverSpdyTest {
public HttpOverHttp20Draft09Test() {
public HttpOverHttp20Draft10Test() {
super(Protocol.HTTP_2);
this.hostHeader = ":authority";
}

View File

@@ -27,24 +27,25 @@ import static com.squareup.okhttp.internal.Util.headerEntries;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class HpackDraft05Test {
public class HpackDraft06Test {
private final Buffer bytesIn = new Buffer();
private HpackDraft05.Reader hpackReader;
private HpackDraft06.Reader hpackReader;
private Buffer bytesOut = new Buffer();
private HpackDraft05.Writer hpackWriter;
private HpackDraft06.Writer hpackWriter;
@Before public void reset() {
hpackReader = newReader(bytesIn);
hpackWriter = new HpackDraft05.Writer(bytesOut);
hpackWriter = new HpackDraft06.Writer(bytesOut);
}
/**
* Variable-length quantity special cases strings which are longer than 127
* bytes. Values such as cookies can be 4KiB, and should be possible to send.
*
* <p> http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#section-4.1.2
* <p> http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#section-4.1.2
*/
@Test public void largeHeaderValue() throws IOException {
char[] value = new char[4096];
@@ -185,7 +186,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.1
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.1.1
*/
@Test public void readLiteralHeaderFieldWithIndexing() throws IOException {
Buffer out = new Buffer();
@@ -239,7 +240,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.2
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.1.2
*/
@Test public void literalHeaderFieldWithoutIndexingIndexedName() throws IOException {
List<Header> headerBlock = headerEntries(":path", "/sample/path");
@@ -263,7 +264,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.3
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.1.3
*/
@Test public void readIndexedHeaderField() throws IOException {
bytesIn.writeByte(0x82); // == Indexed - Add ==
@@ -282,8 +283,69 @@ public class HpackDraft05Test {
assertEquals(headerEntries(":method", "GET"), hpackReader.getAndReset());
}
// Example taken from twitter/hpack DecoderTest.testIllegalIndex
@Test public void readIndexedHeaderFieldTooLargeIndex() throws IOException {
bytesIn.writeByte(0xff); // == Indexed - Add ==
bytesIn.writeByte(0x00); // idx = 127
try {
hpackReader.readHeaders();
fail();
} catch (IOException e) {
assertEquals("Header index too large 127", e.getMessage());
}
}
// Example taken from twitter/hpack DecoderTest.testInsidiousIndex
@Test public void readIndexedHeaderFieldInsidiousIndex() throws IOException {
bytesIn.writeByte(0xff); // == Indexed - Add ==
bytesIn.writeByte(0x80); // idx = -2147483521
bytesIn.writeByte(0x80);
bytesIn.writeByte(0x80);
bytesIn.writeByte(0x80);
bytesIn.writeByte(0x08);
try {
hpackReader.readHeaders();
fail();
} catch (IllegalArgumentException e) {
assertEquals("input must be between 0 and 63: -2147483514", e.getMessage());
}
}
// Example taken from twitter/hpack DecoderTest.testIllegalEncodeContextUpdate
@Test public void readHeaderTableStateChangeInvalid() throws IOException {
bytesIn.writeByte(0x80); // header table state change
bytesIn.writeByte(0x81); // should be 0x80 for empty!
try {
hpackReader.readHeaders();
fail();
} catch (IOException e) {
assertEquals("Invalid header table state change -127", e.getMessage());
}
}
// Example taken from twitter/hpack DecoderTest.testInsidiousMaxHeaderSize
@Test public void readHeaderTableStateChangeInsidiousMaxHeaderByteCount() throws IOException {
bytesIn.writeByte(0x80); // header table state change
bytesIn.writeByte(0x7F); // encoded -1879048193
bytesIn.writeByte(0x80);
bytesIn.writeByte(0xFF);
bytesIn.writeByte(0xFF);
bytesIn.writeByte(0xFF);
bytesIn.writeByte(0x08);
try {
hpackReader.readHeaders();
fail();
} catch (IOException e) {
assertEquals("Invalid header table byte count -1879048193", e.getMessage());
}
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#section-3.2.1
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#section-3.2.1
*/
@Test public void toggleIndex() throws IOException {
// Static table entries are copied to the top of the reference set.
@@ -349,7 +411,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.1.4
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.1.4
*/
@Test public void readIndexedHeaderFieldFromStaticTableWithoutBuffering() throws IOException {
bytesIn.writeByte(0x82); // == Indexed - Add ==
@@ -366,7 +428,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.2
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.2
*/
@Test public void readRequestExamplesWithoutHuffman() throws IOException {
Buffer out = firstRequestWithoutHuffman();
@@ -494,6 +556,7 @@ public class HpackDraft05Test {
Buffer out = new Buffer();
out.writeByte(0x80); // == Empty reference set ==
out.writeByte(0x80); // idx = 0, flag = 1
out.writeByte(0x85); // == Indexed - Add ==
// idx = 5 -> :method: GET
out.writeByte(0x8c); // == Indexed - Add ==
@@ -568,7 +631,7 @@ public class HpackDraft05Test {
}
/**
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-E.3
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-D.3
*/
@Test public void readRequestExamplesWithHuffman() throws IOException {
Buffer out = firstRequestWithHuffman();
@@ -705,6 +768,7 @@ public class HpackDraft05Test {
Buffer out = new Buffer();
out.writeByte(0x80); // == Empty reference set ==
out.writeByte(0x80); // idx = 0, flag = 1
out.writeByte(0x85); // == Indexed - Add ==
// idx = 5 -> :method: GET
out.writeByte(0x8c); // == Indexed - Add ==
@@ -848,8 +912,8 @@ public class HpackDraft05Test {
assertEquals(ByteString.EMPTY, newReader(byteStream(0)).readByteString(false));
}
private HpackDraft05.Reader newReader(Buffer source) {
return new HpackDraft05.Reader(false, 4096, source);
private HpackDraft06.Reader newReader(Buffer source) {
return new HpackDraft06.Reader(4096, source);
}
private Buffer byteStream(int... bytes) {

View File

@@ -1,515 +0,0 @@
/*
* Copyright (C) 2013 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.okhttp.internal.spdy;
import com.squareup.okhttp.internal.Util;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import okio.Buffer;
import okio.BufferedSource;
import okio.ByteString;
import org.junit.Test;
import static com.squareup.okhttp.internal.Util.headerEntries;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class Http20Draft09Test {
static final int expectedStreamId = 15;
@Test public void unknownFrameTypeIgnored() throws IOException {
Buffer frame = new Buffer();
frame.writeShort(4); // has a 4-byte field
frame.writeByte(99); // type 99
frame.writeByte(0); // no flags
frame.writeInt(expectedStreamId);
frame.writeInt(111111111); // custom data
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the unknown frame.
fr.nextFrame(new BaseTestHandler());
}
@Test public void onlyOneLiteralHeadersFrame() throws IOException {
final List<Header> sentHeaders = headerEntries("name", "value");
Buffer frame = new Buffer();
// Write the headers frame, specifying no more frames are expected.
{
Buffer headerBytes = literalHeaders(sentHeaders);
frame.writeShort((int) headerBytes.size());
frame.writeByte(Http20Draft09.TYPE_HEADERS);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_END_STREAM);
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.write(headerBytes, headerBytes.size());
}
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the headers frame.
fr.nextFrame(new BaseTestHandler() {
@Override
public void headers(boolean outFinished, boolean inFinished, int streamId,
int associatedStreamId, int priority, List<Header> headerBlock,
HeadersMode headersMode) {
assertFalse(outFinished);
assertTrue(inFinished);
assertEquals(expectedStreamId, streamId);
assertEquals(-1, associatedStreamId);
assertEquals(-1, priority);
assertEquals(sentHeaders, headerBlock);
assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
}
});
}
@Test public void headersWithPriority() throws IOException {
Buffer frame = new Buffer();
final List<Header> sentHeaders = headerEntries("name", "value");
{ // Write the headers frame, specifying priority flag and value.
Buffer headerBytes = literalHeaders(sentHeaders);
frame.writeShort((int) (headerBytes.size() + 4));
frame.writeByte(Http20Draft09.TYPE_HEADERS);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS | Http20Draft09.FLAG_PRIORITY);
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.writeInt(0); // Highest priority is 0.
frame.write(headerBytes, headerBytes.size());
}
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the headers frame.
fr.nextFrame(new BaseTestHandler() {
@Override
public void headers(boolean outFinished, boolean inFinished, int streamId,
int associatedStreamId, int priority, List<Header> nameValueBlock,
HeadersMode headersMode) {
assertFalse(outFinished);
assertFalse(inFinished);
assertEquals(expectedStreamId, streamId);
assertEquals(-1, associatedStreamId);
assertEquals(0, priority);
assertEquals(sentHeaders, nameValueBlock);
assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
}
});
}
/** Headers are compressed, then framed. */
@Test public void headersFrameThenContinuation() throws IOException {
Buffer frame = new Buffer();
// Decoding the first header will cross frame boundaries.
Buffer headerBlock = literalHeaders(headerEntries("foo", "barrr", "baz", "qux"));
{ // Write the first headers frame.
frame.writeShort((int) (headerBlock.size() / 2));
frame.writeByte(Http20Draft09.TYPE_HEADERS);
frame.writeByte(0); // no flags
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.write(headerBlock, headerBlock.size() / 2);
}
{ // Write the continuation frame, specifying no more frames are expected.
frame.writeShort((int) headerBlock.size());
frame.writeByte(Http20Draft09.TYPE_CONTINUATION);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS);
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.write(headerBlock, headerBlock.size());
}
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Reading the above frames should result in a concatenated headerBlock.
fr.nextFrame(new BaseTestHandler() {
@Override
public void headers(boolean outFinished, boolean inFinished, int streamId,
int associatedStreamId, int priority, List<Header> headerBlock,
HeadersMode headersMode) {
assertFalse(outFinished);
assertFalse(inFinished);
assertEquals(expectedStreamId, streamId);
assertEquals(-1, associatedStreamId);
assertEquals(-1, priority);
assertEquals(headerEntries("foo", "barrr", "baz", "qux"), headerBlock);
assertEquals(HeadersMode.HTTP_20_HEADERS, headersMode);
}
});
}
@Test public void pushPromise() throws IOException {
Buffer frame = new Buffer();
final int expectedPromisedStreamId = 11;
final List<Header> pushPromise = Arrays.asList(
new Header(Header.TARGET_METHOD, "GET"),
new Header(Header.TARGET_SCHEME, "https"),
new Header(Header.TARGET_AUTHORITY, "squareup.com"),
new Header(Header.TARGET_PATH, "/")
);
{ // Write the push promise frame, specifying the associated stream ID.
Buffer headerBytes = literalHeaders(pushPromise);
frame.writeShort((int) (headerBytes.size() + 4));
frame.writeByte(Http20Draft09.TYPE_PUSH_PROMISE);
frame.writeByte(Http20Draft09.FLAG_END_PUSH_PROMISE);
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.writeInt(expectedPromisedStreamId & 0x7fffffff);
frame.write(headerBytes, headerBytes.size());
}
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the headers frame.
fr.nextFrame(new BaseTestHandler() {
@Override
public void pushPromise(int streamId, int promisedStreamId, List<Header> headerBlock) {
assertEquals(expectedStreamId, streamId);
assertEquals(expectedPromisedStreamId, promisedStreamId);
assertEquals(pushPromise, headerBlock);
}
});
}
/** Headers are compressed, then framed. */
@Test public void pushPromiseThenContinuation() throws IOException {
Buffer frame = new Buffer();
final int expectedPromisedStreamId = 11;
final List<Header> pushPromise = Arrays.asList(
new Header(Header.TARGET_METHOD, "GET"),
new Header(Header.TARGET_SCHEME, "https"),
new Header(Header.TARGET_AUTHORITY, "squareup.com"),
new Header(Header.TARGET_PATH, "/")
);
// Decoding the first header will cross frame boundaries.
Buffer headerBlock = literalHeaders(pushPromise);
int firstFrameLength = (int) (headerBlock.size() - 1);
{ // Write the first headers frame.
frame.writeShort(firstFrameLength + 4);
frame.writeByte(Http20Draft09.TYPE_PUSH_PROMISE);
frame.writeByte(0); // no flags
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.writeInt(expectedPromisedStreamId & 0x7fffffff);
frame.write(headerBlock, firstFrameLength);
}
{ // Write the continuation frame, specifying no more frames are expected.
frame.writeShort(1);
frame.writeByte(Http20Draft09.TYPE_CONTINUATION);
frame.writeByte(Http20Draft09.FLAG_END_HEADERS);
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.write(headerBlock, 1);
}
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Reading the above frames should result in a concatenated headerBlock.
fr.nextFrame(new BaseTestHandler() {
@Override
public void pushPromise(int streamId, int promisedStreamId, List<Header> headerBlock) {
assertEquals(expectedStreamId, streamId);
assertEquals(expectedPromisedStreamId, promisedStreamId);
assertEquals(pushPromise, headerBlock);
}
});
}
@Test public void readRstStreamFrame() throws IOException {
Buffer frame = new Buffer();
frame.writeShort(4);
frame.writeByte(Http20Draft09.TYPE_RST_STREAM);
frame.writeByte(0); // No flags
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.writeInt(ErrorCode.COMPRESSION_ERROR.httpCode);
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the reset frame.
fr.nextFrame(new BaseTestHandler() {
@Override public void rstStream(int streamId, ErrorCode errorCode) {
assertEquals(expectedStreamId, streamId);
assertEquals(ErrorCode.COMPRESSION_ERROR, errorCode);
}
});
}
@Test public void readSettingsFrame() throws IOException {
Buffer frame = new Buffer();
final int reducedTableSizeBytes = 16;
frame.writeShort(16); // 2 settings * 4 bytes for the code and 4 for the value.
frame.writeByte(Http20Draft09.TYPE_SETTINGS);
frame.writeByte(0); // No flags
frame.writeInt(0 & 0x7fffffff); // Settings are always on the connection stream 0.
frame.writeInt(Settings.HEADER_TABLE_SIZE & 0xffffff);
frame.writeInt(reducedTableSizeBytes);
frame.writeInt(Settings.ENABLE_PUSH & 0xffffff);
frame.writeInt(0);
final Http20Draft09.Reader fr = new Http20Draft09.Reader(frame, 4096, false);
// Consume the settings frame.
fr.nextFrame(new BaseTestHandler() {
@Override public void settings(boolean clearPrevious, Settings settings) {
assertFalse(clearPrevious); // No clearPrevious in http/2.
assertEquals(reducedTableSizeBytes, settings.getHeaderTableSize());
assertEquals(false, settings.getEnablePush(true));
}
});
}
@Test public void pingRoundTrip() throws IOException {
Buffer frame = new Buffer();
final int expectedPayload1 = 7;
final int expectedPayload2 = 8;
// Compose the expected PING frame.
frame.writeShort(8); // length
frame.writeByte(Http20Draft09.TYPE_PING);
frame.writeByte(Http20Draft09.FLAG_ACK);
frame.writeInt(0); // connection-level
frame.writeInt(expectedPayload1);
frame.writeInt(expectedPayload2);
// Check writer sends the same bytes.
assertEquals(frame, sendPingFrame(true, expectedPayload1, expectedPayload2));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
fr.nextFrame(new BaseTestHandler() { // Consume the ping frame.
@Override public void ping(boolean ack, int payload1, int payload2) {
assertTrue(ack);
assertEquals(expectedPayload1, payload1);
assertEquals(expectedPayload2, payload2);
}
});
}
@Test public void maxLengthDataFrame() throws IOException {
Buffer frame = new Buffer();
final byte[] expectedData = new byte[16383];
Arrays.fill(expectedData, (byte) 2);
// Write the data frame.
frame.writeShort(expectedData.length);
frame.writeByte(Http20Draft09.TYPE_DATA);
frame.writeByte(0); // no flags
frame.writeInt(expectedStreamId & 0x7fffffff);
frame.write(expectedData);
// Check writer sends the same bytes.
assertEquals(frame, sendDataFrame(new Buffer().write(expectedData)));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
fr.nextFrame(new BaseTestHandler() {
@Override public void data(
boolean inFinished, int streamId, BufferedSource source, int length) throws IOException {
assertFalse(inFinished);
assertEquals(expectedStreamId, streamId);
assertEquals(16383, length);
ByteString data = source.readByteString(length);
for (byte b : data.toByteArray()){
assertEquals(2, b);
}
}
});
}
@Test public void tooLargeDataFrame() throws IOException {
try {
sendDataFrame(new Buffer().write(new byte[0x1000000]));
fail();
} catch (IllegalArgumentException e) {
assertEquals("FRAME_SIZE_ERROR length > 16383: 16777216", e.getMessage());
}
}
@Test public void windowUpdateRoundTrip() throws IOException {
Buffer frame = new Buffer();
final long expectedWindowSizeIncrement = 0x7fffffff;
// Compose the expected window update frame.
frame.writeShort(4); // length
frame.writeByte(Http20Draft09.TYPE_WINDOW_UPDATE);
frame.writeByte(0); // No flags.
frame.writeInt(expectedStreamId);
frame.writeInt((int) expectedWindowSizeIncrement);
// Check writer sends the same bytes.
assertEquals(frame, windowUpdate(expectedWindowSizeIncrement));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
fr.nextFrame(new BaseTestHandler() { // Consume the window update frame.
@Override public void windowUpdate(int streamId, long windowSizeIncrement) {
assertEquals(expectedStreamId, streamId);
assertEquals(expectedWindowSizeIncrement, windowSizeIncrement);
}
});
}
@Test public void badWindowSizeIncrement() throws IOException {
try {
windowUpdate(0);
fail();
} catch (IllegalArgumentException e) {
assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 0",
e.getMessage());
}
try {
windowUpdate(0x80000000L);
fail();
} catch (IllegalArgumentException e) {
assertEquals("windowSizeIncrement == 0 || windowSizeIncrement > 0x7fffffffL: 2147483648",
e.getMessage());
}
}
@Test public void goAwayWithoutDebugDataRoundTrip() throws IOException {
Buffer frame = new Buffer();
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
// Compose the expected GOAWAY frame without debug data.
frame.writeShort(8); // Without debug data there's only 2 32-bit fields.
frame.writeByte(Http20Draft09.TYPE_GOAWAY);
frame.writeByte(0); // no flags.
frame.writeInt(0); // connection-scope
frame.writeInt(expectedStreamId); // last good stream.
frame.writeInt(expectedError.httpCode);
// Check writer sends the same bytes.
assertEquals(frame, sendGoAway(expectedStreamId, expectedError, Util.EMPTY_BYTE_ARRAY));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
@Override public void goAway(
int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
assertEquals(expectedStreamId, lastGoodStreamId);
assertEquals(expectedError, errorCode);
assertEquals(0, debugData.size());
}
});
}
@Test public void goAwayWithDebugDataRoundTrip() throws IOException {
Buffer frame = new Buffer();
final ErrorCode expectedError = ErrorCode.PROTOCOL_ERROR;
final ByteString expectedData = ByteString.encodeUtf8("abcdefgh");
// Compose the expected GOAWAY frame without debug data.
frame.writeShort(8 + expectedData.size());
frame.writeByte(Http20Draft09.TYPE_GOAWAY);
frame.writeByte(0); // no flags.
frame.writeInt(0); // connection-scope
frame.writeInt(0); // never read any stream!
frame.writeInt(expectedError.httpCode);
frame.write(expectedData.toByteArray());
// Check writer sends the same bytes.
assertEquals(frame, sendGoAway(0, expectedError, expectedData.toByteArray()));
FrameReader fr = new Http20Draft09.Reader(frame, 4096, false);
fr.nextFrame(new BaseTestHandler() { // Consume the go away frame.
@Override public void goAway(
int lastGoodStreamId, ErrorCode errorCode, ByteString debugData) {
assertEquals(0, lastGoodStreamId);
assertEquals(expectedError, errorCode);
assertEquals(expectedData, debugData);
}
});
}
@Test public void frameSizeError() throws IOException {
Http20Draft09.Writer writer = new Http20Draft09.Writer(new Buffer(), true);
try {
writer.frameHeader(16384, Http20Draft09.TYPE_DATA, Http20Draft09.FLAG_NONE, 0);
fail();
} catch (IllegalArgumentException e) {
assertEquals("FRAME_SIZE_ERROR length > 16383: 16384", e.getMessage());
}
}
@Test public void streamIdHasReservedBit() throws IOException {
Http20Draft09.Writer writer = new Http20Draft09.Writer(new Buffer(), true);
try {
int streamId = 3;
streamId |= 1L << 31; // set reserved bit
writer.frameHeader(16383, Http20Draft09.TYPE_DATA, Http20Draft09.FLAG_NONE, streamId);
fail();
} catch (IllegalArgumentException e) {
assertEquals("reserved bit set: -2147483645", e.getMessage());
}
}
private Buffer literalHeaders(List<Header> sentHeaders) throws IOException {
Buffer out = new Buffer();
new HpackDraft05.Writer(out).writeHeaders(sentHeaders);
return out;
}
private Buffer sendPingFrame(boolean ack, int payload1, int payload2) throws IOException {
Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).ping(ack, payload1, payload2);
return out;
}
private Buffer sendGoAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData)
throws IOException {
Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).goAway(lastGoodStreamId, errorCode, debugData);
return out;
}
private Buffer sendDataFrame(Buffer data) throws IOException {
Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).dataFrame(expectedStreamId, Http20Draft09.FLAG_NONE, data,
(int) data.size());
return out;
}
private Buffer windowUpdate(long windowSizeIncrement) throws IOException {
Buffer out = new Buffer();
new Http20Draft09.Writer(out, true).windowUpdate(expectedStreamId, windowSizeIncrement);
return out;
}
}

View File

@@ -43,18 +43,13 @@ public class HuffmanTest {
}
private void assertRoundTrip(byte[] buf) throws IOException {
assertRoundTrip(Huffman.Codec.REQUEST, buf);
assertRoundTrip(Huffman.Codec.RESPONSE, buf);
}
private static void assertRoundTrip(Huffman.Codec codec, byte[] buf) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
codec.encode(buf, dos);
assertEquals(baos.size(), codec.encodedLength(buf));
Huffman.get().encode(buf, dos);
assertEquals(baos.size(), Huffman.get().encodedLength(buf));
byte[] decodedBytes = codec.decode(baos.toByteArray());
byte[] decodedBytes = Huffman.get().decode(baos.toByteArray());
assertTrue(Arrays.equals(buf, decodedBytes));
}
}

View File

@@ -280,7 +280,7 @@ public final class MockSpdyPeer implements Closeable {
@Override
public void pushPromise(int streamId, int associatedStreamId, List<Header> headerBlock) {
this.type = Http20Draft09.TYPE_PUSH_PROMISE;
this.type = Http20Draft10.TYPE_PUSH_PROMISE;
this.streamId = streamId;
this.associatedStreamId = associatedStreamId;
this.headerBlock = headerBlock;

View File

@@ -37,7 +37,7 @@ public final class SettingsTest {
@Test public void setFields() {
Settings settings = new Settings();
// WARNING: clash on flags between spdy/3 and http/2!
// WARNING: clash on flags between spdy/3 and HTTP/2!
assertEquals(-3, settings.getUploadBandwidth(-3));
assertEquals(-1, settings.getHeaderTableSize());
settings.set(UPLOAD_BANDWIDTH, 0, 42);
@@ -45,7 +45,7 @@ public final class SettingsTest {
settings.set(Settings.HEADER_TABLE_SIZE, 0, 8096);
assertEquals(8096, settings.getHeaderTableSize());
// WARNING: clash on flags between spdy/3 and http/2!
// WARNING: clash on flags between spdy/3 and HTTP/2!
assertEquals(-3, settings.getDownloadBandwidth(-3));
assertEquals(true, settings.getEnablePush(true));
settings.set(DOWNLOAD_BANDWIDTH, 0, 53);

View File

@@ -55,7 +55,7 @@ import static org.junit.Assert.fail;
public final class SpdyConnectionTest {
private static final Variant SPDY3 = new Spdy3();
private static final Variant HTTP_20_DRAFT_09 = new Http20Draft09();
private static final Variant HTTP_20_DRAFT_09 = new Http20Draft10();
private final MockSpdyPeer peer = new MockSpdyPeer();
@After public void tearDown() throws Exception {
@@ -336,7 +336,7 @@ public final class SpdyConnectionTest {
// verify the peer's settings were read and applied.
synchronized (connection) {
assertEquals(0, connection.peerSettings.getHeaderTableSize());
Http20Draft09.Reader frameReader = (Http20Draft09.Reader) connection.frameReader;
Http20Draft10.Reader frameReader = (Http20Draft10.Reader) connection.frameReader;
assertEquals(0, frameReader.hpackReader.maxHeaderTableByteCount());
// TODO: when supported, check the frameWriter's compression table is unaffected.
}

View File

@@ -340,7 +340,7 @@ public final class OkHttpClient implements URLStreamHandlerFactory, Cloneable {
*
* <p><strong>This is an evolving set.</strong> Future releases may drop
* support for transitional protocols (like spdy/3.1), in favor of their
* successors (spdy/4 or http/2.0). The http/1.1 transport will never be
* successors (spdy/4 or hTTP/2). The http/1.1 transport will never be
* dropped.
*
* <p>If multiple protocols are specified, <a

View File

@@ -34,7 +34,7 @@ import okio.ByteString;
* indicate how HTTP messages are framed.
*/
public enum Protocol {
HTTP_2("HTTP-draft-09/2.0", true),
HTTP_2("h2-10", true),
SPDY_3("spdy/3.1", true),
HTTP_11("http/1.1", false);

View File

@@ -125,7 +125,7 @@ public final class SpdyTransport implements Transport {
result.add(new Header(VERSION, version));
result.add(new Header(TARGET_HOST, host));
} else if (Protocol.HTTP_2 == protocol) {
result.add(new Header(TARGET_AUTHORITY, host));
result.add(new Header(TARGET_AUTHORITY, host)); // Optional in HTTP/2
} else {
throw new AssertionError();
}

View File

@@ -1,7 +1,6 @@
package com.squareup.okhttp.internal.spdy;
// TODO: revisit for http/2 draft 9
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-09#section-7
// http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-7
public enum ErrorCode {
/** Not an error! For SPDY stream resets, prefer null over NO_ERROR. */
NO_ERROR(0, -1, 0),
@@ -34,6 +33,12 @@ public enum ErrorCode {
COMPRESSION_ERROR(9, -1, -1),
CONNECT_ERROR(10, -1, -1),
ENHANCE_YOUR_CALM(11, -1, -1),
INADEQUATE_SECURITY(12, -1, -1),
INVALID_CREDENTIALS(-1, 10, -1);
public final int httpCode;

View File

@@ -76,7 +76,7 @@ public interface FrameReader extends Closeable {
* sending this message. If {@code lastGoodStreamId} is zero, the peer
* processed no frames.
* @param errorCode reason for closing the connection.
* @param debugData only valid for http/2; opaque debug data to send.
* @param debugData only valid for HTTP/2; opaque debug data to send.
*/
void goAway(int lastGoodStreamId, ErrorCode errorCode, ByteString debugData);

View File

@@ -87,7 +87,7 @@ public interface FrameWriter extends Closeable {
* @param lastGoodStreamId the last stream ID processed, or zero if no
* streams were processed.
* @param errorCode reason for closing the connection.
* @param debugData only valid for http/2; opaque debug data to send.
* @param debugData only valid for HTTP/2; opaque debug data to send.
*/
void goAway(int lastGoodStreamId, ErrorCode errorCode, byte[] debugData) throws IOException;

View File

@@ -9,7 +9,7 @@ public final class Header {
public static final ByteString TARGET_METHOD = ByteString.encodeUtf8(":method");
public static final ByteString TARGET_PATH = ByteString.encodeUtf8(":path");
public static final ByteString TARGET_SCHEME = ByteString.encodeUtf8(":scheme");
public static final ByteString TARGET_AUTHORITY = ByteString.encodeUtf8(":authority"); // http/2
public static final ByteString TARGET_AUTHORITY = ByteString.encodeUtf8(":authority"); // HTTP/2
public static final ByteString TARGET_HOST = ByteString.encodeUtf8(":host"); // spdy/3
public static final ByteString VERSION = ByteString.encodeUtf8(":version"); // spdy/3

View File

@@ -15,15 +15,15 @@ import okio.Okio;
import okio.Source;
/**
* Read and write HPACK v05.
* Read and write HPACK v06.
*
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05
* http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06
*
* This implementation uses an array for the header table with a bitset for
* references. Dynamic entries are added to the array, starting in the last
* position moving forward. When the array fills, it is doubled.
*/
final class HpackDraft05 {
final class HpackDraft06 {
private static final int PREFIX_6_BITS = 0x3f;
private static final int PREFIX_7_BITS = 0x7f;
@@ -90,12 +90,11 @@ final class HpackDraft05 {
new Header("www-authenticate", "")
};
private HpackDraft05() {
private HpackDraft06() {
}
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#section-4.1.2
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#section-3.2
static final class Reader {
private final Huffman.Codec huffmanCodec;
private final List<Header> emittedHeaders = new ArrayList<Header>();
private final BufferedSource source;
@@ -119,8 +118,7 @@ final class HpackDraft05 {
BitArray emittedReferencedHeaders = new BitArray.FixedCapacity();
int headerTableByteCount = 0;
Reader(boolean client, int maxHeaderTableByteCount, Source source) {
this.huffmanCodec = client ? Huffman.Codec.RESPONSE : Huffman.Codec.REQUEST;
Reader(int maxHeaderTableByteCount, Source source) {
this.maxHeaderTableByteCount = maxHeaderTableByteCount;
this.source = Okio.buffer(source);
}
@@ -181,7 +179,22 @@ final class HpackDraft05 {
while (!source.exhausted()) {
int b = source.readByte() & 0xff;
if (b == 0x80) { // 10000000
clearReferenceSet();
b = source.readByte();
if ((b & 0x80) == 0x80) { // 1NNNNNNN
if ((b & PREFIX_7_BITS) == 0) {
clearReferenceSet();
} else {
throw new IOException("Invalid header table state change " + b);
}
} else {
int headerTableByteCount = b != PREFIX_7_BITS ? b : readInt(b, PREFIX_7_BITS);
if (headerTableByteCount < 0) {
throw new IOException("Invalid header table byte count " + headerTableByteCount);
}
maxHeaderTableByteCount = Math.min(headerTableByteCount, maxHeaderTableByteCount);
int toEvict = headerTableByteCount - maxHeaderTableByteCount;
if (toEvict > 0) evictToRecoverBytes(toEvict);
}
} else if ((b & 0x80) == 0x80) { // 1NNNNNNN
int index = readInt(b, PREFIX_7_BITS);
readIndexedHeader(index - 1);
@@ -228,9 +241,13 @@ final class HpackDraft05 {
return result;
}
private void readIndexedHeader(int index) {
private void readIndexedHeader(int index) throws IOException {
if (isStaticHeader(index)) {
Header staticEntry = STATIC_HEADER_TABLE[index - headerCount];
index -= headerCount;
if (index > STATIC_HEADER_TABLE.length - 1) {
throw new IOException("Header index too large " + (index + 1));
}
Header staticEntry = STATIC_HEADER_TABLE[index];
if (maxHeaderTableByteCount == 0) {
emittedHeaders.add(staticEntry);
} else {
@@ -371,7 +388,7 @@ final class HpackDraft05 {
ByteString byteString = source.readByteString(length);
if (huffmanDecode) {
byteString = huffmanCodec.decode(byteString); // TODO: streaming Huffman!
byteString = Huffman.get().decode(byteString); // TODO: streaming Huffman!
}
if (asciiLowercase) {
@@ -419,7 +436,7 @@ final class HpackDraft05 {
}
}
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#section-4.1.1
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#section-4.1.1
void writeInt(int value, int prefixMask, int bits) throws IOException {
// Write the raw value for a single byte value.
if (value < prefixMask) {

View File

@@ -26,10 +26,10 @@ import okio.Source;
import okio.Timeout;
/**
* Read and write http/2 v09 frames.
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-09
* Read and write HTTP/2 v10 frames.
* http://tools.ietf.org/html/draft-ietf-httpbis-http2-10
*/
public final class Http20Draft09 implements Variant {
public final class Http20Draft10 implements Variant {
@Override public Protocol getProtocol() {
return Protocol.HTTP_2;
@@ -46,15 +46,18 @@ public final class Http20Draft09 implements Variant {
static final byte TYPE_PUSH_PROMISE = 0x5;
static final byte TYPE_PING = 0x6;
static final byte TYPE_GOAWAY = 0x7;
static final byte TYPE_WINDOW_UPDATE = 0x9;
static final byte TYPE_CONTINUATION = 0xa;
static final byte TYPE_WINDOW_UPDATE = 0x8;
static final byte TYPE_CONTINUATION = 0x9;
static final byte FLAG_NONE = 0x0;
static final byte FLAG_ACK = 0x1;
static final byte FLAG_END_STREAM = 0x1;
static final byte FLAG_END_SEGMENT = 0x2;
static final byte FLAG_END_HEADERS = 0x4; // Used for headers and continuation.
static final byte FLAG_END_PUSH_PROMISE = 0x4;
static final byte FLAG_PRIORITY = 0x8;
static final byte FLAG_PAD_LOW = 0x10;
static final byte FLAG_PAD_HIGH = 0x20;
@Override public FrameReader newReader(BufferedSource source, boolean client) {
return new Reader(source, 4096, client);
@@ -74,13 +77,13 @@ public final class Http20Draft09 implements Variant {
private final boolean client;
// Visible for testing.
final HpackDraft05.Reader hpackReader;
final HpackDraft06.Reader hpackReader;
Reader(BufferedSource source, int headerTableSize, boolean client) {
this.source = source;
this.client = client;
this.continuation = new ContinuationSource(this.source);
this.hpackReader = new HpackDraft05.Reader(client, headerTableSize, continuation);
this.hpackReader = new HpackDraft06.Reader(headerTableSize, continuation);
}
@Override public void readConnectionHeader() throws IOException {
@@ -146,8 +149,7 @@ public final class Http20Draft09 implements Variant {
break;
default:
// Implementations MUST ignore frames of unsupported or unrecognized types.
source.skip(length);
throw ioException("PROTOCOL_ERROR: unknown frame type %s", type);
}
return true;
}
@@ -158,21 +160,27 @@ public final class Http20Draft09 implements Variant {
boolean endStream = (flags & FLAG_END_STREAM) != 0;
short padding = readPadding(source, flags);
int priority = -1;
if ((flags & FLAG_PRIORITY) != 0) {
priority = source.readInt() & 0x7fffffff;
length -= 4; // account for above read.
}
List<Header> headerBlock = readHeaderBlock(length, flags, streamId);
length = lengthWithoutPadding(length, flags, padding);
List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
handler.headers(false, endStream, streamId, -1, priority, headerBlock,
HeadersMode.HTTP_20_HEADERS);
}
private List<Header> readHeaderBlock(short length, byte flags, int streamId)
private List<Header> readHeaderBlock(short length, short padding, byte flags, int streamId)
throws IOException {
continuation.length = continuation.left = length;
continuation.padding = padding;
continuation.flags = flags;
continuation.streamId = streamId;
@@ -187,7 +195,12 @@ public final class Http20Draft09 implements Variant {
throws IOException {
boolean inFinished = (flags & FLAG_END_STREAM) != 0;
// TODO: checkState open or half-closed (local) or raise STREAM_CLOSED
short padding = readPadding(source, flags);
length = lengthWithoutPadding(length, flags, padding);
handler.data(inFinished, streamId, source, length);
source.skip(padding);
}
private void readPriority(Handler handler, short length, byte flags, int streamId)
@@ -221,13 +234,32 @@ public final class Http20Draft09 implements Variant {
return;
}
if (length % 8 != 0) throw ioException("TYPE_SETTINGS length %% 8 != 0: %s", length);
if (length % 5 != 0) throw ioException("TYPE_SETTINGS length %% 5 != 0: %s", length);
Settings settings = new Settings();
for (int i = 0; i < length; i += 8) {
int w1 = source.readInt();
for (int i = 0; i < length; i += 5) {
int id = source.readByte();
int value = source.readInt();
// int r = (w1 & 0xff000000) >>> 24; // Reserved.
int id = w1 & 0xffffff;
switch (id) {
case 1: // SETTINGS_HEADER_TABLE_SIZE
break;
case 2: // SETTINGS_ENABLE_PUSH
if (value != 0 && value != 1) {
throw ioException("PROTOCOL_ERROR SETTINGS_ENABLE_PUSH != 0 or 1");
}
break;
case 3: // SETTINGS_MAX_CONCURRENT_STREAMS
id = 4; // Renumbered in draft 10.
break;
case 4: // SETTINGS_INITIAL_WINDOW_SIZE
id = 7; // Renumbered in draft 10.
if (value < 0) {
throw ioException("PROTOCOL_ERROR SETTINGS_INITIAL_WINDOW_SIZE > 2^31 - 1");
}
break;
default:
throw ioException("PROTOCOL_ERROR invalid settings id: %s", id);
}
settings.set(id, 0, value);
}
handler.settings(false, settings);
@@ -243,7 +275,8 @@ public final class Http20Draft09 implements Variant {
}
int promisedStreamId = source.readInt() & 0x7fffffff;
length -= 4; // account for above read.
List<Header> headerBlock = readHeaderBlock(length, flags, streamId);
short padding = 0; // no padding for push promise.
List<Header> headerBlock = readHeaderBlock(length, padding, flags, streamId);
handler.pushPromise(streamId, promisedStreamId, headerBlock);
}
@@ -292,14 +325,14 @@ public final class Http20Draft09 implements Variant {
private final BufferedSink sink;
private final boolean client;
private final Buffer hpackBuffer;
private final HpackDraft05.Writer hpackWriter;
private final HpackDraft06.Writer hpackWriter;
private boolean closed;
Writer(BufferedSink sink, boolean client) {
this.sink = sink;
this.client = client;
this.hpackBuffer = new Buffer();
this.hpackWriter = new HpackDraft05.Writer(hpackBuffer);
this.hpackWriter = new HpackDraft06.Writer(hpackBuffer);
}
@Override public synchronized void flush() throws IOException {
@@ -411,14 +444,17 @@ public final class Http20Draft09 implements Variant {
@Override public synchronized void settings(Settings settings) throws IOException {
if (closed) throw new IOException("closed");
int length = settings.size() * 8;
int length = settings.size() * 5;
byte type = TYPE_SETTINGS;
byte flags = FLAG_NONE;
int streamId = 0;
frameHeader(length, type, flags, streamId);
for (int i = 0; i < Settings.COUNT; i++) {
if (!settings.isSet(i)) continue;
sink.writeInt(i & 0xffffff);
int id = i;
if (id == 4) id = 3; // SETTINGS_MAX_CONCURRENT_STREAMS renumbered.
else if (id == 7) id = 4; // SETTINGS_INITIAL_WINDOW_SIZE renumbered.
sink.writeByte(id);
sink.writeInt(settings.get(i));
}
sink.flush();
@@ -493,16 +529,17 @@ public final class Http20Draft09 implements Variant {
/**
* Decompression of the header block occurs above the framing layer. This
* class lazily reads continuation frames as they are needed by {@link
* HpackDraft05.Reader#readHeaders()}.
* HpackDraft06.Reader#readHeaders()}.
*/
static final class ContinuationSource implements Source {
private final BufferedSource source;
int length;
short length;
byte flags;
int streamId;
int left;
short left;
short padding;
public ContinuationSource(BufferedSource source) {
this.source = source;
@@ -510,6 +547,8 @@ public final class Http20Draft09 implements Variant {
@Override public long read(Buffer sink, long byteCount) throws IOException {
while (left == 0) {
source.skip(padding);
padding = 0;
if ((flags & FLAG_END_HEADERS) != 0) return -1;
readContinuationHeader();
// TODO: test case for empty continuation header?
@@ -532,12 +571,41 @@ public final class Http20Draft09 implements Variant {
int previousStreamId = streamId;
int w1 = source.readInt();
int w2 = source.readInt();
length = left = (short) ((w1 & 0x3fff0000) >> 16);
length = (short) ((w1 & 0x3fff0000) >> 16);
byte type = (byte) ((w1 & 0xff00) >> 8);
flags = (byte) (w1 & 0xff);
padding = readPadding(source, flags);
length = left = lengthWithoutPadding(length, flags, padding);
streamId = (w2 & 0x7fffffff);
if (type != TYPE_CONTINUATION) throw ioException("%s != TYPE_CONTINUATION", type);
if (streamId != previousStreamId) throw ioException("TYPE_CONTINUATION streamId changed");
}
}
private static short readPadding(BufferedSource source, byte flags) throws IOException {
if ((flags & FLAG_PAD_HIGH) != 0 && (flags & FLAG_PAD_LOW) == 0) {
throw ioException("PROTOCOL_ERROR FLAG_PAD_HIGH set without FLAG_PAD_LOW");
}
int padding = 0;
if ((flags & FLAG_PAD_HIGH) != 0) {
padding = source.readShort() & 0xffff;
} else if ((flags & FLAG_PAD_LOW) != 0) {
padding = source.readByte() & 0xff;
}
if (padding > 16383) throw ioException("PROTOCOL_ERROR padding > 16383: %s", padding);
return (short) padding;
}
private static short lengthWithoutPadding(short length, byte flags, short padding)
throws IOException {
if ((flags & FLAG_PAD_HIGH) != 0) { // account for reading the padding length.
length -= 2;
} else if ((flags & FLAG_PAD_LOW) != 0) {
length--;
}
if (padding > length) {
throw ioException("PROTOCOL_ERROR padding %s > remaining length %s", padding, length);
}
return (short) (length - padding);
}
}

View File

@@ -30,132 +30,172 @@ import okio.ByteString;
* </ul>
*/
class Huffman {
enum Codec {
REQUEST(REQUEST_CODES, REQUEST_CODE_LENGTHS),
RESPONSE(RESPONSE_CODES, RESPONSE_CODE_LENGTHS);
private final Node root = new Node();
private final int[] codes;
private final byte[] lengths;
// Appendix C: Huffman Codes
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-06#appendix-C
private static final int[] CODES = {
0x7ffffba, 0x7ffffbb, 0x7ffffbc, 0x7ffffbd, 0x7ffffbe, 0x7ffffbf, 0x7ffffc0, 0x7ffffc1,
0x7ffffc2, 0x7ffffc3, 0x7ffffc4, 0x7ffffc5, 0x7ffffc6, 0x7ffffc7, 0x7ffffc8, 0x7ffffc9,
0x7ffffca, 0x7ffffcb, 0x7ffffcc, 0x7ffffcd, 0x7ffffce, 0x7ffffcf, 0x7ffffd0, 0x7ffffd1,
0x7ffffd2, 0x7ffffd3, 0x7ffffd4, 0x7ffffd5, 0x7ffffd6, 0x7ffffd7, 0x7ffffd8, 0x7ffffd9, 0xe8,
0xffc, 0x3ffa, 0x7ffc, 0x7ffd, 0x24, 0x6e, 0x7ffe, 0x7fa, 0x7fb, 0x3fa, 0x7fc, 0xe9, 0x25,
0x4, 0x0, 0x5, 0x6, 0x7, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1ec, 0xea, 0x3fffe, 0x2d,
0x1fffc, 0x1ed, 0x3ffb, 0x6f, 0xeb, 0xec, 0xed, 0xee, 0x70, 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x3fb,
0x1f2, 0xef, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f7, 0xf0, 0xf1, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc,
0x3fc, 0x3ffc, 0x7ffffda, 0x1ffc, 0x3ffd, 0x2e, 0x7fffe, 0x8, 0x2f, 0x9, 0x30, 0x1, 0x31,
0x32, 0x33, 0xa, 0x71, 0x72, 0xb, 0x34, 0xc, 0xd, 0xe, 0xf2, 0xf, 0x10, 0x11, 0x35, 0x73,
0x36, 0xf3, 0xf4, 0xf5, 0x1fffd, 0x7fd, 0x1fffe, 0xffd, 0x7ffffdb, 0x7ffffdc, 0x7ffffdd,
0x7ffffde, 0x7ffffdf, 0x7ffffe0, 0x7ffffe1, 0x7ffffe2, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
0x7ffffe6, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0x7ffffec, 0x7ffffed,
0x7ffffee, 0x7ffffef, 0x7fffff0, 0x7fffff1, 0x7fffff2, 0x7fffff3, 0x7fffff4, 0x7fffff5,
0x7fffff6, 0x7fffff7, 0x7fffff8, 0x7fffff9, 0x7fffffa, 0x7fffffb, 0x7fffffc, 0x7fffffd,
0x7fffffe, 0x7ffffff, 0x3ffff80, 0x3ffff81, 0x3ffff82, 0x3ffff83, 0x3ffff84, 0x3ffff85,
0x3ffff86, 0x3ffff87, 0x3ffff88, 0x3ffff89, 0x3ffff8a, 0x3ffff8b, 0x3ffff8c, 0x3ffff8d,
0x3ffff8e, 0x3ffff8f, 0x3ffff90, 0x3ffff91, 0x3ffff92, 0x3ffff93, 0x3ffff94, 0x3ffff95,
0x3ffff96, 0x3ffff97, 0x3ffff98, 0x3ffff99, 0x3ffff9a, 0x3ffff9b, 0x3ffff9c, 0x3ffff9d,
0x3ffff9e, 0x3ffff9f, 0x3ffffa0, 0x3ffffa1, 0x3ffffa2, 0x3ffffa3, 0x3ffffa4, 0x3ffffa5,
0x3ffffa6, 0x3ffffa7, 0x3ffffa8, 0x3ffffa9, 0x3ffffaa, 0x3ffffab, 0x3ffffac, 0x3ffffad,
0x3ffffae, 0x3ffffaf, 0x3ffffb0, 0x3ffffb1, 0x3ffffb2, 0x3ffffb3, 0x3ffffb4, 0x3ffffb5,
0x3ffffb6, 0x3ffffb7, 0x3ffffb8, 0x3ffffb9, 0x3ffffba, 0x3ffffbb, 0x3ffffbc, 0x3ffffbd,
0x3ffffbe, 0x3ffffbf, 0x3ffffc0, 0x3ffffc1, 0x3ffffc2, 0x3ffffc3, 0x3ffffc4, 0x3ffffc5,
0x3ffffc6, 0x3ffffc7, 0x3ffffc8, 0x3ffffc9, 0x3ffffca, 0x3ffffcb, 0x3ffffcc, 0x3ffffcd,
0x3ffffce, 0x3ffffcf, 0x3ffffd0, 0x3ffffd1, 0x3ffffd2, 0x3ffffd3, 0x3ffffd4, 0x3ffffd5,
0x3ffffd6, 0x3ffffd7, 0x3ffffd8, 0x3ffffd9, 0x3ffffda, 0x3ffffdb
};
/**
* @param codes Index designates the symbol this code represents.
* @param lengths Index designates the symbol this code represents.
*/
Codec(int[] codes, byte[] lengths) {
buildTree(codes, lengths);
this.codes = codes;
this.lengths = lengths;
}
private static final byte[] CODE_LENGTHS = {
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 12, 14, 15, 15, 6, 7, 15, 11, 11, 10, 11, 8, 6, 5, 4,
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 9, 8, 18, 6, 17, 9, 14, 7, 8, 8, 8, 8, 7, 9, 9, 9, 9, 10, 9, 8,
9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 10, 14, 27, 13, 14, 6, 19, 5, 6, 5, 6, 4, 6, 6, 6, 5, 7,
7, 5, 6, 5, 5, 5, 8, 5, 5, 5, 6, 7, 6, 8, 8, 8, 17, 11, 17, 12, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26
};
void encode(byte[] data, OutputStream out) throws IOException {
long current = 0;
int n = 0;
private static final Huffman INSTANCE = new Huffman();
for (int i = 0; i < data.length; i++) {
int b = data[i] & 0xFF;
int code = codes[b];
int nbits = lengths[b];
public static Huffman get() {
return INSTANCE;
}
current <<= nbits;
current |= code;
n += nbits;
private final Node root = new Node();
while (n >= 8) {
n -= 8;
out.write(((int) (current >> n)));
}
}
private Huffman() {
buildTree();
}
if (n > 0) {
current <<= (8 - n);
current |= (0xFF >>> n);
out.write((int) current);
void encode(byte[] data, OutputStream out) throws IOException {
long current = 0;
int n = 0;
for (int i = 0; i < data.length; i++) {
int b = data[i] & 0xFF;
int code = CODES[b];
int nbits = CODE_LENGTHS[b];
current <<= nbits;
current |= code;
n += nbits;
while (n >= 8) {
n -= 8;
out.write(((int) (current >> n)));
}
}
int encodedLength(byte[] bytes) {
long len = 0;
if (n > 0) {
current <<= (8 - n);
current |= (0xFF >>> n);
out.write((int) current);
}
}
for (int i = 0; i < bytes.length; i++) {
int b = bytes[i] & 0xFF;
len += lengths[b];
}
int encodedLength(byte[] bytes) {
long len = 0;
return (int) ((len + 7) >> 3);
for (int i = 0; i < bytes.length; i++) {
int b = bytes[i] & 0xFF;
len += CODE_LENGTHS[b];
}
ByteString decode(ByteString buf) throws IOException {
return ByteString.of(decode(buf.toByteArray()));
}
return (int) ((len + 7) >> 3);
}
byte[] decode(byte[] buf) throws IOException {
// FIXME
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Node node = root;
int current = 0;
int nbits = 0;
for (int i = 0; i < buf.length; i++) {
int b = buf[i] & 0xFF;
current = (current << 8) | b;
nbits += 8;
while (nbits >= 8) {
int c = (current >>> (nbits - 8)) & 0xFF;
node = node.children[c];
if (node.children == null) {
// terminal node
baos.write(node.symbol);
nbits -= node.terminalBits;
node = root;
} else {
// non-terminal node
nbits -= 8;
}
}
}
ByteString decode(ByteString buf) throws IOException {
return ByteString.of(decode(buf.toByteArray()));
}
while (nbits > 0) {
int c = (current << (8 - nbits)) & 0xFF;
byte[] decode(byte[] buf) throws IOException {
// FIXME
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Node node = root;
int current = 0;
int nbits = 0;
for (int i = 0; i < buf.length; i++) {
int b = buf[i] & 0xFF;
current = (current << 8) | b;
nbits += 8;
while (nbits >= 8) {
int c = (current >>> (nbits - 8)) & 0xFF;
node = node.children[c];
if (node.children != null || node.terminalBits > nbits) {
break;
if (node.children == null) {
// terminal node
baos.write(node.symbol);
nbits -= node.terminalBits;
node = root;
} else {
// non-terminal node
nbits -= 8;
}
baos.write(node.symbol);
nbits -= node.terminalBits;
node = root;
}
return baos.toByteArray();
}
private void buildTree(int[] codes, byte[] lengths) {
for (int i = 0; i < lengths.length; i++) {
addCode(i, codes[i], lengths[i]);
}
}
private void addCode(int sym, int code, byte len) {
Node terminal = new Node(sym, len);
Node current = root;
while (len > 8) {
len -= 8;
int i = ((code >>> len) & 0xFF);
if (current.children == null) {
throw new IllegalStateException("invalid dictionary: prefix not unique");
}
if (current.children[i] == null) {
current.children[i] = new Node();
}
current = current.children[i];
while (nbits > 0) {
int c = (current << (8 - nbits)) & 0xFF;
node = node.children[c];
if (node.children != null || node.terminalBits > nbits) {
break;
}
baos.write(node.symbol);
nbits -= node.terminalBits;
node = root;
}
int shift = 8 - len;
int start = (code << shift) & 0xFF;
int end = 1 << shift;
for (int i = start; i < start + end; i++) {
current.children[i] = terminal;
return baos.toByteArray();
}
private void buildTree() {
for (int i = 0; i < CODE_LENGTHS.length; i++) {
addCode(i, CODES[i], CODE_LENGTHS[i]);
}
}
private void addCode(int sym, int code, byte len) {
Node terminal = new Node(sym, len);
Node current = root;
while (len > 8) {
len -= 8;
int i = ((code >>> len) & 0xFF);
if (current.children == null) {
throw new IllegalStateException("invalid dictionary: prefix not unique");
}
if (current.children[i] == null) {
current.children[i] = new Node();
}
current = current.children[i];
}
int shift = 8 - len;
int start = (code << shift) & 0xFF;
int end = 1 << shift;
for (int i = start; i < start + end; i++) {
current.children[i] = terminal;
}
}
@@ -190,95 +230,4 @@ class Huffman {
this.terminalBits = b == 0 ? 8 : b;
}
}
// Appendix C: Huffman Codes For Requests
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-C
private static final int[] REQUEST_CODES = {
0x7ffffba, 0x7ffffbb, 0x7ffffbc, 0x7ffffbd, 0x7ffffbe, 0x7ffffbf, 0x7ffffc0, 0x7ffffc1,
0x7ffffc2, 0x7ffffc3, 0x7ffffc4, 0x7ffffc5, 0x7ffffc6, 0x7ffffc7, 0x7ffffc8, 0x7ffffc9,
0x7ffffca, 0x7ffffcb, 0x7ffffcc, 0x7ffffcd, 0x7ffffce, 0x7ffffcf, 0x7ffffd0, 0x7ffffd1,
0x7ffffd2, 0x7ffffd3, 0x7ffffd4, 0x7ffffd5, 0x7ffffd6, 0x7ffffd7, 0x7ffffd8, 0x7ffffd9, 0xe8,
0xffc, 0x3ffa, 0x7ffc, 0x7ffd, 0x24, 0x6e, 0x7ffe, 0x7fa, 0x7fb, 0x3fa, 0x7fc, 0xe9, 0x25,
0x4, 0x0, 0x5, 0x6, 0x7, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1ec, 0xea, 0x3fffe, 0x2d,
0x1fffc, 0x1ed, 0x3ffb, 0x6f, 0xeb, 0xec, 0xed, 0xee, 0x70, 0x1ee, 0x1ef, 0x1f0, 0x1f1, 0x3fb,
0x1f2, 0xef, 0x1f3, 0x1f4, 0x1f5, 0x1f6, 0x1f7, 0xf0, 0xf1, 0x1f8, 0x1f9, 0x1fa, 0x1fb, 0x1fc,
0x3fc, 0x3ffc, 0x7ffffda, 0x1ffc, 0x3ffd, 0x2e, 0x7fffe, 0x8, 0x2f, 0x9, 0x30, 0x1, 0x31,
0x32, 0x33, 0xa, 0x71, 0x72, 0xb, 0x34, 0xc, 0xd, 0xe, 0xf2, 0xf, 0x10, 0x11, 0x35, 0x73,
0x36, 0xf3, 0xf4, 0xf5, 0x1fffd, 0x7fd, 0x1fffe, 0xffd, 0x7ffffdb, 0x7ffffdc, 0x7ffffdd,
0x7ffffde, 0x7ffffdf, 0x7ffffe0, 0x7ffffe1, 0x7ffffe2, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5,
0x7ffffe6, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0x7ffffec, 0x7ffffed,
0x7ffffee, 0x7ffffef, 0x7fffff0, 0x7fffff1, 0x7fffff2, 0x7fffff3, 0x7fffff4, 0x7fffff5,
0x7fffff6, 0x7fffff7, 0x7fffff8, 0x7fffff9, 0x7fffffa, 0x7fffffb, 0x7fffffc, 0x7fffffd,
0x7fffffe, 0x7ffffff, 0x3ffff80, 0x3ffff81, 0x3ffff82, 0x3ffff83, 0x3ffff84, 0x3ffff85,
0x3ffff86, 0x3ffff87, 0x3ffff88, 0x3ffff89, 0x3ffff8a, 0x3ffff8b, 0x3ffff8c, 0x3ffff8d,
0x3ffff8e, 0x3ffff8f, 0x3ffff90, 0x3ffff91, 0x3ffff92, 0x3ffff93, 0x3ffff94, 0x3ffff95,
0x3ffff96, 0x3ffff97, 0x3ffff98, 0x3ffff99, 0x3ffff9a, 0x3ffff9b, 0x3ffff9c, 0x3ffff9d,
0x3ffff9e, 0x3ffff9f, 0x3ffffa0, 0x3ffffa1, 0x3ffffa2, 0x3ffffa3, 0x3ffffa4, 0x3ffffa5,
0x3ffffa6, 0x3ffffa7, 0x3ffffa8, 0x3ffffa9, 0x3ffffaa, 0x3ffffab, 0x3ffffac, 0x3ffffad,
0x3ffffae, 0x3ffffaf, 0x3ffffb0, 0x3ffffb1, 0x3ffffb2, 0x3ffffb3, 0x3ffffb4, 0x3ffffb5,
0x3ffffb6, 0x3ffffb7, 0x3ffffb8, 0x3ffffb9, 0x3ffffba, 0x3ffffbb, 0x3ffffbc, 0x3ffffbd,
0x3ffffbe, 0x3ffffbf, 0x3ffffc0, 0x3ffffc1, 0x3ffffc2, 0x3ffffc3, 0x3ffffc4, 0x3ffffc5,
0x3ffffc6, 0x3ffffc7, 0x3ffffc8, 0x3ffffc9, 0x3ffffca, 0x3ffffcb, 0x3ffffcc, 0x3ffffcd,
0x3ffffce, 0x3ffffcf, 0x3ffffd0, 0x3ffffd1, 0x3ffffd2, 0x3ffffd3, 0x3ffffd4, 0x3ffffd5,
0x3ffffd6, 0x3ffffd7, 0x3ffffd8, 0x3ffffd9, 0x3ffffda, 0x3ffffdb
};
private static final byte[] REQUEST_CODE_LENGTHS = {
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 12, 14, 15, 15, 6, 7, 15, 11, 11, 10, 11, 8, 6, 5, 4,
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 9, 8, 18, 6, 17, 9, 14, 7, 8, 8, 8, 8, 7, 9, 9, 9, 9, 10, 9, 8,
9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 10, 14, 27, 13, 14, 6, 19, 5, 6, 5, 6, 4, 6, 6, 6, 5, 7,
7, 5, 6, 5, 5, 5, 8, 5, 5, 5, 6, 7, 6, 8, 8, 8, 17, 11, 17, 12, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26
};
// Appendix D: Huffman Codes For Responses
// http://tools.ietf.org/html/draft-ietf-httpbis-header-compression-05#appendix-D
private static final int[] RESPONSE_CODES = {
0x1ffffbc, 0x1ffffbd, 0x1ffffbe, 0x1ffffbf, 0x1ffffc0, 0x1ffffc1, 0x1ffffc2, 0x1ffffc3,
0x1ffffc4, 0x1ffffc5, 0x1ffffc6, 0x1ffffc7, 0x1ffffc8, 0x1ffffc9, 0x1ffffca, 0x1ffffcb,
0x1ffffcc, 0x1ffffcd, 0x1ffffce, 0x1ffffcf, 0x1ffffd0, 0x1ffffd1, 0x1ffffd2, 0x1ffffd3,
0x1ffffd4, 0x1ffffd5, 0x1ffffd6, 0x1ffffd7, 0x1ffffd8, 0x1ffffd9, 0x1ffffda, 0x1ffffdb, 0x0,
0xffa, 0x6a, 0x1ffa, 0x3ffc, 0x1ec, 0x3f8, 0x1ffb, 0x1ed, 0x1ee, 0xffb, 0x7fa, 0x22, 0x23,
0x24, 0x6b, 0x1, 0x2, 0x3, 0x8, 0x9, 0xa, 0x25, 0x26, 0xb, 0xc, 0xd, 0x1ef, 0xfffa, 0x6c,
0x1ffc, 0xffc, 0xfffb, 0x6d, 0xea, 0xeb, 0xec, 0xed, 0xee, 0x27, 0x1f0, 0xef, 0xf0, 0x3f9,
0x1f1, 0x28, 0xf1, 0xf2, 0x1f2, 0x3fa, 0x1f3, 0x29, 0xe, 0x1f4, 0x1f5, 0xf3, 0x3fb, 0x1f6,
0x3fc, 0x7fb, 0x1ffd, 0x7fc, 0x7ffc, 0x1f7, 0x1fffe, 0xf, 0x6e, 0x2a, 0x2b, 0x10, 0x6f, 0x70,
0x71, 0x2c, 0x1f8, 0x1f9, 0x72, 0x2d, 0x2e, 0x2f, 0x30, 0x1fa, 0x31, 0x32, 0x33, 0x34, 0x73,
0xf4, 0x74, 0xf5, 0x1fb, 0xfffc, 0x3ffd, 0xfffd, 0xfffe, 0x1ffffdc, 0x1ffffdd, 0x1ffffde,
0x1ffffdf, 0x1ffffe0, 0x1ffffe1, 0x1ffffe2, 0x1ffffe3, 0x1ffffe4, 0x1ffffe5, 0x1ffffe6,
0x1ffffe7, 0x1ffffe8, 0x1ffffe9, 0x1ffffea, 0x1ffffeb, 0x1ffffec, 0x1ffffed, 0x1ffffee,
0x1ffffef, 0x1fffff0, 0x1fffff1, 0x1fffff2, 0x1fffff3, 0x1fffff4, 0x1fffff5, 0x1fffff6,
0x1fffff7, 0x1fffff8, 0x1fffff9, 0x1fffffa, 0x1fffffb, 0x1fffffc, 0x1fffffd, 0x1fffffe,
0x1ffffff, 0xffff80, 0xffff81, 0xffff82, 0xffff83, 0xffff84, 0xffff85, 0xffff86, 0xffff87,
0xffff88, 0xffff89, 0xffff8a, 0xffff8b, 0xffff8c, 0xffff8d, 0xffff8e, 0xffff8f, 0xffff90,
0xffff91, 0xffff92, 0xffff93, 0xffff94, 0xffff95, 0xffff96, 0xffff97, 0xffff98, 0xffff99,
0xffff9a, 0xffff9b, 0xffff9c, 0xffff9d, 0xffff9e, 0xffff9f, 0xffffa0, 0xffffa1, 0xffffa2,
0xffffa3, 0xffffa4, 0xffffa5, 0xffffa6, 0xffffa7, 0xffffa8, 0xffffa9, 0xffffaa, 0xffffab,
0xffffac, 0xffffad, 0xffffae, 0xffffaf, 0xffffb0, 0xffffb1, 0xffffb2, 0xffffb3, 0xffffb4,
0xffffb5, 0xffffb6, 0xffffb7, 0xffffb8, 0xffffb9, 0xffffba, 0xffffbb, 0xffffbc, 0xffffbd,
0xffffbe, 0xffffbf, 0xffffc0, 0xffffc1, 0xffffc2, 0xffffc3, 0xffffc4, 0xffffc5, 0xffffc6,
0xffffc7, 0xffffc8, 0xffffc9, 0xffffca, 0xffffcb, 0xffffcc, 0xffffcd, 0xffffce, 0xffffcf,
0xffffd0, 0xffffd1, 0xffffd2, 0xffffd3, 0xffffd4, 0xffffd5, 0xffffd6, 0xffffd7, 0xffffd8,
0xffffd9, 0xffffda, 0xffffdb, 0xffffdc
};
private static final byte[] RESPONSE_CODE_LENGTHS = {
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 4, 12, 7, 13, 14, 9, 10, 13, 9, 9, 12, 11, 6, 6, 6, 7, 4,
4, 4, 5, 5, 5, 6, 6, 5, 5, 5, 9, 16, 7, 13, 12, 16, 7, 8, 8, 8, 8, 8, 6, 9, 8, 8, 10, 9, 6, 8,
8, 9, 10, 9, 6, 5, 9, 9, 8, 10, 9, 10, 11, 13, 11, 15, 9, 17, 5, 7, 6, 6, 5, 7, 7, 7, 6, 9, 9,
7, 6, 6, 6, 6, 9, 6, 6, 6, 6, 7, 8, 7, 8, 9, 16, 14, 16, 16, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24
};
}

View File

@@ -38,11 +38,11 @@ final class Settings {
/** spdy/3: Sender's estimate of max incoming kbps. */
static final int UPLOAD_BANDWIDTH = 1;
/** http/2: Size in bytes of the table used to decode the sender's header blocks. */
/** HTTP/2: Size in bytes of the table used to decode the sender's header blocks. */
static final int HEADER_TABLE_SIZE = 1;
/** spdy/3: Sender's estimate of max outgoing kbps. */
static final int DOWNLOAD_BANDWIDTH = 2;
/** http/2: An endpoint must not send a PUSH_PROMISE frame when this is 0. */
/** HTTP/2: An endpoint must not send a PUSH_PROMISE frame when this is 0. */
static final int ENABLE_PUSH = 2;
/** spdy/3: Sender's estimate of millis between sending a request and receiving a response. */
static final int ROUND_TRIP_TIME = 3;
@@ -134,7 +134,7 @@ final class Settings {
return (bit & set) != 0 ? values[UPLOAD_BANDWIDTH] : defaultValue;
}
/** http/2 only. Returns -1 if unset. */
/** HTTP/2 only. Returns -1 if unset. */
int getHeaderTableSize() {
int bit = 1 << HEADER_TABLE_SIZE;
return (bit & set) != 0 ? values[HEADER_TABLE_SIZE] : -1;
@@ -146,8 +146,8 @@ final class Settings {
return (bit & set) != 0 ? values[DOWNLOAD_BANDWIDTH] : defaultValue;
}
/** http/2 only. */
// TODO: honor this setting in http/2.
/** HTTP/2 only. */
// TODO: honor this setting in HTTP/2.
boolean getEnablePush(boolean defaultValue) {
int bit = 1 << ENABLE_PUSH;
return ((bit & set) != 0 ? values[ENABLE_PUSH] : defaultValue ? 1 : 0) == 1;
@@ -159,7 +159,7 @@ final class Settings {
return (bit & set) != 0 ? values[ROUND_TRIP_TIME] : defaultValue;
}
// TODO: honor this setting in spdy/3 and http/2.
// TODO: honor this setting in spdy/3 and HTTP/2.
int getMaxConcurrentStreams(int defaultValue) {
int bit = 1 << MAX_CONCURRENT_STREAMS;
return (bit & set) != 0 ? values[MAX_CONCURRENT_STREAMS] : defaultValue;
@@ -188,7 +188,7 @@ final class Settings {
return (bit & set) != 0 ? values[CLIENT_CERTIFICATE_VECTOR_SIZE] : defaultValue;
}
// TODO: honor this setting in spdy/3 and http/2.
// TODO: honor this setting in spdy/3 and HTTP/2.
boolean isFlowControlDisabled() {
int bit = 1 << FLOW_CONTROL_OPTIONS;
int value = (bit & set) != 0 ? values[FLOW_CONTROL_OPTIONS] : 0;

View File

@@ -143,7 +143,7 @@ public final class SpdyConnection implements Closeable {
Variant variant;
if (protocol == Protocol.HTTP_2) {
variant = new Http20Draft09();
variant = new Http20Draft10();
} else if (protocol == Protocol.SPDY_3) {
variant = new Spdy3();
} else {