Forking Twitter's hpack
Motivation: The twitter hpack project does not have the support that it used to have. See discussion here: https://github.com/netty/netty/issues/4403. Modifications: Created a new module in Netty and copied the latest from twitter hpack master. Result: Netty no longer depends on twitter hpack.
This commit is contained in:
parent
33b74e3f87
commit
bfac74e382
@ -211,3 +211,11 @@ non-blocking XML processor, which can be obtained at:
|
|||||||
* HOMEPAGE:
|
* HOMEPAGE:
|
||||||
* http://wiki.fasterxml.com/AaltoHome
|
* http://wiki.fasterxml.com/AaltoHome
|
||||||
|
|
||||||
|
This product contains a modified version of 'HPACK', a Java implementation of
|
||||||
|
the HTTP/2 HPACK algorithm written by Twitter. It can be obtained at:
|
||||||
|
|
||||||
|
* LICENSE:
|
||||||
|
* license/LICENSE.hpack.txt (Apache License 2.0)
|
||||||
|
* HOMEPAGE:
|
||||||
|
* https://github.com/twitter/hpack
|
||||||
|
|
||||||
|
@ -39,15 +39,15 @@
|
|||||||
<artifactId>netty-handler</artifactId>
|
<artifactId>netty-handler</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.twitter</groupId>
|
|
||||||
<artifactId>hpack</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.jcraft</groupId>
|
<groupId>com.jcraft</groupId>
|
||||||
<artifactId>jzlib</artifactId>
|
<artifactId>jzlib</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import com.twitter.hpack.Decoder;
|
|
||||||
import com.twitter.hpack.HeaderListener;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufInputStream;
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
|
import io.netty.handler.codec.http2.hpack.Decoder;
|
||||||
|
import io.netty.handler.codec.http2.hpack.HeaderListener;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import com.twitter.hpack.Encoder;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufOutputStream;
|
import io.netty.buffer.ByteBufOutputStream;
|
||||||
|
import io.netty.handler.codec.http2.hpack.Encoder;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -0,0 +1,568 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http2.hpack.HpackUtil.IndexType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public final class Decoder {
|
||||||
|
|
||||||
|
private static final IOException DECOMPRESSION_EXCEPTION =
|
||||||
|
new IOException("decompression failure");
|
||||||
|
private static final IOException ILLEGAL_INDEX_VALUE =
|
||||||
|
new IOException("illegal index value");
|
||||||
|
private static final IOException INVALID_MAX_DYNAMIC_TABLE_SIZE =
|
||||||
|
new IOException("invalid max dynamic table size");
|
||||||
|
private static final IOException MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED =
|
||||||
|
new IOException("max dynamic table size change required");
|
||||||
|
|
||||||
|
private static final byte[] EMPTY = {};
|
||||||
|
|
||||||
|
private final DynamicTable dynamicTable;
|
||||||
|
|
||||||
|
private int maxHeaderSize;
|
||||||
|
private int maxDynamicTableSize;
|
||||||
|
private int encoderMaxDynamicTableSize;
|
||||||
|
private boolean maxDynamicTableSizeChangeRequired;
|
||||||
|
|
||||||
|
private long headerSize;
|
||||||
|
private State state;
|
||||||
|
private IndexType indexType;
|
||||||
|
private int index;
|
||||||
|
private boolean huffmanEncoded;
|
||||||
|
private int skipLength;
|
||||||
|
private int nameLength;
|
||||||
|
private int valueLength;
|
||||||
|
private byte[] name;
|
||||||
|
|
||||||
|
private enum State {
|
||||||
|
READ_HEADER_REPRESENTATION,
|
||||||
|
READ_MAX_DYNAMIC_TABLE_SIZE,
|
||||||
|
READ_INDEXED_HEADER,
|
||||||
|
READ_INDEXED_HEADER_NAME,
|
||||||
|
READ_LITERAL_HEADER_NAME_LENGTH_PREFIX,
|
||||||
|
READ_LITERAL_HEADER_NAME_LENGTH,
|
||||||
|
READ_LITERAL_HEADER_NAME,
|
||||||
|
SKIP_LITERAL_HEADER_NAME,
|
||||||
|
READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX,
|
||||||
|
READ_LITERAL_HEADER_VALUE_LENGTH,
|
||||||
|
READ_LITERAL_HEADER_VALUE,
|
||||||
|
SKIP_LITERAL_HEADER_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new decoder.
|
||||||
|
*/
|
||||||
|
public Decoder(int maxHeaderSize, int maxHeaderTableSize) {
|
||||||
|
dynamicTable = new DynamicTable(maxHeaderTableSize);
|
||||||
|
this.maxHeaderSize = maxHeaderSize;
|
||||||
|
maxDynamicTableSize = maxHeaderTableSize;
|
||||||
|
encoderMaxDynamicTableSize = maxHeaderTableSize;
|
||||||
|
maxDynamicTableSizeChangeRequired = false;
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
headerSize = 0;
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
indexType = IndexType.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode the header block into header fields.
|
||||||
|
*/
|
||||||
|
public void decode(InputStream in, HeaderListener headerListener) throws IOException {
|
||||||
|
while (in.available() > 0) {
|
||||||
|
switch (state) {
|
||||||
|
case READ_HEADER_REPRESENTATION:
|
||||||
|
byte b = (byte) in.read();
|
||||||
|
if (maxDynamicTableSizeChangeRequired && (b & 0xE0) != 0x20) {
|
||||||
|
// Encoder MUST signal maximum dynamic table size change
|
||||||
|
throw MAX_DYNAMIC_TABLE_SIZE_CHANGE_REQUIRED;
|
||||||
|
}
|
||||||
|
if (b < 0) {
|
||||||
|
// Indexed Header Field
|
||||||
|
index = b & 0x7F;
|
||||||
|
if (index == 0) {
|
||||||
|
throw ILLEGAL_INDEX_VALUE;
|
||||||
|
} else if (index == 0x7F) {
|
||||||
|
state = State.READ_INDEXED_HEADER;
|
||||||
|
} else {
|
||||||
|
indexHeader(index, headerListener);
|
||||||
|
}
|
||||||
|
} else if ((b & 0x40) == 0x40) {
|
||||||
|
// Literal Header Field with Incremental Indexing
|
||||||
|
indexType = IndexType.INCREMENTAL;
|
||||||
|
index = b & 0x3F;
|
||||||
|
if (index == 0) {
|
||||||
|
state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;
|
||||||
|
} else if (index == 0x3F) {
|
||||||
|
state = State.READ_INDEXED_HEADER_NAME;
|
||||||
|
} else {
|
||||||
|
// Index was stored as the prefix
|
||||||
|
readName(index);
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
|
||||||
|
}
|
||||||
|
} else if ((b & 0x20) == 0x20) {
|
||||||
|
// Dynamic Table Size Update
|
||||||
|
index = b & 0x1F;
|
||||||
|
if (index == 0x1F) {
|
||||||
|
state = State.READ_MAX_DYNAMIC_TABLE_SIZE;
|
||||||
|
} else {
|
||||||
|
setDynamicTableSize(index);
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Literal Header Field without Indexing / never Indexed
|
||||||
|
indexType = ((b & 0x10) == 0x10) ? IndexType.NEVER : IndexType.NONE;
|
||||||
|
index = b & 0x0F;
|
||||||
|
if (index == 0) {
|
||||||
|
state = State.READ_LITERAL_HEADER_NAME_LENGTH_PREFIX;
|
||||||
|
} else if (index == 0x0F) {
|
||||||
|
state = State.READ_INDEXED_HEADER_NAME;
|
||||||
|
} else {
|
||||||
|
// Index was stored as the prefix
|
||||||
|
readName(index);
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_MAX_DYNAMIC_TABLE_SIZE:
|
||||||
|
int maxSize = decodeULE128(in);
|
||||||
|
if (maxSize == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for numerical overflow
|
||||||
|
if (maxSize > Integer.MAX_VALUE - index) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDynamicTableSize(index + maxSize);
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_INDEXED_HEADER:
|
||||||
|
int headerIndex = decodeULE128(in);
|
||||||
|
if (headerIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for numerical overflow
|
||||||
|
if (headerIndex > Integer.MAX_VALUE - index) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
indexHeader(index + headerIndex, headerListener);
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_INDEXED_HEADER_NAME:
|
||||||
|
// Header Name matches an entry in the Header Table
|
||||||
|
int nameIndex = decodeULE128(in);
|
||||||
|
if (nameIndex == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for numerical overflow
|
||||||
|
if (nameIndex > Integer.MAX_VALUE - index) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
readName(index + nameIndex);
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_NAME_LENGTH_PREFIX:
|
||||||
|
b = (byte) in.read();
|
||||||
|
huffmanEncoded = (b & 0x80) == 0x80;
|
||||||
|
index = b & 0x7F;
|
||||||
|
if (index == 0x7f) {
|
||||||
|
state = State.READ_LITERAL_HEADER_NAME_LENGTH;
|
||||||
|
} else {
|
||||||
|
nameLength = index;
|
||||||
|
|
||||||
|
// Disallow empty names -- they cannot be represented in HTTP/1.x
|
||||||
|
if (nameLength == 0) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check name length against max header size
|
||||||
|
if (exceedsMaxHeaderSize(nameLength)) {
|
||||||
|
|
||||||
|
if (indexType == IndexType.NONE) {
|
||||||
|
// Name is unused so skip bytes
|
||||||
|
name = EMPTY;
|
||||||
|
skipLength = nameLength;
|
||||||
|
state = State.SKIP_LITERAL_HEADER_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check name length against max dynamic table size
|
||||||
|
if (nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > dynamicTable.capacity()) {
|
||||||
|
dynamicTable.clear();
|
||||||
|
name = EMPTY;
|
||||||
|
skipLength = nameLength;
|
||||||
|
state = State.SKIP_LITERAL_HEADER_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = State.READ_LITERAL_HEADER_NAME;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_NAME_LENGTH:
|
||||||
|
// Header Name is a Literal String
|
||||||
|
nameLength = decodeULE128(in);
|
||||||
|
if (nameLength == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for numerical overflow
|
||||||
|
if (nameLength > Integer.MAX_VALUE - index) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
nameLength += index;
|
||||||
|
|
||||||
|
// Check name length against max header size
|
||||||
|
if (exceedsMaxHeaderSize(nameLength)) {
|
||||||
|
if (indexType == IndexType.NONE) {
|
||||||
|
// Name is unused so skip bytes
|
||||||
|
name = EMPTY;
|
||||||
|
skipLength = nameLength;
|
||||||
|
state = State.SKIP_LITERAL_HEADER_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check name length against max dynamic table size
|
||||||
|
if (nameLength + HeaderField.HEADER_ENTRY_OVERHEAD > dynamicTable.capacity()) {
|
||||||
|
dynamicTable.clear();
|
||||||
|
name = EMPTY;
|
||||||
|
skipLength = nameLength;
|
||||||
|
state = State.SKIP_LITERAL_HEADER_NAME;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = State.READ_LITERAL_HEADER_NAME;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_NAME:
|
||||||
|
// Wait until entire name is readable
|
||||||
|
if (in.available() < nameLength) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = readStringLiteral(in, nameLength);
|
||||||
|
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SKIP_LITERAL_HEADER_NAME:
|
||||||
|
skipLength -= in.skip(skipLength);
|
||||||
|
|
||||||
|
if (skipLength == 0) {
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_VALUE_LENGTH_PREFIX:
|
||||||
|
b = (byte) in.read();
|
||||||
|
huffmanEncoded = (b & 0x80) == 0x80;
|
||||||
|
index = b & 0x7F;
|
||||||
|
if (index == 0x7f) {
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE_LENGTH;
|
||||||
|
} else {
|
||||||
|
valueLength = index;
|
||||||
|
|
||||||
|
// Check new header size against max header size
|
||||||
|
long newHeaderSize = (long) nameLength + (long) valueLength;
|
||||||
|
if (exceedsMaxHeaderSize(newHeaderSize)) {
|
||||||
|
// truncation will be reported during endHeaderBlock
|
||||||
|
headerSize = maxHeaderSize + 1;
|
||||||
|
|
||||||
|
if (indexType == IndexType.NONE) {
|
||||||
|
// Value is unused so skip bytes
|
||||||
|
state = State.SKIP_LITERAL_HEADER_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new header size against max dynamic table size
|
||||||
|
if (newHeaderSize + HeaderField.HEADER_ENTRY_OVERHEAD > dynamicTable.capacity()) {
|
||||||
|
dynamicTable.clear();
|
||||||
|
state = State.SKIP_LITERAL_HEADER_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueLength == 0) {
|
||||||
|
insertHeader(headerListener, name, EMPTY, indexType);
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
} else {
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_VALUE_LENGTH:
|
||||||
|
// Header Value is a Literal String
|
||||||
|
valueLength = decodeULE128(in);
|
||||||
|
if (valueLength == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for numerical overflow
|
||||||
|
if (valueLength > Integer.MAX_VALUE - index) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
valueLength += index;
|
||||||
|
|
||||||
|
// Check new header size against max header size
|
||||||
|
long newHeaderSize = (long) nameLength + (long) valueLength;
|
||||||
|
if (newHeaderSize + headerSize > maxHeaderSize) {
|
||||||
|
// truncation will be reported during endHeaderBlock
|
||||||
|
headerSize = maxHeaderSize + 1;
|
||||||
|
|
||||||
|
if (indexType == IndexType.NONE) {
|
||||||
|
// Value is unused so skip bytes
|
||||||
|
state = State.SKIP_LITERAL_HEADER_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check new header size against max dynamic table size
|
||||||
|
if (newHeaderSize + HeaderField.HEADER_ENTRY_OVERHEAD > dynamicTable.capacity()) {
|
||||||
|
dynamicTable.clear();
|
||||||
|
state = State.SKIP_LITERAL_HEADER_VALUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = State.READ_LITERAL_HEADER_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_LITERAL_HEADER_VALUE:
|
||||||
|
// Wait until entire value is readable
|
||||||
|
if (in.available() < valueLength) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] value = readStringLiteral(in, valueLength);
|
||||||
|
insertHeader(headerListener, name, value, indexType);
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SKIP_LITERAL_HEADER_VALUE:
|
||||||
|
valueLength -= in.skip(valueLength);
|
||||||
|
|
||||||
|
if (valueLength == 0) {
|
||||||
|
state = State.READ_HEADER_REPRESENTATION;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("should not reach here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* End the current header block. Returns if the header field has been truncated. This must be
|
||||||
|
* called after the header block has been completely decoded.
|
||||||
|
*/
|
||||||
|
public boolean endHeaderBlock() {
|
||||||
|
boolean truncated = headerSize > maxHeaderSize;
|
||||||
|
reset();
|
||||||
|
return truncated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum table size. If this is below the maximum size of the dynamic table used by
|
||||||
|
* the encoder, the beginning of the next header block MUST signal this change.
|
||||||
|
*/
|
||||||
|
public void setMaxHeaderTableSize(int maxHeaderTableSize) {
|
||||||
|
maxDynamicTableSize = maxHeaderTableSize;
|
||||||
|
if (maxDynamicTableSize < encoderMaxDynamicTableSize) {
|
||||||
|
// decoder requires less space than encoder
|
||||||
|
// encoder MUST signal this change
|
||||||
|
maxDynamicTableSizeChangeRequired = true;
|
||||||
|
dynamicTable.setCapacity(maxDynamicTableSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the maximum table size. This is the maximum size allowed by both the encoder and the
|
||||||
|
* decoder.
|
||||||
|
*/
|
||||||
|
public int getMaxHeaderTableSize() {
|
||||||
|
return dynamicTable.capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of header fields in the dynamic table. Exposed for testing.
|
||||||
|
*/
|
||||||
|
int length() {
|
||||||
|
return dynamicTable.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of the dynamic table. Exposed for testing.
|
||||||
|
*/
|
||||||
|
int size() {
|
||||||
|
return dynamicTable.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the header field at the given index. Exposed for testing.
|
||||||
|
*/
|
||||||
|
HeaderField getHeaderField(int index) {
|
||||||
|
return dynamicTable.getEntry(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDynamicTableSize(int dynamicTableSize) throws IOException {
|
||||||
|
if (dynamicTableSize > maxDynamicTableSize) {
|
||||||
|
throw INVALID_MAX_DYNAMIC_TABLE_SIZE;
|
||||||
|
}
|
||||||
|
encoderMaxDynamicTableSize = dynamicTableSize;
|
||||||
|
maxDynamicTableSizeChangeRequired = false;
|
||||||
|
dynamicTable.setCapacity(dynamicTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readName(int index) throws IOException {
|
||||||
|
if (index <= StaticTable.length) {
|
||||||
|
HeaderField headerField = StaticTable.getEntry(index);
|
||||||
|
name = headerField.name;
|
||||||
|
} else if (index - StaticTable.length <= dynamicTable.length()) {
|
||||||
|
HeaderField headerField = dynamicTable.getEntry(index - StaticTable.length);
|
||||||
|
name = headerField.name;
|
||||||
|
} else {
|
||||||
|
throw ILLEGAL_INDEX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void indexHeader(int index, HeaderListener headerListener) throws IOException {
|
||||||
|
if (index <= StaticTable.length) {
|
||||||
|
HeaderField headerField = StaticTable.getEntry(index);
|
||||||
|
addHeader(headerListener, headerField.name, headerField.value, false);
|
||||||
|
} else if (index - StaticTable.length <= dynamicTable.length()) {
|
||||||
|
HeaderField headerField = dynamicTable.getEntry(index - StaticTable.length);
|
||||||
|
addHeader(headerListener, headerField.name, headerField.value, false);
|
||||||
|
} else {
|
||||||
|
throw ILLEGAL_INDEX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertHeader(HeaderListener headerListener, byte[] name, byte[] value,
|
||||||
|
IndexType indexType) {
|
||||||
|
addHeader(headerListener, name, value, indexType == IndexType.NEVER);
|
||||||
|
|
||||||
|
switch (indexType) {
|
||||||
|
case NONE:
|
||||||
|
case NEVER:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INCREMENTAL:
|
||||||
|
dynamicTable.add(new HeaderField(name, value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("should not reach here");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addHeader(HeaderListener headerListener, byte[] name, byte[] value,
|
||||||
|
boolean sensitive) {
|
||||||
|
if (name.length == 0) {
|
||||||
|
throw new AssertionError("name is empty");
|
||||||
|
}
|
||||||
|
long newSize = headerSize + name.length + value.length;
|
||||||
|
if (newSize <= maxHeaderSize) {
|
||||||
|
headerListener.addHeader(name, value, sensitive);
|
||||||
|
headerSize = (int) newSize;
|
||||||
|
} else {
|
||||||
|
// truncation will be reported during endHeaderBlock
|
||||||
|
headerSize = maxHeaderSize + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean exceedsMaxHeaderSize(long size) {
|
||||||
|
// Check new header size against max header size
|
||||||
|
if (size + headerSize <= maxHeaderSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// truncation will be reported during endHeaderBlock
|
||||||
|
headerSize = maxHeaderSize + 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readStringLiteral(InputStream in, int length) throws IOException {
|
||||||
|
byte[] buf = new byte[length];
|
||||||
|
if (in.read(buf) != length) {
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (huffmanEncoded) {
|
||||||
|
return Huffman.DECODER.decode(buf);
|
||||||
|
} else {
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsigned Little Endian Base 128 Variable-Length Integer Encoding
|
||||||
|
private static int decodeULE128(InputStream in) throws IOException {
|
||||||
|
in.mark(5);
|
||||||
|
int result = 0;
|
||||||
|
int shift = 0;
|
||||||
|
while (shift < 32) {
|
||||||
|
if (in.available() == 0) {
|
||||||
|
// Buffer does not contain entire integer,
|
||||||
|
// reset reader index and return -1.
|
||||||
|
in.reset();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
byte b = (byte) in.read();
|
||||||
|
if (shift == 28 && (b & 0xF8) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result |= (b & 0x7F) << shift;
|
||||||
|
if ((b & 0x80) == 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
// Value exceeds Integer.MAX_VALUE
|
||||||
|
in.reset();
|
||||||
|
throw DECOMPRESSION_EXCEPTION;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HeaderField.HEADER_ENTRY_OVERHEAD;
|
||||||
|
|
||||||
|
final class DynamicTable {
|
||||||
|
|
||||||
|
// a circular queue of header fields
|
||||||
|
HeaderField[] headerFields;
|
||||||
|
int head;
|
||||||
|
int tail;
|
||||||
|
private int size;
|
||||||
|
private int capacity = -1; // ensure setCapacity creates the array
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new dynamic table with the specified initial capacity.
|
||||||
|
*/
|
||||||
|
DynamicTable(int initialCapacity) {
|
||||||
|
setCapacity(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of header fields in the dynamic table.
|
||||||
|
*/
|
||||||
|
public int length() {
|
||||||
|
int length;
|
||||||
|
if (head < tail) {
|
||||||
|
length = headerFields.length - tail + head;
|
||||||
|
} else {
|
||||||
|
length = head - tail;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current size of the dynamic table. This is the sum of the size of the entries.
|
||||||
|
*/
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the maximum allowable size of the dynamic table.
|
||||||
|
*/
|
||||||
|
public int capacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the header field at the given index. The first and newest entry is always at index 1,
|
||||||
|
* and the oldest entry is at the index length().
|
||||||
|
*/
|
||||||
|
public HeaderField getEntry(int index) {
|
||||||
|
if (index <= 0 || index > length()) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
int i = head - index;
|
||||||
|
if (i < 0) {
|
||||||
|
return headerFields[i + headerFields.length];
|
||||||
|
} else {
|
||||||
|
return headerFields[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the header field to the dynamic table. Entries are evicted from the dynamic table until
|
||||||
|
* the size of the table and the new header field is less than or equal to the table's capacity.
|
||||||
|
* If the size of the new entry is larger than the table's capacity, the dynamic table will be
|
||||||
|
* cleared.
|
||||||
|
*/
|
||||||
|
public void add(HeaderField header) {
|
||||||
|
int headerSize = header.size();
|
||||||
|
if (headerSize > capacity) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (size + headerSize > capacity) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
headerFields[head++] = header;
|
||||||
|
size += header.size();
|
||||||
|
if (head == headerFields.length) {
|
||||||
|
head = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove and return the oldest header field from the dynamic table.
|
||||||
|
*/
|
||||||
|
public HeaderField remove() {
|
||||||
|
HeaderField removed = headerFields[tail];
|
||||||
|
if (removed == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
size -= removed.size();
|
||||||
|
headerFields[tail++] = null;
|
||||||
|
if (tail == headerFields.length) {
|
||||||
|
tail = 0;
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all entries from the dynamic table.
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
while (tail != head) {
|
||||||
|
headerFields[tail++] = null;
|
||||||
|
if (tail == headerFields.length) {
|
||||||
|
tail = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
head = 0;
|
||||||
|
tail = 0;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum size of the dynamic table. Entries are evicted from the dynamic table until
|
||||||
|
* the size of the table is less than or equal to the maximum size.
|
||||||
|
*/
|
||||||
|
public void setCapacity(int capacity) {
|
||||||
|
if (capacity < 0) {
|
||||||
|
throw new IllegalArgumentException("Illegal Capacity: " + capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initially capacity will be -1 so init won't return here
|
||||||
|
if (this.capacity == capacity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.capacity = capacity;
|
||||||
|
|
||||||
|
if (capacity == 0) {
|
||||||
|
clear();
|
||||||
|
} else {
|
||||||
|
// initially size will be 0 so remove won't be called
|
||||||
|
while (size > capacity) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int maxEntries = capacity / HEADER_ENTRY_OVERHEAD;
|
||||||
|
if (capacity % HEADER_ENTRY_OVERHEAD != 0) {
|
||||||
|
maxEntries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if capacity change requires us to reallocate the array
|
||||||
|
if (headerFields != null && headerFields.length == maxEntries) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderField[] tmp = new HeaderField[maxEntries];
|
||||||
|
|
||||||
|
// initially length will be 0 so there will be no copy
|
||||||
|
int len = length();
|
||||||
|
int cursor = tail;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
HeaderField entry = headerFields[cursor++];
|
||||||
|
tmp[i] = entry;
|
||||||
|
if (cursor == headerFields.length) {
|
||||||
|
cursor = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tail = 0;
|
||||||
|
this.head = tail + len;
|
||||||
|
this.headerFields = tmp;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,469 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http2.hpack.HpackUtil.IndexType;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public final class Encoder {
|
||||||
|
|
||||||
|
private static final int BUCKET_SIZE = 17;
|
||||||
|
private static final byte[] EMPTY = {};
|
||||||
|
|
||||||
|
// for testing
|
||||||
|
private final boolean useIndexing;
|
||||||
|
private final boolean forceHuffmanOn;
|
||||||
|
private final boolean forceHuffmanOff;
|
||||||
|
|
||||||
|
// a linked hash map of header fields
|
||||||
|
private final HeaderEntry[] headerFields = new HeaderEntry[BUCKET_SIZE];
|
||||||
|
private final HeaderEntry head = new HeaderEntry(-1, EMPTY, EMPTY, Integer.MAX_VALUE, null);
|
||||||
|
private int size;
|
||||||
|
private int capacity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new encoder.
|
||||||
|
*/
|
||||||
|
public Encoder(int maxHeaderTableSize) {
|
||||||
|
this(maxHeaderTableSize, true, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for testing only.
|
||||||
|
*/
|
||||||
|
Encoder(
|
||||||
|
int maxHeaderTableSize,
|
||||||
|
boolean useIndexing,
|
||||||
|
boolean forceHuffmanOn,
|
||||||
|
boolean forceHuffmanOff
|
||||||
|
) {
|
||||||
|
if (maxHeaderTableSize < 0) {
|
||||||
|
throw new IllegalArgumentException("Illegal Capacity: " + maxHeaderTableSize);
|
||||||
|
}
|
||||||
|
this.useIndexing = useIndexing;
|
||||||
|
this.forceHuffmanOn = forceHuffmanOn;
|
||||||
|
this.forceHuffmanOff = forceHuffmanOff;
|
||||||
|
this.capacity = maxHeaderTableSize;
|
||||||
|
head.before = head.after = head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode the header field into the header block.
|
||||||
|
*/
|
||||||
|
public void encodeHeader(OutputStream out, byte[] name, byte[] value, boolean sensitive)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
// If the header value is sensitive then it must never be indexed
|
||||||
|
if (sensitive) {
|
||||||
|
int nameIndex = getNameIndex(name);
|
||||||
|
encodeLiteral(out, name, value, IndexType.NEVER, nameIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the peer will only use the static table
|
||||||
|
if (capacity == 0) {
|
||||||
|
int staticTableIndex = StaticTable.getIndex(name, value);
|
||||||
|
if (staticTableIndex == -1) {
|
||||||
|
int nameIndex = StaticTable.getIndex(name);
|
||||||
|
encodeLiteral(out, name, value, IndexType.NONE, nameIndex);
|
||||||
|
} else {
|
||||||
|
encodeInteger(out, 0x80, 7, staticTableIndex);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int headerSize = HeaderField.sizeOf(name, value);
|
||||||
|
|
||||||
|
// If the headerSize is greater than the max table size then it must be encoded literally
|
||||||
|
if (headerSize > capacity) {
|
||||||
|
int nameIndex = getNameIndex(name);
|
||||||
|
encodeLiteral(out, name, value, IndexType.NONE, nameIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderEntry headerField = getEntry(name, value);
|
||||||
|
if (headerField != null) {
|
||||||
|
int index = getIndex(headerField.index) + StaticTable.length;
|
||||||
|
// Section 6.1. Indexed Header Field Representation
|
||||||
|
encodeInteger(out, 0x80, 7, index);
|
||||||
|
} else {
|
||||||
|
int staticTableIndex = StaticTable.getIndex(name, value);
|
||||||
|
if (staticTableIndex != -1) {
|
||||||
|
// Section 6.1. Indexed Header Field Representation
|
||||||
|
encodeInteger(out, 0x80, 7, staticTableIndex);
|
||||||
|
} else {
|
||||||
|
int nameIndex = getNameIndex(name);
|
||||||
|
if (useIndexing) {
|
||||||
|
ensureCapacity(headerSize);
|
||||||
|
}
|
||||||
|
IndexType indexType = useIndexing ? IndexType.INCREMENTAL : IndexType.NONE;
|
||||||
|
encodeLiteral(out, name, value, indexType, nameIndex);
|
||||||
|
if (useIndexing) {
|
||||||
|
add(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the maximum table size.
|
||||||
|
*/
|
||||||
|
public void setMaxHeaderTableSize(OutputStream out, int maxHeaderTableSize) throws IOException {
|
||||||
|
if (maxHeaderTableSize < 0) {
|
||||||
|
throw new IllegalArgumentException("Illegal Capacity: " + maxHeaderTableSize);
|
||||||
|
}
|
||||||
|
if (capacity == maxHeaderTableSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
capacity = maxHeaderTableSize;
|
||||||
|
ensureCapacity(0);
|
||||||
|
encodeInteger(out, 0x20, 5, maxHeaderTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the maximum table size.
|
||||||
|
*/
|
||||||
|
public int getMaxHeaderTableSize() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode integer according to Section 5.1.
|
||||||
|
*/
|
||||||
|
private static void encodeInteger(OutputStream out, int mask, int n, int i) throws IOException {
|
||||||
|
if (n < 0 || n > 8) {
|
||||||
|
throw new IllegalArgumentException("N: " + n);
|
||||||
|
}
|
||||||
|
int nbits = 0xFF >>> (8 - n);
|
||||||
|
if (i < nbits) {
|
||||||
|
out.write(mask | i);
|
||||||
|
} else {
|
||||||
|
out.write(mask | nbits);
|
||||||
|
int length = i - nbits;
|
||||||
|
while (true) {
|
||||||
|
if ((length & ~0x7F) == 0) {
|
||||||
|
out.write(length);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
out.write((length & 0x7F) | 0x80);
|
||||||
|
length >>>= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode string literal according to Section 5.2.
|
||||||
|
*/
|
||||||
|
private void encodeStringLiteral(OutputStream out, byte[] string) throws IOException {
|
||||||
|
int huffmanLength = Huffman.ENCODER.getEncodedLength(string);
|
||||||
|
if ((huffmanLength < string.length && !forceHuffmanOff) || forceHuffmanOn) {
|
||||||
|
encodeInteger(out, 0x80, 7, huffmanLength);
|
||||||
|
Huffman.ENCODER.encode(out, string);
|
||||||
|
} else {
|
||||||
|
encodeInteger(out, 0x00, 7, string.length);
|
||||||
|
out.write(string, 0, string.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode literal header field according to Section 6.2.
|
||||||
|
*/
|
||||||
|
private void encodeLiteral(OutputStream out, byte[] name, byte[] value, IndexType indexType,
|
||||||
|
int nameIndex)
|
||||||
|
throws IOException {
|
||||||
|
int mask;
|
||||||
|
int prefixBits;
|
||||||
|
switch (indexType) {
|
||||||
|
case INCREMENTAL:
|
||||||
|
mask = 0x40;
|
||||||
|
prefixBits = 6;
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
mask = 0x00;
|
||||||
|
prefixBits = 4;
|
||||||
|
break;
|
||||||
|
case NEVER:
|
||||||
|
mask = 0x10;
|
||||||
|
prefixBits = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("should not reach here");
|
||||||
|
}
|
||||||
|
encodeInteger(out, mask, prefixBits, nameIndex == -1 ? 0 : nameIndex);
|
||||||
|
if (nameIndex == -1) {
|
||||||
|
encodeStringLiteral(out, name);
|
||||||
|
}
|
||||||
|
encodeStringLiteral(out, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getNameIndex(byte[] name) {
|
||||||
|
int index = StaticTable.getIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
index = getIndex(name);
|
||||||
|
if (index >= 0) {
|
||||||
|
index += StaticTable.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that the dynamic table has enough room to hold 'headerSize' more bytes. Removes the
|
||||||
|
* oldest entry from the dynamic table until sufficient space is available.
|
||||||
|
*/
|
||||||
|
private void ensureCapacity(int headerSize) throws IOException {
|
||||||
|
while (size + headerSize > capacity) {
|
||||||
|
int index = length();
|
||||||
|
if (index == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of header fields in the dynamic table. Exposed for testing.
|
||||||
|
*/
|
||||||
|
int length() {
|
||||||
|
return size == 0 ? 0 : head.after.index - head.before.index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of the dynamic table. Exposed for testing.
|
||||||
|
*/
|
||||||
|
int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the header field at the given index. Exposed for testing.
|
||||||
|
*/
|
||||||
|
HeaderField getHeaderField(int index) {
|
||||||
|
HeaderEntry entry = head;
|
||||||
|
while (index-- >= 0) {
|
||||||
|
entry = entry.before;
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the header entry with the lowest index value for the header field. Returns null if
|
||||||
|
* header field is not in the dynamic table.
|
||||||
|
*/
|
||||||
|
private HeaderEntry getEntry(byte[] name, byte[] value) {
|
||||||
|
if (length() == 0 || name == null || value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int h = hash(name);
|
||||||
|
int i = index(h);
|
||||||
|
for (HeaderEntry e = headerFields[i]; e != null; e = e.next) {
|
||||||
|
if (e.hash == h &&
|
||||||
|
HpackUtil.equals(name, e.name) &&
|
||||||
|
HpackUtil.equals(value, e.value)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lowest index value for the header field name in the dynamic table. Returns -1 if
|
||||||
|
* the header field name is not in the dynamic table.
|
||||||
|
*/
|
||||||
|
private int getIndex(byte[] name) {
|
||||||
|
if (length() == 0 || name == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int h = hash(name);
|
||||||
|
int i = index(h);
|
||||||
|
int index = -1;
|
||||||
|
for (HeaderEntry e = headerFields[i]; e != null; e = e.next) {
|
||||||
|
if (e.hash == h && HpackUtil.equals(name, e.name)) {
|
||||||
|
index = e.index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the index into the dynamic table given the index in the header entry.
|
||||||
|
*/
|
||||||
|
private int getIndex(int index) {
|
||||||
|
if (index == -1) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return index - head.before.index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the header field to the dynamic table. Entries are evicted from the dynamic table until
|
||||||
|
* the size of the table and the new header field is less than the table's capacity. If the size
|
||||||
|
* of the new entry is larger than the table's capacity, the dynamic table will be cleared.
|
||||||
|
*/
|
||||||
|
private void add(byte[] name, byte[] value) {
|
||||||
|
int headerSize = HeaderField.sizeOf(name, value);
|
||||||
|
|
||||||
|
// Clear the table if the header field size is larger than the capacity.
|
||||||
|
if (headerSize > capacity) {
|
||||||
|
clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evict oldest entries until we have enough capacity.
|
||||||
|
while (size + headerSize > capacity) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy name and value that modifications of original do not affect the dynamic table.
|
||||||
|
name = Arrays.copyOf(name, name.length);
|
||||||
|
value = Arrays.copyOf(value, value.length);
|
||||||
|
|
||||||
|
int h = hash(name);
|
||||||
|
int i = index(h);
|
||||||
|
HeaderEntry old = headerFields[i];
|
||||||
|
HeaderEntry e = new HeaderEntry(h, name, value, head.before.index - 1, old);
|
||||||
|
headerFields[i] = e;
|
||||||
|
e.addBefore(head);
|
||||||
|
size += headerSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove and return the oldest header field from the dynamic table.
|
||||||
|
*/
|
||||||
|
private HeaderField remove() {
|
||||||
|
if (size == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
HeaderEntry eldest = head.after;
|
||||||
|
int h = eldest.hash;
|
||||||
|
int i = index(h);
|
||||||
|
HeaderEntry prev = headerFields[i];
|
||||||
|
HeaderEntry e = prev;
|
||||||
|
while (e != null) {
|
||||||
|
HeaderEntry next = e.next;
|
||||||
|
if (e == eldest) {
|
||||||
|
if (prev == eldest) {
|
||||||
|
headerFields[i] = next;
|
||||||
|
} else {
|
||||||
|
prev.next = next;
|
||||||
|
}
|
||||||
|
eldest.remove();
|
||||||
|
size -= eldest.size();
|
||||||
|
return eldest;
|
||||||
|
}
|
||||||
|
prev = e;
|
||||||
|
e = next;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all entries from the dynamic table.
|
||||||
|
*/
|
||||||
|
private void clear() {
|
||||||
|
Arrays.fill(headerFields, null);
|
||||||
|
head.before = head.after = head;
|
||||||
|
this.size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hash code for the given header field name.
|
||||||
|
*/
|
||||||
|
private static int hash(byte[] name) {
|
||||||
|
int h = 0;
|
||||||
|
for (int i = 0; i < name.length; i++) {
|
||||||
|
h = 31 * h + name[i];
|
||||||
|
}
|
||||||
|
if (h > 0) {
|
||||||
|
return h;
|
||||||
|
} else if (h == Integer.MIN_VALUE) {
|
||||||
|
return Integer.MAX_VALUE;
|
||||||
|
} else {
|
||||||
|
return -h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index into the hash table for the hash code h.
|
||||||
|
*/
|
||||||
|
private static int index(int h) {
|
||||||
|
return h % BUCKET_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A linked hash map HeaderField entry.
|
||||||
|
*/
|
||||||
|
private static class HeaderEntry extends HeaderField {
|
||||||
|
// These fields comprise the doubly linked list used for iteration.
|
||||||
|
HeaderEntry before, after;
|
||||||
|
|
||||||
|
// These fields comprise the chained list for header fields with the same hash.
|
||||||
|
HeaderEntry next;
|
||||||
|
int hash;
|
||||||
|
|
||||||
|
// This is used to compute the index in the dynamic table.
|
||||||
|
int index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new entry.
|
||||||
|
*/
|
||||||
|
HeaderEntry(int hash, byte[] name, byte[] value, int index, HeaderEntry next) {
|
||||||
|
super(name, value);
|
||||||
|
this.index = index;
|
||||||
|
this.hash = hash;
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes this entry from the linked list.
|
||||||
|
*/
|
||||||
|
private void remove() {
|
||||||
|
before.after = after;
|
||||||
|
after.before = before;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts this entry before the specified existing entry in the list.
|
||||||
|
*/
|
||||||
|
private void addBefore(HeaderEntry existingEntry) {
|
||||||
|
after = existingEntry;
|
||||||
|
before = existingEntry.before;
|
||||||
|
before.after = this;
|
||||||
|
after.before = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HpackUtil.ISO_8859_1;
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HpackUtil.requireNonNull;
|
||||||
|
|
||||||
|
class HeaderField implements Comparable<HeaderField> {
|
||||||
|
|
||||||
|
// Section 4.1. Calculating Table Size
|
||||||
|
// The additional 32 octets account for an estimated
|
||||||
|
// overhead associated with the structure.
|
||||||
|
static final int HEADER_ENTRY_OVERHEAD = 32;
|
||||||
|
|
||||||
|
static int sizeOf(byte[] name, byte[] value) {
|
||||||
|
return name.length + value.length + HEADER_ENTRY_OVERHEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] name;
|
||||||
|
final byte[] value;
|
||||||
|
|
||||||
|
// This constructor can only be used if name and value are ISO-8859-1 encoded.
|
||||||
|
HeaderField(String name, String value) {
|
||||||
|
this(name.getBytes(ISO_8859_1), value.getBytes(ISO_8859_1));
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderField(byte[] name, byte[] value) {
|
||||||
|
this.name = requireNonNull(name);
|
||||||
|
this.value = requireNonNull(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() {
|
||||||
|
return name.length + value.length + HEADER_ENTRY_OVERHEAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
// TODO(nmittler): Netty's build rules require this. Probably need a better implementation.
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(HeaderField anotherHeaderField) {
|
||||||
|
int ret = compareTo(name, anotherHeaderField.name);
|
||||||
|
if (ret == 0) {
|
||||||
|
ret = compareTo(value, anotherHeaderField.value);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int compareTo(byte[] s1, byte[] s2) {
|
||||||
|
int len1 = s1.length;
|
||||||
|
int len2 = s2.length;
|
||||||
|
int lim = Math.min(len1, len2);
|
||||||
|
|
||||||
|
int k = 0;
|
||||||
|
while (k < lim) {
|
||||||
|
byte b1 = s1[k];
|
||||||
|
byte b2 = s2[k];
|
||||||
|
if (b1 != b2) {
|
||||||
|
return b1 - b2;
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
return len1 - len2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof HeaderField)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HeaderField other = (HeaderField) obj;
|
||||||
|
boolean nameEquals = HpackUtil.equals(name, other.name);
|
||||||
|
boolean valueEquals = HpackUtil.equals(value, other.value);
|
||||||
|
return nameEquals && valueEquals;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String nameString = new String(name);
|
||||||
|
String valueString = new String(value);
|
||||||
|
return nameString + ": " + valueString;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
public interface HeaderListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* emitHeader is called by the decoder during header field emission.
|
||||||
|
* The name and value byte arrays must not be modified.
|
||||||
|
*/
|
||||||
|
void addHeader(byte[] name, byte[] value, boolean sensitive);
|
||||||
|
}
|
@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
final class HpackUtil {
|
||||||
|
|
||||||
|
static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A string compare that doesn't leak timing information.
|
||||||
|
*/
|
||||||
|
static boolean equals(byte[] s1, byte[] s2) {
|
||||||
|
if (s1.length != s2.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char c = 0;
|
||||||
|
for (int i = 0; i < s1.length; i++) {
|
||||||
|
c |= s1[i] ^ s2[i];
|
||||||
|
}
|
||||||
|
return c == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the specified object reference is not {@code null}.
|
||||||
|
*/
|
||||||
|
static <T> T requireNonNull(T obj) {
|
||||||
|
if (obj == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 6.2. Literal Header Field Representation
|
||||||
|
enum IndexType {
|
||||||
|
INCREMENTAL, // Section 6.2.1. Literal Header Field with Incremental Indexing
|
||||||
|
NONE, // Section 6.2.2. Literal Header Field without Indexing
|
||||||
|
NEVER // Section 6.2.3. Literal Header Field never Indexed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Appendix B: Huffman Codes
|
||||||
|
// http://tools.ietf.org/html/rfc7541#appendix-B
|
||||||
|
static final int[] HUFFMAN_CODES = {
|
||||||
|
0x1ff8,
|
||||||
|
0x7fffd8,
|
||||||
|
0xfffffe2,
|
||||||
|
0xfffffe3,
|
||||||
|
0xfffffe4,
|
||||||
|
0xfffffe5,
|
||||||
|
0xfffffe6,
|
||||||
|
0xfffffe7,
|
||||||
|
0xfffffe8,
|
||||||
|
0xffffea,
|
||||||
|
0x3ffffffc,
|
||||||
|
0xfffffe9,
|
||||||
|
0xfffffea,
|
||||||
|
0x3ffffffd,
|
||||||
|
0xfffffeb,
|
||||||
|
0xfffffec,
|
||||||
|
0xfffffed,
|
||||||
|
0xfffffee,
|
||||||
|
0xfffffef,
|
||||||
|
0xffffff0,
|
||||||
|
0xffffff1,
|
||||||
|
0xffffff2,
|
||||||
|
0x3ffffffe,
|
||||||
|
0xffffff3,
|
||||||
|
0xffffff4,
|
||||||
|
0xffffff5,
|
||||||
|
0xffffff6,
|
||||||
|
0xffffff7,
|
||||||
|
0xffffff8,
|
||||||
|
0xffffff9,
|
||||||
|
0xffffffa,
|
||||||
|
0xffffffb,
|
||||||
|
0x14,
|
||||||
|
0x3f8,
|
||||||
|
0x3f9,
|
||||||
|
0xffa,
|
||||||
|
0x1ff9,
|
||||||
|
0x15,
|
||||||
|
0xf8,
|
||||||
|
0x7fa,
|
||||||
|
0x3fa,
|
||||||
|
0x3fb,
|
||||||
|
0xf9,
|
||||||
|
0x7fb,
|
||||||
|
0xfa,
|
||||||
|
0x16,
|
||||||
|
0x17,
|
||||||
|
0x18,
|
||||||
|
0x0,
|
||||||
|
0x1,
|
||||||
|
0x2,
|
||||||
|
0x19,
|
||||||
|
0x1a,
|
||||||
|
0x1b,
|
||||||
|
0x1c,
|
||||||
|
0x1d,
|
||||||
|
0x1e,
|
||||||
|
0x1f,
|
||||||
|
0x5c,
|
||||||
|
0xfb,
|
||||||
|
0x7ffc,
|
||||||
|
0x20,
|
||||||
|
0xffb,
|
||||||
|
0x3fc,
|
||||||
|
0x1ffa,
|
||||||
|
0x21,
|
||||||
|
0x5d,
|
||||||
|
0x5e,
|
||||||
|
0x5f,
|
||||||
|
0x60,
|
||||||
|
0x61,
|
||||||
|
0x62,
|
||||||
|
0x63,
|
||||||
|
0x64,
|
||||||
|
0x65,
|
||||||
|
0x66,
|
||||||
|
0x67,
|
||||||
|
0x68,
|
||||||
|
0x69,
|
||||||
|
0x6a,
|
||||||
|
0x6b,
|
||||||
|
0x6c,
|
||||||
|
0x6d,
|
||||||
|
0x6e,
|
||||||
|
0x6f,
|
||||||
|
0x70,
|
||||||
|
0x71,
|
||||||
|
0x72,
|
||||||
|
0xfc,
|
||||||
|
0x73,
|
||||||
|
0xfd,
|
||||||
|
0x1ffb,
|
||||||
|
0x7fff0,
|
||||||
|
0x1ffc,
|
||||||
|
0x3ffc,
|
||||||
|
0x22,
|
||||||
|
0x7ffd,
|
||||||
|
0x3,
|
||||||
|
0x23,
|
||||||
|
0x4,
|
||||||
|
0x24,
|
||||||
|
0x5,
|
||||||
|
0x25,
|
||||||
|
0x26,
|
||||||
|
0x27,
|
||||||
|
0x6,
|
||||||
|
0x74,
|
||||||
|
0x75,
|
||||||
|
0x28,
|
||||||
|
0x29,
|
||||||
|
0x2a,
|
||||||
|
0x7,
|
||||||
|
0x2b,
|
||||||
|
0x76,
|
||||||
|
0x2c,
|
||||||
|
0x8,
|
||||||
|
0x9,
|
||||||
|
0x2d,
|
||||||
|
0x77,
|
||||||
|
0x78,
|
||||||
|
0x79,
|
||||||
|
0x7a,
|
||||||
|
0x7b,
|
||||||
|
0x7ffe,
|
||||||
|
0x7fc,
|
||||||
|
0x3ffd,
|
||||||
|
0x1ffd,
|
||||||
|
0xffffffc,
|
||||||
|
0xfffe6,
|
||||||
|
0x3fffd2,
|
||||||
|
0xfffe7,
|
||||||
|
0xfffe8,
|
||||||
|
0x3fffd3,
|
||||||
|
0x3fffd4,
|
||||||
|
0x3fffd5,
|
||||||
|
0x7fffd9,
|
||||||
|
0x3fffd6,
|
||||||
|
0x7fffda,
|
||||||
|
0x7fffdb,
|
||||||
|
0x7fffdc,
|
||||||
|
0x7fffdd,
|
||||||
|
0x7fffde,
|
||||||
|
0xffffeb,
|
||||||
|
0x7fffdf,
|
||||||
|
0xffffec,
|
||||||
|
0xffffed,
|
||||||
|
0x3fffd7,
|
||||||
|
0x7fffe0,
|
||||||
|
0xffffee,
|
||||||
|
0x7fffe1,
|
||||||
|
0x7fffe2,
|
||||||
|
0x7fffe3,
|
||||||
|
0x7fffe4,
|
||||||
|
0x1fffdc,
|
||||||
|
0x3fffd8,
|
||||||
|
0x7fffe5,
|
||||||
|
0x3fffd9,
|
||||||
|
0x7fffe6,
|
||||||
|
0x7fffe7,
|
||||||
|
0xffffef,
|
||||||
|
0x3fffda,
|
||||||
|
0x1fffdd,
|
||||||
|
0xfffe9,
|
||||||
|
0x3fffdb,
|
||||||
|
0x3fffdc,
|
||||||
|
0x7fffe8,
|
||||||
|
0x7fffe9,
|
||||||
|
0x1fffde,
|
||||||
|
0x7fffea,
|
||||||
|
0x3fffdd,
|
||||||
|
0x3fffde,
|
||||||
|
0xfffff0,
|
||||||
|
0x1fffdf,
|
||||||
|
0x3fffdf,
|
||||||
|
0x7fffeb,
|
||||||
|
0x7fffec,
|
||||||
|
0x1fffe0,
|
||||||
|
0x1fffe1,
|
||||||
|
0x3fffe0,
|
||||||
|
0x1fffe2,
|
||||||
|
0x7fffed,
|
||||||
|
0x3fffe1,
|
||||||
|
0x7fffee,
|
||||||
|
0x7fffef,
|
||||||
|
0xfffea,
|
||||||
|
0x3fffe2,
|
||||||
|
0x3fffe3,
|
||||||
|
0x3fffe4,
|
||||||
|
0x7ffff0,
|
||||||
|
0x3fffe5,
|
||||||
|
0x3fffe6,
|
||||||
|
0x7ffff1,
|
||||||
|
0x3ffffe0,
|
||||||
|
0x3ffffe1,
|
||||||
|
0xfffeb,
|
||||||
|
0x7fff1,
|
||||||
|
0x3fffe7,
|
||||||
|
0x7ffff2,
|
||||||
|
0x3fffe8,
|
||||||
|
0x1ffffec,
|
||||||
|
0x3ffffe2,
|
||||||
|
0x3ffffe3,
|
||||||
|
0x3ffffe4,
|
||||||
|
0x7ffffde,
|
||||||
|
0x7ffffdf,
|
||||||
|
0x3ffffe5,
|
||||||
|
0xfffff1,
|
||||||
|
0x1ffffed,
|
||||||
|
0x7fff2,
|
||||||
|
0x1fffe3,
|
||||||
|
0x3ffffe6,
|
||||||
|
0x7ffffe0,
|
||||||
|
0x7ffffe1,
|
||||||
|
0x3ffffe7,
|
||||||
|
0x7ffffe2,
|
||||||
|
0xfffff2,
|
||||||
|
0x1fffe4,
|
||||||
|
0x1fffe5,
|
||||||
|
0x3ffffe8,
|
||||||
|
0x3ffffe9,
|
||||||
|
0xffffffd,
|
||||||
|
0x7ffffe3,
|
||||||
|
0x7ffffe4,
|
||||||
|
0x7ffffe5,
|
||||||
|
0xfffec,
|
||||||
|
0xfffff3,
|
||||||
|
0xfffed,
|
||||||
|
0x1fffe6,
|
||||||
|
0x3fffe9,
|
||||||
|
0x1fffe7,
|
||||||
|
0x1fffe8,
|
||||||
|
0x7ffff3,
|
||||||
|
0x3fffea,
|
||||||
|
0x3fffeb,
|
||||||
|
0x1ffffee,
|
||||||
|
0x1ffffef,
|
||||||
|
0xfffff4,
|
||||||
|
0xfffff5,
|
||||||
|
0x3ffffea,
|
||||||
|
0x7ffff4,
|
||||||
|
0x3ffffeb,
|
||||||
|
0x7ffffe6,
|
||||||
|
0x3ffffec,
|
||||||
|
0x3ffffed,
|
||||||
|
0x7ffffe7,
|
||||||
|
0x7ffffe8,
|
||||||
|
0x7ffffe9,
|
||||||
|
0x7ffffea,
|
||||||
|
0x7ffffeb,
|
||||||
|
0xffffffe,
|
||||||
|
0x7ffffec,
|
||||||
|
0x7ffffed,
|
||||||
|
0x7ffffee,
|
||||||
|
0x7ffffef,
|
||||||
|
0x7fffff0,
|
||||||
|
0x3ffffee,
|
||||||
|
0x3fffffff // EOS
|
||||||
|
};
|
||||||
|
|
||||||
|
static final byte[] HUFFMAN_CODE_LENGTHS = {
|
||||||
|
13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28,
|
||||||
|
28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28,
|
||||||
|
6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6,
|
||||||
|
5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10,
|
||||||
|
13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
||||||
|
7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6,
|
||||||
|
15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5,
|
||||||
|
6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28,
|
||||||
|
20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23,
|
||||||
|
24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24,
|
||||||
|
22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23,
|
||||||
|
21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23,
|
||||||
|
26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25,
|
||||||
|
19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27,
|
||||||
|
20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23,
|
||||||
|
26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26,
|
||||||
|
30 // EOS
|
||||||
|
};
|
||||||
|
|
||||||
|
static final int HUFFMAN_EOS = 256;
|
||||||
|
|
||||||
|
private HpackUtil() {
|
||||||
|
// utility class
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HpackUtil.HUFFMAN_CODES;
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HpackUtil.HUFFMAN_CODE_LENGTHS;
|
||||||
|
|
||||||
|
public final class Huffman {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Huffman Decoder
|
||||||
|
*/
|
||||||
|
public static final HuffmanDecoder DECODER =
|
||||||
|
new HuffmanDecoder(HUFFMAN_CODES, HUFFMAN_CODE_LENGTHS);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Huffman Encoder
|
||||||
|
*/
|
||||||
|
public static final HuffmanEncoder ENCODER =
|
||||||
|
new HuffmanEncoder(HUFFMAN_CODES, HUFFMAN_CODE_LENGTHS);
|
||||||
|
|
||||||
|
private Huffman() {
|
||||||
|
// utility class
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
final class HuffmanDecoder {
|
||||||
|
|
||||||
|
private static final IOException EOS_DECODED = new IOException("EOS Decoded");
|
||||||
|
private static final IOException INVALID_PADDING = new IOException("Invalid Padding");
|
||||||
|
|
||||||
|
private final Node root;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Huffman decoder with the specified Huffman coding.
|
||||||
|
*
|
||||||
|
* @param codes the Huffman codes indexed by symbol
|
||||||
|
* @param lengths the length of each Huffman code
|
||||||
|
*/
|
||||||
|
HuffmanDecoder(int[] codes, byte[] lengths) {
|
||||||
|
if (codes.length != 257 || codes.length != lengths.length) {
|
||||||
|
throw new IllegalArgumentException("invalid Huffman coding");
|
||||||
|
}
|
||||||
|
root = buildTree(codes, lengths);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses the given Huffman coded string literal.
|
||||||
|
*
|
||||||
|
* @param buf the string literal to be decoded
|
||||||
|
* @return the output stream for the compressed data
|
||||||
|
* @throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be
|
||||||
|
* thrown if the output stream has been closed.
|
||||||
|
*/
|
||||||
|
public byte[] decode(byte[] buf) throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
Node node = root;
|
||||||
|
int current = 0;
|
||||||
|
int bits = 0;
|
||||||
|
for (int i = 0; i < buf.length; i++) {
|
||||||
|
int b = buf[i] & 0xFF;
|
||||||
|
current = (current << 8) | b;
|
||||||
|
bits += 8;
|
||||||
|
while (bits >= 8) {
|
||||||
|
int c = (current >>> (bits - 8)) & 0xFF;
|
||||||
|
node = node.children[c];
|
||||||
|
bits -= node.bits;
|
||||||
|
if (node.isTerminal()) {
|
||||||
|
if (node.symbol == HpackUtil.HUFFMAN_EOS) {
|
||||||
|
throw EOS_DECODED;
|
||||||
|
}
|
||||||
|
baos.write(node.symbol);
|
||||||
|
node = root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (bits > 0) {
|
||||||
|
int c = (current << (8 - bits)) & 0xFF;
|
||||||
|
node = node.children[c];
|
||||||
|
if (node.isTerminal() && node.bits <= bits) {
|
||||||
|
bits -= node.bits;
|
||||||
|
baos.write(node.symbol);
|
||||||
|
node = root;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 5.2. String Literal Representation
|
||||||
|
// Padding not corresponding to the most significant bits of the code
|
||||||
|
// for the EOS symbol (0xFF) MUST be treated as a decoding error.
|
||||||
|
int mask = (1 << bits) - 1;
|
||||||
|
if ((current & mask) != mask) {
|
||||||
|
throw INVALID_PADDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Node {
|
||||||
|
|
||||||
|
private final int symbol; // terminal nodes have a symbol
|
||||||
|
private final int bits; // number of bits matched by the node
|
||||||
|
private final Node[] children; // internal nodes have children
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an internal node
|
||||||
|
*/
|
||||||
|
private Node() {
|
||||||
|
symbol = 0;
|
||||||
|
bits = 8;
|
||||||
|
children = new Node[256];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a terminal node
|
||||||
|
*
|
||||||
|
* @param symbol the symbol the node represents
|
||||||
|
* @param bits the number of bits matched by this node
|
||||||
|
*/
|
||||||
|
private Node(int symbol, int bits) {
|
||||||
|
assert bits > 0 && bits <= 8;
|
||||||
|
this.symbol = symbol;
|
||||||
|
this.bits = bits;
|
||||||
|
children = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isTerminal() {
|
||||||
|
return children == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Node buildTree(int[] codes, byte[] lengths) {
|
||||||
|
Node root = new Node();
|
||||||
|
for (int i = 0; i < codes.length; i++) {
|
||||||
|
insert(root, i, codes[i], lengths[i]);
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void insert(Node root, int symbol, int code, byte length) {
|
||||||
|
// traverse tree using the most significant bytes of code
|
||||||
|
Node current = root;
|
||||||
|
while (length > 8) {
|
||||||
|
if (current.isTerminal()) {
|
||||||
|
throw new IllegalStateException("invalid Huffman code: prefix not unique");
|
||||||
|
}
|
||||||
|
length -= 8;
|
||||||
|
int i = (code >>> length) & 0xFF;
|
||||||
|
if (current.children[i] == null) {
|
||||||
|
current.children[i] = new Node();
|
||||||
|
}
|
||||||
|
current = current.children[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
Node terminal = new Node(symbol, length);
|
||||||
|
int shift = 8 - length;
|
||||||
|
int start = (code << shift) & 0xFF;
|
||||||
|
int end = 1 << shift;
|
||||||
|
for (int i = start; i < start + end; i++) {
|
||||||
|
current.children[i] = terminal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
final class HuffmanEncoder {
|
||||||
|
|
||||||
|
private final int[] codes;
|
||||||
|
private final byte[] lengths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new Huffman encoder with the specified Huffman coding.
|
||||||
|
*
|
||||||
|
* @param codes the Huffman codes indexed by symbol
|
||||||
|
* @param lengths the length of each Huffman code
|
||||||
|
*/
|
||||||
|
HuffmanEncoder(int[] codes, byte[] lengths) {
|
||||||
|
this.codes = codes;
|
||||||
|
this.lengths = lengths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses the input string literal using the Huffman coding.
|
||||||
|
*
|
||||||
|
* @param out the output stream for the compressed data
|
||||||
|
* @param data the string literal to be Huffman encoded
|
||||||
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @see HuffmanEncoder#encode(OutputStream, byte[], int, int)
|
||||||
|
*/
|
||||||
|
public void encode(OutputStream out, byte[] data) throws IOException {
|
||||||
|
encode(out, data, 0, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compresses the input string literal using the Huffman coding.
|
||||||
|
*
|
||||||
|
* @param out the output stream for the compressed data
|
||||||
|
* @param data the string literal to be Huffman encoded
|
||||||
|
* @param off the start offset in the data
|
||||||
|
* @param len the number of bytes to encode
|
||||||
|
* @throws IOException if an I/O error occurs. In particular, an <code>IOException</code> may be
|
||||||
|
* thrown if the output stream has been closed.
|
||||||
|
*/
|
||||||
|
public void encode(OutputStream out, byte[] data, int off, int len) throws IOException {
|
||||||
|
if (out == null) {
|
||||||
|
throw new NullPointerException("out");
|
||||||
|
} else if (data == null) {
|
||||||
|
throw new NullPointerException("data");
|
||||||
|
} else if (off < 0 || len < 0 || (off + len) < 0 || off > data.length ||
|
||||||
|
(off + len) > data.length) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
} else if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long current = 0;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int b = data[off + i] & 0xFF;
|
||||||
|
int code = codes[b];
|
||||||
|
int nbits = lengths[b];
|
||||||
|
|
||||||
|
current <<= nbits;
|
||||||
|
current |= code;
|
||||||
|
n += nbits;
|
||||||
|
|
||||||
|
while (n >= 8) {
|
||||||
|
n -= 8;
|
||||||
|
out.write((int) (current >> n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n > 0) {
|
||||||
|
current <<= 8 - n;
|
||||||
|
current |= 0xFF >>> n; // this should be EOS symbol
|
||||||
|
out.write((int) current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of bytes required to Huffman encode the input string literal.
|
||||||
|
*
|
||||||
|
* @param data the string literal to be Huffman encoded
|
||||||
|
* @return the number of bytes required to Huffman encode <code>data</code>
|
||||||
|
*/
|
||||||
|
public int getEncodedLength(byte[] data) {
|
||||||
|
if (data == null) {
|
||||||
|
throw new NullPointerException("data");
|
||||||
|
}
|
||||||
|
long len = 0;
|
||||||
|
for (byte b : data) {
|
||||||
|
len += lengths[b & 0xFF];
|
||||||
|
}
|
||||||
|
return (int) ((len + 7) >> 3);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
final class StaticTable {
|
||||||
|
|
||||||
|
private static final String EMPTY = "";
|
||||||
|
|
||||||
|
// Appendix A: Static Table
|
||||||
|
// http://tools.ietf.org/html/rfc7541#appendix-A
|
||||||
|
private static final List<HeaderField> STATIC_TABLE = Arrays.asList(
|
||||||
|
/* 1 */ new HeaderField(":authority", EMPTY),
|
||||||
|
/* 2 */ new HeaderField(":method", "GET"),
|
||||||
|
/* 3 */ new HeaderField(":method", "POST"),
|
||||||
|
/* 4 */ new HeaderField(":path", "/"),
|
||||||
|
/* 5 */ new HeaderField(":path", "/index.html"),
|
||||||
|
/* 6 */ new HeaderField(":scheme", "http"),
|
||||||
|
/* 7 */ new HeaderField(":scheme", "https"),
|
||||||
|
/* 8 */ new HeaderField(":status", "200"),
|
||||||
|
/* 9 */ new HeaderField(":status", "204"),
|
||||||
|
/* 10 */ new HeaderField(":status", "206"),
|
||||||
|
/* 11 */ new HeaderField(":status", "304"),
|
||||||
|
/* 12 */ new HeaderField(":status", "400"),
|
||||||
|
/* 13 */ new HeaderField(":status", "404"),
|
||||||
|
/* 14 */ new HeaderField(":status", "500"),
|
||||||
|
/* 15 */ new HeaderField("accept-charset", EMPTY),
|
||||||
|
/* 16 */ new HeaderField("accept-encoding", "gzip, deflate"),
|
||||||
|
/* 17 */ new HeaderField("accept-language", EMPTY),
|
||||||
|
/* 18 */ new HeaderField("accept-ranges", EMPTY),
|
||||||
|
/* 19 */ new HeaderField("accept", EMPTY),
|
||||||
|
/* 20 */ new HeaderField("access-control-allow-origin", EMPTY),
|
||||||
|
/* 21 */ new HeaderField("age", EMPTY),
|
||||||
|
/* 22 */ new HeaderField("allow", EMPTY),
|
||||||
|
/* 23 */ new HeaderField("authorization", EMPTY),
|
||||||
|
/* 24 */ new HeaderField("cache-control", EMPTY),
|
||||||
|
/* 25 */ new HeaderField("content-disposition", EMPTY),
|
||||||
|
/* 26 */ new HeaderField("content-encoding", EMPTY),
|
||||||
|
/* 27 */ new HeaderField("content-language", EMPTY),
|
||||||
|
/* 28 */ new HeaderField("content-length", EMPTY),
|
||||||
|
/* 29 */ new HeaderField("content-location", EMPTY),
|
||||||
|
/* 30 */ new HeaderField("content-range", EMPTY),
|
||||||
|
/* 31 */ new HeaderField("content-type", EMPTY),
|
||||||
|
/* 32 */ new HeaderField("cookie", EMPTY),
|
||||||
|
/* 33 */ new HeaderField("date", EMPTY),
|
||||||
|
/* 34 */ new HeaderField("etag", EMPTY),
|
||||||
|
/* 35 */ new HeaderField("expect", EMPTY),
|
||||||
|
/* 36 */ new HeaderField("expires", EMPTY),
|
||||||
|
/* 37 */ new HeaderField("from", EMPTY),
|
||||||
|
/* 38 */ new HeaderField("host", EMPTY),
|
||||||
|
/* 39 */ new HeaderField("if-match", EMPTY),
|
||||||
|
/* 40 */ new HeaderField("if-modified-since", EMPTY),
|
||||||
|
/* 41 */ new HeaderField("if-none-match", EMPTY),
|
||||||
|
/* 42 */ new HeaderField("if-range", EMPTY),
|
||||||
|
/* 43 */ new HeaderField("if-unmodified-since", EMPTY),
|
||||||
|
/* 44 */ new HeaderField("last-modified", EMPTY),
|
||||||
|
/* 45 */ new HeaderField("link", EMPTY),
|
||||||
|
/* 46 */ new HeaderField("location", EMPTY),
|
||||||
|
/* 47 */ new HeaderField("max-forwards", EMPTY),
|
||||||
|
/* 48 */ new HeaderField("proxy-authenticate", EMPTY),
|
||||||
|
/* 49 */ new HeaderField("proxy-authorization", EMPTY),
|
||||||
|
/* 50 */ new HeaderField("range", EMPTY),
|
||||||
|
/* 51 */ new HeaderField("referer", EMPTY),
|
||||||
|
/* 52 */ new HeaderField("refresh", EMPTY),
|
||||||
|
/* 53 */ new HeaderField("retry-after", EMPTY),
|
||||||
|
/* 54 */ new HeaderField("server", EMPTY),
|
||||||
|
/* 55 */ new HeaderField("set-cookie", EMPTY),
|
||||||
|
/* 56 */ new HeaderField("strict-transport-security", EMPTY),
|
||||||
|
/* 57 */ new HeaderField("transfer-encoding", EMPTY),
|
||||||
|
/* 58 */ new HeaderField("user-agent", EMPTY),
|
||||||
|
/* 59 */ new HeaderField("vary", EMPTY),
|
||||||
|
/* 60 */ new HeaderField("via", EMPTY),
|
||||||
|
/* 61 */ new HeaderField("www-authenticate", EMPTY)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final Map<String, Integer> STATIC_INDEX_BY_NAME = createMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of header fields in the static table.
|
||||||
|
*/
|
||||||
|
static final int length = STATIC_TABLE.size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the header field at the given index value.
|
||||||
|
*/
|
||||||
|
static HeaderField getEntry(int index) {
|
||||||
|
return STATIC_TABLE.get(index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the lowest index value for the given header field name in the static table. Returns
|
||||||
|
* -1 if the header field name is not in the static table.
|
||||||
|
*/
|
||||||
|
static int getIndex(byte[] name) {
|
||||||
|
String nameString = new String(name, 0, name.length, HpackUtil.ISO_8859_1);
|
||||||
|
Integer index = STATIC_INDEX_BY_NAME.get(nameString);
|
||||||
|
if (index == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index value for the given header field in the static table. Returns -1 if the
|
||||||
|
* header field is not in the static table.
|
||||||
|
*/
|
||||||
|
static int getIndex(byte[] name, byte[] value) {
|
||||||
|
int index = getIndex(name);
|
||||||
|
if (index == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this assumes all entries for a given header field are sequential.
|
||||||
|
while (index <= length) {
|
||||||
|
HeaderField entry = getEntry(index);
|
||||||
|
if (!HpackUtil.equals(name, entry.name)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (HpackUtil.equals(value, entry.value)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a map of header name to index value to allow quick lookup
|
||||||
|
private static Map<String, Integer> createMap() {
|
||||||
|
int length = STATIC_TABLE.size();
|
||||||
|
HashMap<String, Integer> ret = new HashMap<String, Integer>(length);
|
||||||
|
// Iterate through the static table in reverse order to
|
||||||
|
// save the smallest index for a given name in the map.
|
||||||
|
for (int index = length; index > 0; index--) {
|
||||||
|
HeaderField entry = getEntry(index);
|
||||||
|
String name = new String(entry.name, 0, entry.name.length, HpackUtil.ISO_8859_1);
|
||||||
|
ret.put(name, index);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// singleton
|
||||||
|
private StaticTable() {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <a href="http://tools.ietf.org/html/rfc7541">HPACK: Header Compression for HTTP/2</a>
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.http2.hpack;
|
@ -15,9 +15,9 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import com.twitter.hpack.Encoder;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.handler.codec.http2.hpack.Encoder;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import static io.netty.handler.codec.http2.hpack.HpackUtil.ISO_8859_1;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class DecoderTest {
|
||||||
|
|
||||||
|
private static final int MAX_HEADER_SIZE = 8192;
|
||||||
|
private static final int MAX_HEADER_TABLE_SIZE = 4096;
|
||||||
|
|
||||||
|
private Decoder decoder;
|
||||||
|
private HeaderListener mockListener;
|
||||||
|
|
||||||
|
private static String hex(String s) {
|
||||||
|
return Hex.encodeHexString(s.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] getBytes(String s) {
|
||||||
|
return s.getBytes(ISO_8859_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decode(String encoded) throws IOException {
|
||||||
|
byte[] b = Hex.decodeHex(encoded.toCharArray());
|
||||||
|
decoder.decode(new ByteArrayInputStream(b), mockListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
decoder = new Decoder(MAX_HEADER_SIZE, MAX_HEADER_TABLE_SIZE);
|
||||||
|
mockListener = mock(HeaderListener.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncompleteIndex() throws IOException {
|
||||||
|
// Verify incomplete indices are unread
|
||||||
|
byte[] compressed = Hex.decodeHex("FFF0".toCharArray());
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(compressed);
|
||||||
|
decoder.decode(in, mockListener);
|
||||||
|
assertEquals(1, in.available());
|
||||||
|
decoder.decode(in, mockListener);
|
||||||
|
assertEquals(1, in.available());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testUnusedIndex() throws IOException {
|
||||||
|
// Index 0 is not used
|
||||||
|
decode("80");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testIllegalIndex() throws IOException {
|
||||||
|
// Index larger than the header table
|
||||||
|
decode("FF00");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testInsidiousIndex() throws IOException {
|
||||||
|
// Insidious index so the last shift causes sign overflow
|
||||||
|
decode("FF8080808008");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicTableSizeUpdate() throws Exception {
|
||||||
|
decode("20");
|
||||||
|
assertEquals(0, decoder.getMaxHeaderTableSize());
|
||||||
|
decode("3FE11F");
|
||||||
|
assertEquals(4096, decoder.getMaxHeaderTableSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDynamicTableSizeUpdateRequired() throws Exception {
|
||||||
|
decoder.setMaxHeaderTableSize(32);
|
||||||
|
decode("3F00");
|
||||||
|
assertEquals(31, decoder.getMaxHeaderTableSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testIllegalDynamicTableSizeUpdate() throws Exception {
|
||||||
|
// max header table size = MAX_HEADER_TABLE_SIZE + 1
|
||||||
|
decode("3FE21F");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testInsidiousMaxDynamicTableSize() throws IOException {
|
||||||
|
// max header table size sign overflow
|
||||||
|
decode("3FE1FFFFFF07");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReduceMaxDynamicTableSize() throws Exception {
|
||||||
|
decoder.setMaxHeaderTableSize(0);
|
||||||
|
assertEquals(0, decoder.getMaxHeaderTableSize());
|
||||||
|
decode("2081");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testTooLargeDynamicTableSizeUpdate() throws Exception {
|
||||||
|
decoder.setMaxHeaderTableSize(0);
|
||||||
|
assertEquals(0, decoder.getMaxHeaderTableSize());
|
||||||
|
decode("21"); // encoder max header table size not small enough
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testMissingDynamicTableSizeUpdate() throws Exception {
|
||||||
|
decoder.setMaxHeaderTableSize(0);
|
||||||
|
assertEquals(0, decoder.getMaxHeaderTableSize());
|
||||||
|
decode("81");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralWithIncrementalIndexingWithEmptyName() throws Exception {
|
||||||
|
decode("000005" + hex("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLiteralWithIncrementalIndexingCompleteEviction() throws Exception {
|
||||||
|
// Verify indexed host header
|
||||||
|
decode("4004" + hex("name") + "05" + hex("value"));
|
||||||
|
verify(mockListener).addHeader(getBytes("name"), getBytes("value"), false);
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
assertFalse(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
reset(mockListener);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < 4096; i++) {
|
||||||
|
sb.append("a");
|
||||||
|
}
|
||||||
|
String value = sb.toString();
|
||||||
|
sb = new StringBuilder();
|
||||||
|
sb.append("417F811F");
|
||||||
|
for (int i = 0; i < 4096; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
decode(sb.toString());
|
||||||
|
verify(mockListener).addHeader(getBytes(":authority"), getBytes(value), false);
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
assertFalse(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify next header is inserted at index 62
|
||||||
|
decode("4004" + hex("name") + "05" + hex("value") + "BE");
|
||||||
|
verify(mockListener, times(2)).addHeader(getBytes("name"), getBytes("value"), false);
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLiteralWithIncrementalIndexingWithLargeName() throws Exception {
|
||||||
|
// Ignore header name that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("407F817F");
|
||||||
|
for (int i = 0; i < 16384; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
sb.append("00");
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify next header is inserted at index 62
|
||||||
|
decode("4004" + hex("name") + "05" + hex("value") + "BE");
|
||||||
|
verify(mockListener, times(2)).addHeader(getBytes("name"), getBytes("value"), false);
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLiteralWithIncrementalIndexingWithLargeValue() throws Exception {
|
||||||
|
// Ignore header that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("4004");
|
||||||
|
sb.append(hex("name"));
|
||||||
|
sb.append("7F813F");
|
||||||
|
for (int i = 0; i < 8192; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify next header is inserted at index 62
|
||||||
|
decode("4004" + hex("name") + "05" + hex("value") + "BE");
|
||||||
|
verify(mockListener, times(2)).addHeader(getBytes("name"), getBytes("value"), false);
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralWithoutIndexingWithEmptyName() throws Exception {
|
||||||
|
decode("000005" + hex("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralWithoutIndexingWithLargeName() throws Exception {
|
||||||
|
// Ignore header name that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("007F817F");
|
||||||
|
for (int i = 0; i < 16384; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
sb.append("00");
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify table is unmodified
|
||||||
|
decode("BE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralWithoutIndexingWithLargeValue() throws Exception {
|
||||||
|
// Ignore header that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("0004");
|
||||||
|
sb.append(hex("name"));
|
||||||
|
sb.append("7F813F");
|
||||||
|
for (int i = 0; i < 8192; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify table is unmodified
|
||||||
|
decode("BE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralNeverIndexedWithEmptyName() throws Exception {
|
||||||
|
decode("100005" + hex("value"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralNeverIndexedWithLargeName() throws Exception {
|
||||||
|
// Ignore header name that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("107F817F");
|
||||||
|
for (int i = 0; i < 16384; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
sb.append("00");
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify table is unmodified
|
||||||
|
decode("BE");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testLiteralNeverIndexedWithLargeValue() throws Exception {
|
||||||
|
// Ignore header that exceeds max header size
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("1004");
|
||||||
|
sb.append(hex("name"));
|
||||||
|
sb.append("7F813F");
|
||||||
|
for (int i = 0; i < 8192; i++) {
|
||||||
|
sb.append("61"); // 'a'
|
||||||
|
}
|
||||||
|
decode(sb.toString());
|
||||||
|
verifyNoMoreInteractions(mockListener);
|
||||||
|
|
||||||
|
// Verify header block is reported as truncated
|
||||||
|
assertTrue(decoder.endHeaderBlock());
|
||||||
|
|
||||||
|
// Verify table is unmodified
|
||||||
|
decode("BE");
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracted from org/apache/commons/codec/binary/Hex.java Copyright Apache Software Foundation
|
||||||
|
*/
|
||||||
|
final class Hex {
|
||||||
|
private Hex() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to build output as Hex
|
||||||
|
*/
|
||||||
|
private static final char[] DIGITS_LOWER =
|
||||||
|
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to build output as Hex
|
||||||
|
*/
|
||||||
|
private static final char[] DIGITS_UPPER =
|
||||||
|
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of characters representing hexadecimal values into an array of bytes of
|
||||||
|
* those same values. The returned array will be half the length of the passed array, as it
|
||||||
|
* takes two characters to represent any given byte. An exception is thrown if the passed char
|
||||||
|
* array has an odd number of elements.
|
||||||
|
*
|
||||||
|
* @param data An array of characters containing hexadecimal digits
|
||||||
|
* @return A byte array containing binary data decoded from the supplied char array.
|
||||||
|
* @throws IOException Thrown if an odd number or illegal of characters is supplied
|
||||||
|
*/
|
||||||
|
public static byte[] decodeHex(char[] data) throws IOException {
|
||||||
|
|
||||||
|
int len = data.length;
|
||||||
|
|
||||||
|
if ((len & 0x01) != 0) {
|
||||||
|
throw new IOException("Odd number of characters.");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] out = new byte[len >> 1];
|
||||||
|
|
||||||
|
// two characters form the hex value.
|
||||||
|
for (int i = 0, j = 0; j < len; i++) {
|
||||||
|
int f = toDigit(data[j], j) << 4;
|
||||||
|
j++;
|
||||||
|
f = f | toDigit(data[j], j);
|
||||||
|
j++;
|
||||||
|
out[i] = (byte) (f & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of bytes into an array of characters representing the hexadecimal values of
|
||||||
|
* each byte in order. The returned array will be double the length of the passed array, as it
|
||||||
|
* takes two characters to represent any given byte.
|
||||||
|
*
|
||||||
|
* @param data a byte[] to convert to Hex characters
|
||||||
|
* @return A char[] containing hexadecimal characters
|
||||||
|
*/
|
||||||
|
public static char[] encodeHex(byte[] data) {
|
||||||
|
return encodeHex(data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of bytes into an array of characters representing the hexadecimal values of
|
||||||
|
* each byte in order. The returned array will be double the length of the passed array, as it
|
||||||
|
* takes two characters to represent any given byte.
|
||||||
|
*
|
||||||
|
* @param data a byte[] to convert to Hex characters
|
||||||
|
* @param toLowerCase <code>true</code> converts to lowercase, <code>false</code> to uppercase
|
||||||
|
* @return A char[] containing hexadecimal characters
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
|
||||||
|
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of bytes into an array of characters representing the hexadecimal values of
|
||||||
|
* each byte in order. The returned array will be double the length of the passed array, as it
|
||||||
|
* takes two characters to represent any given byte.
|
||||||
|
*
|
||||||
|
* @param data a byte[] to convert to Hex characters
|
||||||
|
* @param toDigits the output alphabet
|
||||||
|
* @return A char[] containing hexadecimal characters
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
protected static char[] encodeHex(byte[] data, char[] toDigits) {
|
||||||
|
int l = data.length;
|
||||||
|
char[] out = new char[l << 1];
|
||||||
|
// two characters form the hex value.
|
||||||
|
for (int i = 0, j = 0; i < l; i++) {
|
||||||
|
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
|
||||||
|
out[j++] = toDigits[0x0F & data[i]];
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an array of bytes into a String representing the hexadecimal values of each byte in
|
||||||
|
* order. The returned String will be double the length of the passed array, as it takes two
|
||||||
|
* characters to represent any given byte.
|
||||||
|
*
|
||||||
|
* @param data a byte[] to convert to Hex characters
|
||||||
|
* @return A String containing hexadecimal characters
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
public static String encodeHexString(byte[] data) {
|
||||||
|
return new String(encodeHex(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a hexadecimal character to an integer.
|
||||||
|
*
|
||||||
|
* @param ch A character to convert to an integer digit
|
||||||
|
* @param index The index of the character in the source
|
||||||
|
* @return An integer
|
||||||
|
* @throws IOException Thrown if ch is an illegal hex character
|
||||||
|
*/
|
||||||
|
protected static int toDigit(char ch, int index) throws IOException {
|
||||||
|
int digit = Character.digit(ch, 16);
|
||||||
|
if (digit == -1) {
|
||||||
|
throw new IOException("Illegal hexadecimal character " + ch + " at index " + index);
|
||||||
|
}
|
||||||
|
return digit;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class HpackTest {
|
||||||
|
|
||||||
|
private static final String TEST_DIR = "/io/netty/handler/codec/http2/hpack/testdata/";
|
||||||
|
|
||||||
|
private final String fileName;
|
||||||
|
|
||||||
|
public HpackTest(String fileName) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Parameters(name = "{0}")
|
||||||
|
public static Collection<Object[]> data() {
|
||||||
|
URL url = HpackTest.class.getResource(TEST_DIR);
|
||||||
|
File[] files = new File(url.getFile()).listFiles();
|
||||||
|
if (files == null) {
|
||||||
|
throw new NullPointerException("files");
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Object[]> data = new ArrayList<Object[]>();
|
||||||
|
for (File file : files) {
|
||||||
|
data.add(new Object[]{file.getName()});
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() throws Exception {
|
||||||
|
InputStream is = HpackTest.class.getResourceAsStream(TEST_DIR + fileName);
|
||||||
|
TestCase testCase = TestCase.load(is);
|
||||||
|
testCase.testCompress();
|
||||||
|
testCase.testDecompress();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class HuffmanTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHuffman() throws IOException {
|
||||||
|
|
||||||
|
String s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
for (int i = 0; i < s.length(); i++) {
|
||||||
|
roundTrip(s.substring(0, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
Random random = new Random(123456789L);
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
random.nextBytes(buf);
|
||||||
|
roundTrip(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testDecodeEOS() throws IOException {
|
||||||
|
byte[] buf = new byte[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
buf[i] = (byte) 0xFF;
|
||||||
|
}
|
||||||
|
Huffman.DECODER.decode(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testDecodeIllegalPadding() throws IOException {
|
||||||
|
byte[] buf = new byte[1];
|
||||||
|
buf[0] = 0x00; // '0', invalid padding
|
||||||
|
Huffman.DECODER.decode(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test//(expected = IOException.class) TODO(jpinner) fix me
|
||||||
|
public void testDecodeExtraPadding() throws IOException {
|
||||||
|
byte[] buf = new byte[2];
|
||||||
|
buf[0] = 0x0F; // '1', 'EOS'
|
||||||
|
buf[1] = (byte) 0xFF; // 'EOS'
|
||||||
|
Huffman.DECODER.decode(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void roundTrip(String s) throws IOException {
|
||||||
|
roundTrip(Huffman.ENCODER, Huffman.DECODER, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void roundTrip(HuffmanEncoder encoder, HuffmanDecoder decoder, String s)
|
||||||
|
throws IOException {
|
||||||
|
roundTrip(encoder, decoder, s.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void roundTrip(byte[] buf) throws IOException {
|
||||||
|
roundTrip(Huffman.ENCODER, Huffman.DECODER, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void roundTrip(HuffmanEncoder encoder, HuffmanDecoder decoder, byte[] buf)
|
||||||
|
throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(baos);
|
||||||
|
|
||||||
|
encoder.encode(dos, buf);
|
||||||
|
|
||||||
|
byte[] actualBytes = decoder.decode(baos.toByteArray());
|
||||||
|
|
||||||
|
Assert.assertTrue(Arrays.equals(buf, actualBytes));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import com.google.gson.FieldNamingPolicy;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
final class TestCase {
|
||||||
|
|
||||||
|
private static final Gson GSON = new GsonBuilder()
|
||||||
|
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||||
|
.registerTypeAdapter(HeaderField.class, new HeaderFieldDeserializer())
|
||||||
|
.create();
|
||||||
|
|
||||||
|
int maxHeaderTableSize = -1;
|
||||||
|
boolean useIndexing = true;
|
||||||
|
boolean sensitiveHeaders;
|
||||||
|
boolean forceHuffmanOn;
|
||||||
|
boolean forceHuffmanOff;
|
||||||
|
|
||||||
|
List<HeaderBlock> headerBlocks;
|
||||||
|
|
||||||
|
private TestCase() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static TestCase load(InputStream is) throws IOException {
|
||||||
|
InputStreamReader r = new InputStreamReader(is);
|
||||||
|
TestCase testCase = GSON.fromJson(r, TestCase.class);
|
||||||
|
for (HeaderBlock headerBlock : testCase.headerBlocks) {
|
||||||
|
headerBlock.encodedBytes = Hex.decodeHex(headerBlock.getEncodedStr().toCharArray());
|
||||||
|
}
|
||||||
|
return testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
void testCompress() throws Exception {
|
||||||
|
Encoder encoder = createEncoder();
|
||||||
|
|
||||||
|
for (HeaderBlock headerBlock : headerBlocks) {
|
||||||
|
|
||||||
|
byte[] actual =
|
||||||
|
encode(encoder, headerBlock.getHeaders(), headerBlock.getMaxHeaderTableSize(),
|
||||||
|
sensitiveHeaders);
|
||||||
|
|
||||||
|
if (!Arrays.equals(actual, headerBlock.encodedBytes)) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED:\n" + headerBlock.getEncodedStr() +
|
||||||
|
"\nACTUAL:\n" + Hex.encodeHexString(actual));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HeaderField> actualDynamicTable = new ArrayList<HeaderField>();
|
||||||
|
for (int index = 0; index < encoder.length(); index++) {
|
||||||
|
actualDynamicTable.add(encoder.getHeaderField(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HeaderField> expectedDynamicTable = headerBlock.getDynamicTable();
|
||||||
|
|
||||||
|
if (!expectedDynamicTable.equals(actualDynamicTable)) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED DYNAMIC TABLE:\n" + expectedDynamicTable +
|
||||||
|
"\nACTUAL DYNAMIC TABLE:\n" + actualDynamicTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerBlock.getTableSize() != encoder.size()) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED TABLE SIZE: " + headerBlock.getTableSize() +
|
||||||
|
"\n ACTUAL TABLE SIZE : " + encoder.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testDecompress() throws Exception {
|
||||||
|
Decoder decoder = createDecoder();
|
||||||
|
|
||||||
|
for (HeaderBlock headerBlock : headerBlocks) {
|
||||||
|
|
||||||
|
List<HeaderField> actualHeaders = decode(decoder, headerBlock.encodedBytes);
|
||||||
|
|
||||||
|
List<HeaderField> expectedHeaders = new ArrayList<HeaderField>();
|
||||||
|
for (HeaderField h : headerBlock.getHeaders()) {
|
||||||
|
expectedHeaders.add(new HeaderField(h.name, h.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!expectedHeaders.equals(actualHeaders)) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED:\n" + expectedHeaders +
|
||||||
|
"\nACTUAL:\n" + actualHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HeaderField> actualDynamicTable = new ArrayList<HeaderField>();
|
||||||
|
for (int index = 0; index < decoder.length(); index++) {
|
||||||
|
actualDynamicTable.add(decoder.getHeaderField(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<HeaderField> expectedDynamicTable = headerBlock.getDynamicTable();
|
||||||
|
|
||||||
|
if (!expectedDynamicTable.equals(actualDynamicTable)) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED DYNAMIC TABLE:\n" + expectedDynamicTable +
|
||||||
|
"\nACTUAL DYNAMIC TABLE:\n" + actualDynamicTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (headerBlock.getTableSize() != decoder.size()) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"\nEXPECTED TABLE SIZE: " + headerBlock.getTableSize() +
|
||||||
|
"\n ACTUAL TABLE SIZE : " + decoder.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Encoder createEncoder() {
|
||||||
|
int maxHeaderTableSize = this.maxHeaderTableSize;
|
||||||
|
if (maxHeaderTableSize == -1) {
|
||||||
|
maxHeaderTableSize = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Encoder(maxHeaderTableSize, useIndexing, forceHuffmanOn, forceHuffmanOff);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Decoder createDecoder() {
|
||||||
|
int maxHeaderTableSize = this.maxHeaderTableSize;
|
||||||
|
if (maxHeaderTableSize == -1) {
|
||||||
|
maxHeaderTableSize = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Decoder(8192, maxHeaderTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] encode(Encoder encoder, List<HeaderField> headers, int maxHeaderTableSize,
|
||||||
|
boolean sensitive)
|
||||||
|
throws IOException {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
if (maxHeaderTableSize != -1) {
|
||||||
|
encoder.setMaxHeaderTableSize(baos, maxHeaderTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (HeaderField e : headers) {
|
||||||
|
encoder.encodeHeader(baos, e.name, e.value, sensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
return baos.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<HeaderField> decode(Decoder decoder, byte[] expected) throws IOException {
|
||||||
|
List<HeaderField> headers = new ArrayList<HeaderField>();
|
||||||
|
TestHeaderListener listener = new TestHeaderListener(headers);
|
||||||
|
decoder.decode(new ByteArrayInputStream(expected), listener);
|
||||||
|
decoder.endHeaderBlock();
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String concat(List<String> l) {
|
||||||
|
StringBuilder ret = new StringBuilder();
|
||||||
|
for (String s : l) {
|
||||||
|
ret.append(s);
|
||||||
|
}
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HeaderBlock {
|
||||||
|
private int maxHeaderTableSize = -1;
|
||||||
|
private byte[] encodedBytes;
|
||||||
|
private List<String> encoded;
|
||||||
|
private List<HeaderField> headers;
|
||||||
|
private List<HeaderField> dynamicTable;
|
||||||
|
private int tableSize;
|
||||||
|
|
||||||
|
private int getMaxHeaderTableSize() {
|
||||||
|
return maxHeaderTableSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEncodedStr() {
|
||||||
|
return concat(encoded).replaceAll(" ", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HeaderField> getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<HeaderField> getDynamicTable() {
|
||||||
|
return dynamicTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTableSize() {
|
||||||
|
return tableSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class HeaderFieldDeserializer implements JsonDeserializer<HeaderField> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HeaderField deserialize(JsonElement json, Type typeOfT,
|
||||||
|
JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
JsonObject jsonObject = json.getAsJsonObject();
|
||||||
|
Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
|
||||||
|
if (entrySet.size() != 1) {
|
||||||
|
throw new JsonParseException("JSON Object has multiple entries: " + entrySet);
|
||||||
|
}
|
||||||
|
Map.Entry<String, JsonElement> entry = entrySet.iterator().next();
|
||||||
|
String name = entry.getKey();
|
||||||
|
String value = entry.getValue().getAsString();
|
||||||
|
return new HeaderField(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2014 Twitter, 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 io.netty.handler.codec.http2.hpack;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
final class TestHeaderListener implements HeaderListener {
|
||||||
|
|
||||||
|
private final List<HeaderField> headers;
|
||||||
|
|
||||||
|
TestHeaderListener(List<HeaderField> headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHeader(byte[] name, byte[] value, boolean sensitive) {
|
||||||
|
headers.add(new HeaderField(name, value));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_on": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4487 6107 a4b5 8d33 ff40 86f2 b12d 424f",
|
||||||
|
"4f83 ee3a 3f"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ ":path": "/somepath" }
|
||||||
|
],
|
||||||
|
"table_size": 89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ "x-custom": "val" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"bfbe be"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ ":path": "/somepath" }
|
||||||
|
],
|
||||||
|
"table_size": 89
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ "foo": "bar" },
|
||||||
|
{ "x-custom": "val" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"bfbe 4082 94e7 838c 767f bf"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "foo": "bar" },
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ ":path": "/somepath" }
|
||||||
|
],
|
||||||
|
"table_size": 127
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"c0"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "foo": "bar" },
|
||||||
|
{ "x-custom": "val" },
|
||||||
|
{ ":path": "/somepath" }
|
||||||
|
],
|
||||||
|
"table_size": 127
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
codec-http2/src/test/resources/io/netty/handler/codec/http2/hpack/testdata/testEmpty.json
vendored
Normal file
14
codec-http2/src/test/resources/io/netty/handler/codec/http2/hpack/testdata/testEmpty.json
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
],
|
||||||
|
"table_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
58
codec-http2/src/test/resources/io/netty/handler/codec/http2/hpack/testdata/testEviction.json
vendored
Normal file
58
codec-http2/src/test/resources/io/netty/handler/codec/http2/hpack/testdata/testEviction.json
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"max_header_table_size": 128,
|
||||||
|
"force_huffman_on": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val1" },
|
||||||
|
{ "x-custom": "val2" },
|
||||||
|
{ "x-custom": "val3" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4487 6107 a4b5 8d33 ff40 86f2 b12d 424f",
|
||||||
|
"4f83 ee3a 037e 83ee 3a05 7e83 ee3a 19"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "x-custom": "val3" },
|
||||||
|
{ "x-custom": "val2" }
|
||||||
|
],
|
||||||
|
"table_size": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val4" },
|
||||||
|
{ "x-custom": "val5" },
|
||||||
|
{ "x-custom": "val6" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4487 6107 a4b5 8d33 ff7f 0083 ee3a 1a7e",
|
||||||
|
"83ee 3a1b 7e83 ee3a 1c"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "x-custom": "val6" },
|
||||||
|
{ "x-custom": "val5" }
|
||||||
|
],
|
||||||
|
"table_size": 88
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/somepath" },
|
||||||
|
{ "x-custom": "val1" },
|
||||||
|
{ "x-custom": "val2" },
|
||||||
|
{ "x-custom": "val3" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4487 6107 a4b5 8d33 ff7f 0083 ee3a 037e",
|
||||||
|
"83ee 3a05 7e83 ee3a 19"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "x-custom": "val3" },
|
||||||
|
{ "x-custom": "val2" }
|
||||||
|
],
|
||||||
|
"table_size": 88
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"max_header_table_size": 128,
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ "name1": "val1" },
|
||||||
|
{ "name2": "val2" },
|
||||||
|
{ "name3": "val3" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4005 6e61 6d65 3104 7661 6c31 4005 6e61",
|
||||||
|
"6d65 3204 7661 6c32 4005 6e61 6d65 3304",
|
||||||
|
"7661 6c33"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "name3": "val3" },
|
||||||
|
{ "name2": "val2" },
|
||||||
|
{ "name1": "val1" }
|
||||||
|
],
|
||||||
|
"table_size": 123
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max_header_table_size": 81,
|
||||||
|
"headers": [
|
||||||
|
{ "name3": "val3" },
|
||||||
|
{ "name2": "val2" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"3f32 be40 056e 616d 6532 0476 616c 32"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "name2": "val2" }
|
||||||
|
],
|
||||||
|
"table_size": 41
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max_header_table_size": 128,
|
||||||
|
"headers": [
|
||||||
|
{ "name1": "val1" },
|
||||||
|
{ "name2": "val2" },
|
||||||
|
{ "name3": "val3" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"3f61 4005 6e61 6d65 3104 7661 6c31 bf40",
|
||||||
|
"056e 616d 6533 0476 616c 33"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "name3": "val3" },
|
||||||
|
{ "name1": "val1" },
|
||||||
|
{ "name2": "val2" }
|
||||||
|
],
|
||||||
|
"table_size": 123
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ "custom-key": "custom-header" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"400a 6375 7374 6f6d 2d6b 6579 0d63 7573",
|
||||||
|
"746f 6d2d 6865 6164 6572"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "custom-key": "custom-header" }
|
||||||
|
],
|
||||||
|
"table_size": 55
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"use_indexing": false,
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":path": "/sample/path" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"040c 2f73 616d 706c 652f 7061 7468"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
],
|
||||||
|
"table_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"sensitive_headers": true,
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ "password": "secret" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"1008 7061 7373 776f 7264 0673 6563 7265",
|
||||||
|
"74"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
],
|
||||||
|
"table_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"82"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
],
|
||||||
|
"table_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "http" },
|
||||||
|
{ ":path": "/" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8286 8441 0f77 7777 2e65 7861 6d70 6c65",
|
||||||
|
"2e63 6f6d"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 57
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "http" },
|
||||||
|
{ ":path": "/" },
|
||||||
|
{ ":authority": "www.example.com" },
|
||||||
|
{ "cache-control": "no-cache" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8286 84be 5808 6e6f 2d63 6163 6865"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "cache-control": "no-cache" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "https" },
|
||||||
|
{ ":path": "/index.html" },
|
||||||
|
{ ":authority": "www.example.com" },
|
||||||
|
{ "custom-key": "custom-value" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8287 85bf 400a 6375 7374 6f6d 2d6b 6579",
|
||||||
|
"0c63 7573 746f 6d2d 7661 6c75 65"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "custom-key": "custom-value" },
|
||||||
|
{ "cache-control": "no-cache" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 164
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_on": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "http" },
|
||||||
|
{ ":path": "/" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4",
|
||||||
|
"ff"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 57
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "http" },
|
||||||
|
{ ":path": "/" },
|
||||||
|
{ ":authority": "www.example.com" },
|
||||||
|
{ "cache-control": "no-cache" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8286 84be 5886 a8eb 1064 9cbf"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "cache-control": "no-cache" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 110
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":scheme": "https" },
|
||||||
|
{ ":path": "/index.html" },
|
||||||
|
{ ":authority": "www.example.com" },
|
||||||
|
{ "custom-key": "custom-value" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925",
|
||||||
|
"a849 e95b b8e8 b4bf"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "custom-key": "custom-value" },
|
||||||
|
{ "cache-control": "no-cache" },
|
||||||
|
{ ":authority": "www.example.com" }
|
||||||
|
],
|
||||||
|
"table_size": 164
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"max_header_table_size": 256,
|
||||||
|
"force_huffman_off": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "302" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "location": "https://www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4803 3330 3258 0770 7269 7661 7465 611d",
|
||||||
|
"4d6f 6e2c 2032 3120 4f63 7420 3230 3133",
|
||||||
|
"2032 303a 3133 3a32 3120 474d 546e 1768",
|
||||||
|
"7474 7073 3a2f 2f77 7777 2e65 7861 6d70",
|
||||||
|
"6c65 2e63 6f6d"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ ":status": "302" }
|
||||||
|
],
|
||||||
|
"table_size": 222
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "307" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "location": "https://www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4803 3330 37c1 c0bf"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ ":status": "307" },
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "cache-control": "private" }
|
||||||
|
],
|
||||||
|
"table_size": 222
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "200" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:22 GMT" },
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "content-encoding": "gzip" },
|
||||||
|
{ "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"88c1 611d 4d6f 6e2c 2032 3120 4f63 7420",
|
||||||
|
"3230 3133 2032 303a 3133 3a32 3220 474d",
|
||||||
|
"54c0 5a04 677a 6970 7738 666f 6f3d 4153",
|
||||||
|
"444a 4b48 514b 425a 584f 5157 454f 5049",
|
||||||
|
"5541 5851 5745 4f49 553b 206d 6178 2d61",
|
||||||
|
"6765 3d33 3630 303b 2076 6572 7369 6f6e",
|
||||||
|
"3d31"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" },
|
||||||
|
{ "content-encoding": "gzip" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:22 GMT" }
|
||||||
|
],
|
||||||
|
"table_size": 215
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"max_header_table_size": 256,
|
||||||
|
"force_huffman_on": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "302" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "location": "https://www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4882 6402 5885 aec3 771a 4b61 96d0 7abe",
|
||||||
|
"9410 54d4 44a8 2005 9504 0b81 66e0 82a6",
|
||||||
|
"2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8",
|
||||||
|
"e9ae 82ae 43d3"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ ":status": "302" }
|
||||||
|
],
|
||||||
|
"table_size": 222
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "307" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "location": "https://www.example.com" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"4883 640e ffc1 c0bf"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ ":status": "307" },
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:21 GMT" },
|
||||||
|
{ "cache-control": "private" }
|
||||||
|
],
|
||||||
|
"table_size": 222
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":status": "200" },
|
||||||
|
{ "cache-control": "private" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:22 GMT" },
|
||||||
|
{ "location": "https://www.example.com" },
|
||||||
|
{ "content-encoding": "gzip" },
|
||||||
|
{ "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"88c1 6196 d07a be94 1054 d444 a820 0595",
|
||||||
|
"040b 8166 e084 a62d 1bff c05a 839b d9ab",
|
||||||
|
"77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b",
|
||||||
|
"3960 d5af 2708 7f36 72c1 ab27 0fb5 291f",
|
||||||
|
"9587 3160 65c0 03ed 4ee5 b106 3d50 07"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
{ "set-cookie": "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" },
|
||||||
|
{ "content-encoding": "gzip" },
|
||||||
|
{ "date": "Mon, 21 Oct 2013 20:13:22 GMT" }
|
||||||
|
],
|
||||||
|
"table_size": 215
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
{
|
||||||
|
"force_huffman_on": true,
|
||||||
|
"header_blocks":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{ ":authority": "" },
|
||||||
|
{ ":method": "GET" },
|
||||||
|
{ ":method": "POST" },
|
||||||
|
{ ":path": "/" },
|
||||||
|
{ ":path": "/index.html" },
|
||||||
|
{ ":scheme": "http" },
|
||||||
|
{ ":scheme": "https" },
|
||||||
|
{ ":status": "200" },
|
||||||
|
{ ":status": "204" },
|
||||||
|
{ ":status": "206" },
|
||||||
|
{ ":status": "304" },
|
||||||
|
{ ":status": "400" },
|
||||||
|
{ ":status": "404" },
|
||||||
|
{ ":status": "500" },
|
||||||
|
{ "accept-charset": "" },
|
||||||
|
{ "accept-encoding": "gzip, deflate" },
|
||||||
|
{ "accept-language": "" },
|
||||||
|
{ "accept-ranges": "" },
|
||||||
|
{ "accept": "" },
|
||||||
|
{ "access-control-allow-origin": "" },
|
||||||
|
{ "age": "" },
|
||||||
|
{ "allow": "" },
|
||||||
|
{ "authorization": "" },
|
||||||
|
{ "cache-control": "" },
|
||||||
|
{ "content-disposition": "" },
|
||||||
|
{ "content-encoding": "" },
|
||||||
|
{ "content-language": "" },
|
||||||
|
{ "content-length": "" },
|
||||||
|
{ "content-location": "" },
|
||||||
|
{ "content-range": "" },
|
||||||
|
{ "content-type": "" },
|
||||||
|
{ "cookie": "" },
|
||||||
|
{ "date": "" },
|
||||||
|
{ "etag": "" },
|
||||||
|
{ "expect": "" },
|
||||||
|
{ "expires": "" },
|
||||||
|
{ "from": "" },
|
||||||
|
{ "host": "" },
|
||||||
|
{ "if-match": "" },
|
||||||
|
{ "if-modified-since": "" },
|
||||||
|
{ "if-none-match": "" },
|
||||||
|
{ "if-range": "" },
|
||||||
|
{ "if-unmodified-since": "" },
|
||||||
|
{ "last-modified": "" },
|
||||||
|
{ "link": "" },
|
||||||
|
{ "location": "" },
|
||||||
|
{ "max-forwards": "" },
|
||||||
|
{ "proxy-authenticate": "" },
|
||||||
|
{ "proxy-authorization": "" },
|
||||||
|
{ "range": "" },
|
||||||
|
{ "referer": "" },
|
||||||
|
{ "refresh": "" },
|
||||||
|
{ "retry-after": "" },
|
||||||
|
{ "server": "" },
|
||||||
|
{ "set-cookie": "" },
|
||||||
|
{ "strict-transport-security": "" },
|
||||||
|
{ "transfer-encoding": "" },
|
||||||
|
{ "user-agent": "" },
|
||||||
|
{ "vary": "" },
|
||||||
|
{ "via": "" },
|
||||||
|
{ "www-authenticate": "" }
|
||||||
|
],
|
||||||
|
"encoded": [
|
||||||
|
"8182 8384 8586 8788 898a 8b8c 8d8e 8f90",
|
||||||
|
"9192 9394 9596 9798 999a 9b9c 9d9e 9fa0",
|
||||||
|
"a1a2 a3a4 a5a6 a7a8 a9aa abac adae afb0",
|
||||||
|
"b1b2 b3b4 b5b6 b7b8 b9ba bbbc bd"
|
||||||
|
],
|
||||||
|
"dynamic_table": [
|
||||||
|
],
|
||||||
|
"table_size": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
201
license/LICENSE.hpack.txt
Normal file
201
license/LICENSE.hpack.txt
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
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.
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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 io.netty.microbench.http2.hpack;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http2.hpack.Decoder;
|
||||||
|
import io.netty.handler.codec.http2.hpack.Encoder;
|
||||||
|
import io.netty.handler.codec.http2.hpack.HeaderListener;
|
||||||
|
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Level;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class DecoderBenchmark extends AbstractMicrobenchmark {
|
||||||
|
|
||||||
|
@Param
|
||||||
|
public HeadersSize size;
|
||||||
|
|
||||||
|
@Param({ "4096" })
|
||||||
|
public int maxTableSize;
|
||||||
|
|
||||||
|
@Param({ "8192" })
|
||||||
|
public int maxHeaderSize;
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public boolean sensitive;
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public boolean limitToAscii;
|
||||||
|
|
||||||
|
private byte[] input;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setup() throws IOException {
|
||||||
|
input = getSerializedHeaders(Util.headers(size, limitToAscii), sensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void decode(final Blackhole bh) throws IOException {
|
||||||
|
Decoder decoder = new Decoder(maxHeaderSize, maxTableSize);
|
||||||
|
decoder.decode(new ByteArrayInputStream(input), new HeaderListener() {
|
||||||
|
@Override
|
||||||
|
public void addHeader(byte[] name, byte[] value, boolean sensitive) {
|
||||||
|
bh.consume(sensitive);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
decoder.endHeaderBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getSerializedHeaders(List<Header> headers, boolean sensitive)
|
||||||
|
throws IOException {
|
||||||
|
Encoder encoder = new Encoder(4096);
|
||||||
|
|
||||||
|
ByteArrayOutputStream outputStream = size.newOutputStream();
|
||||||
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
Header header = headers.get(i);
|
||||||
|
encoder.encodeHeader(outputStream, header.name, header.value, sensitive);
|
||||||
|
}
|
||||||
|
return outputStream.toByteArray();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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 io.netty.microbench.http2.hpack;
|
||||||
|
|
||||||
|
import io.netty.handler.codec.http2.hpack.Encoder;
|
||||||
|
import io.netty.microbench.util.AbstractMicrobenchmark;
|
||||||
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
|
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||||
|
import org.openjdk.jmh.annotations.Level;
|
||||||
|
import org.openjdk.jmh.annotations.Mode;
|
||||||
|
import org.openjdk.jmh.annotations.Param;
|
||||||
|
import org.openjdk.jmh.annotations.Setup;
|
||||||
|
import org.openjdk.jmh.infra.Blackhole;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EncoderBenchmark extends AbstractMicrobenchmark {
|
||||||
|
|
||||||
|
@Param
|
||||||
|
public HeadersSize size;
|
||||||
|
|
||||||
|
@Param({ "4096" })
|
||||||
|
public int maxTableSize;
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public boolean sensitive;
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public boolean duplicates;
|
||||||
|
|
||||||
|
@Param({ "true", "false" })
|
||||||
|
public boolean limitToAscii;
|
||||||
|
|
||||||
|
private List<Header> headers;
|
||||||
|
private ByteArrayOutputStream outputStream;
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
public void setup() {
|
||||||
|
headers = Util.headers(size, limitToAscii);
|
||||||
|
outputStream = size.newOutputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@BenchmarkMode(Mode.Throughput)
|
||||||
|
public void encode(Blackhole bh) throws IOException {
|
||||||
|
Encoder encoder = new Encoder(maxTableSize);
|
||||||
|
outputStream.reset();
|
||||||
|
if (duplicates) {
|
||||||
|
// If duplicates is set, re-add the same header each time.
|
||||||
|
Header header = headers.get(0);
|
||||||
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
encoder.encodeHeader(outputStream, header.name, header.value, sensitive);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i = 0; i < headers.size(); ++i) {
|
||||||
|
Header header = headers.get(i);
|
||||||
|
encoder.encodeHeader(outputStream, header.name, header.value, sensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bh.consume(outputStream);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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 io.netty.microbench.http2.hpack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class representing a single header entry. Used by the benchmarks.
|
||||||
|
*/
|
||||||
|
class Header {
|
||||||
|
private static final String ALPHABET =
|
||||||
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
|
||||||
|
|
||||||
|
final byte[] name;
|
||||||
|
final byte[] value;
|
||||||
|
|
||||||
|
Header(byte[] name, byte[] value) {
|
||||||
|
this.name = name;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a number of random headers with the given name/value lengths.
|
||||||
|
*/
|
||||||
|
static List<Header> createHeaders(int numHeaders, int nameLength, int valueLength,
|
||||||
|
boolean limitToAscii) {
|
||||||
|
List<Header> headers = new ArrayList<Header>(numHeaders);
|
||||||
|
for (int i = 0; i < numHeaders; ++i) {
|
||||||
|
byte[] name = randomBytes(new byte[nameLength], limitToAscii);
|
||||||
|
byte[] value = randomBytes(new byte[valueLength], limitToAscii);
|
||||||
|
headers.add(new Header(name, value));
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] randomBytes(byte[] bytes, boolean limitToAscii) {
|
||||||
|
Random r = new Random();
|
||||||
|
if (limitToAscii) {
|
||||||
|
for (int index = 0; index < bytes.length; ++index) {
|
||||||
|
int charIndex = r.nextInt(ALPHABET.length());
|
||||||
|
bytes[index] = (byte) ALPHABET.charAt(charIndex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.nextBytes(bytes);
|
||||||
|
}
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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 io.netty.microbench.http2.hpack;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum that indicates the size of the headers to be used for the benchmark.
|
||||||
|
*/
|
||||||
|
public enum HeadersSize {
|
||||||
|
SMALL(5, 20, 40),
|
||||||
|
MEDIUM(20, 40, 80),
|
||||||
|
LARGE(100, 100, 300);
|
||||||
|
|
||||||
|
private final int numHeaders;
|
||||||
|
private final int nameLength;
|
||||||
|
private final int valueLength;
|
||||||
|
|
||||||
|
HeadersSize(int numHeaders, int nameLength, int valueLength) {
|
||||||
|
this.numHeaders = numHeaders;
|
||||||
|
this.nameLength = nameLength;
|
||||||
|
this.valueLength = valueLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Header> newHeaders(boolean limitAscii) {
|
||||||
|
return Header.createHeaders(numHeaders, nameLength, valueLength, limitAscii);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayOutputStream newOutputStream() {
|
||||||
|
return new ByteArrayOutputStream(numHeaders * (nameLength + valueLength));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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 io.netty.microbench.http2.hpack;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods for hpack tests.
|
||||||
|
*/
|
||||||
|
public final class Util {
|
||||||
|
private Util() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal key used to index a particular set of headers in the map.
|
||||||
|
*/
|
||||||
|
private static class HeadersKey {
|
||||||
|
final HeadersSize size;
|
||||||
|
final boolean limitToAscii;
|
||||||
|
|
||||||
|
public HeadersKey(HeadersSize size, boolean limitToAscii) {
|
||||||
|
this.size = size;
|
||||||
|
this.limitToAscii = limitToAscii;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Header> newHeaders() {
|
||||||
|
return size.newHeaders(limitToAscii);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HeadersKey that = (HeadersKey) o;
|
||||||
|
|
||||||
|
if (limitToAscii != that.limitToAscii) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return size == that.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = size.hashCode();
|
||||||
|
result = 31 * result + (limitToAscii ? 1 : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<HeadersKey, List<Header>> headersMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
HeadersSize[] sizes = HeadersSize.values();
|
||||||
|
headersMap = new HashMap<HeadersKey, List<Header>>(sizes.length * 2);
|
||||||
|
for (HeadersSize size : sizes) {
|
||||||
|
HeadersKey key = new HeadersKey(size, true);
|
||||||
|
headersMap.put(key, key.newHeaders());
|
||||||
|
|
||||||
|
key = new HeadersKey(size, false);
|
||||||
|
headersMap.put(key, key.newHeaders());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets headers for the given size and whether the key/values should be limited to ASCII.
|
||||||
|
*/
|
||||||
|
static List<Header> headers(HeadersSize size, boolean limitToAscii) {
|
||||||
|
return headersMap.get(new HeadersKey(size, limitToAscii));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Twitter, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Benchmarks for {@link io.netty.handler.codec.http2.hpack}.
|
||||||
|
*/
|
||||||
|
package io.netty.microbench.http2.hpack;
|
13
pom.xml
13
pom.xml
@ -671,11 +671,6 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- SPDY and HTTP/2 - completely optional -->
|
<!-- SPDY and HTTP/2 - completely optional -->
|
||||||
<dependency>
|
|
||||||
<groupId>com.twitter</groupId>
|
|
||||||
<artifactId>hpack</artifactId>
|
|
||||||
<version>v1.0.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.npn</groupId>
|
<groupId>org.eclipse.jetty.npn</groupId>
|
||||||
<artifactId>npn-api</artifactId>
|
<artifactId>npn-api</artifactId>
|
||||||
@ -894,6 +889,14 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test dependency used by http/2 hpack -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.3.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Test suite dependency for generating a compressed heap dump file -->
|
<!-- Test suite dependency for generating a compressed heap dump file -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.tukaani</groupId>
|
<groupId>org.tukaani</groupId>
|
||||||
|
Loading…
Reference in New Issue
Block a user