package test.codec.http2.hpack;
import com.firefly.codec.http2.hpack.HpackContext;
import com.firefly.codec.http2.hpack.HpackDecoder;
import com.firefly.codec.http2.hpack.HpackEncoder;
import com.firefly.codec.http2.model.*;
import com.firefly.codec.http2.model.MetaData.Response;
import com.firefly.utils.io.BufferUtils;
import org.junit.Assert;
import org.junit.Test;
import java.nio.ByteBuffer;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class HpackTest {
final static HttpField ServerFirefly = new PreEncodedHttpField(HttpHeader.SERVER, "firefly");
final static HttpField XPowerFirefly = new PreEncodedHttpField(HttpHeader.X_POWERED_BY, "firefly");
final static HttpField Date = new PreEncodedHttpField(HttpHeader.DATE, DateGenerator.formatDate(System.currentTimeMillis()));
@Test
public void encodeDecodeResponseTest() {
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(4096, 8192);
ByteBuffer buffer = BufferUtils.allocate(16 * 1024);
HttpFields fields0 = new HttpFields();
fields0.add(HttpHeader.CONTENT_TYPE, "text/html");
fields0.add(HttpHeader.CONTENT_LENGTH, "1024");
fields0.add(new HttpField(HttpHeader.CONTENT_ENCODING, (String) null));
fields0.add(ServerFirefly);
fields0.add(XPowerFirefly);
fields0.add(Date);
fields0.add(HttpHeader.SET_COOKIE, "abcdefghijklmnopqrstuvwxyz");
fields0.add("custom-key", "custom-value");
Response original0 = new MetaData.Response(HttpVersion.HTTP_2, 200, fields0);
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original0);
BufferUtils.flipToFlush(buffer, 0);
Response decoded0 = (Response) decoder.decode(buffer);
original0.getFields().put(new HttpField(HttpHeader.CONTENT_ENCODING, ""));
assertMetadataSame(original0, decoded0);
// Same again?
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original0);
BufferUtils.flipToFlush(buffer, 0);
Response decoded0b = (Response) decoder.decode(buffer);
assertMetadataSame(original0, decoded0b);
HttpFields fields1 = new HttpFields();
fields1.add(HttpHeader.CONTENT_TYPE, "text/plain");
fields1.add(HttpHeader.CONTENT_LENGTH, "1234");
fields1.add(HttpHeader.CONTENT_ENCODING, " ");
fields1.add(ServerFirefly);
fields1.add(XPowerFirefly);
fields1.add(Date);
fields1.add("Custom-Key", "Other-Value");
Response original1 = new MetaData.Response(HttpVersion.HTTP_2, 200, fields1);
// Same again?
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original1);
BufferUtils.flipToFlush(buffer, 0);
Response decoded1 = (Response) decoder.decode(buffer);
assertMetadataSame(original1, decoded1);
Assert.assertEquals("custom-key", decoded1.getFields().getField("Custom-Key").getName());
}
@Test
public void encodeDecodeTooLargeTest() {
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(4096, 164);
ByteBuffer buffer = BufferUtils.allocate(16 * 1024);
HttpFields fields0 = new HttpFields();
fields0.add("1234567890", "1234567890123456789012345678901234567890");
fields0.add("Cookie", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
MetaData original0 = new MetaData(HttpVersion.HTTP_2, fields0);
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original0);
BufferUtils.flipToFlush(buffer, 0);
MetaData decoded0 = (MetaData) decoder.decode(buffer);
assertMetadataSame(original0, decoded0);
HttpFields fields1 = new HttpFields();
fields1.add("1234567890", "1234567890123456789012345678901234567890");
fields1.add("Cookie", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
fields1.add("x", "y");
MetaData original1 = new MetaData(HttpVersion.HTTP_2, fields1);
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original1);
BufferUtils.flipToFlush(buffer, 0);
try {
decoder.decode(buffer);
Assert.fail();
} catch (BadMessageException e) {
assertEquals(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE_431, e.getCode());
}
}
@Test
public void evictReferencedFieldTest() {
HpackEncoder encoder = new HpackEncoder(200, 200);
HpackDecoder decoder = new HpackDecoder(200, 1024);
ByteBuffer buffer = BufferUtils.allocate(16 * 1024);
HttpFields fields0 = new HttpFields();
fields0.add("123456789012345678901234567890123456788901234567890", "value");
fields0.add("foo", "abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
MetaData original0 = new MetaData(HttpVersion.HTTP_2, fields0);
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original0);
BufferUtils.flipToFlush(buffer, 0);
MetaData decoded0 = (MetaData) decoder.decode(buffer);
assertEquals(2, encoder.getHpackContext().size());
assertEquals(2, decoder.getHpackContext().size());
assertEquals("123456789012345678901234567890123456788901234567890", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName());
assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 0).getHttpField().getName());
assertMetadataSame(original0, decoded0);
HttpFields fields1 = new HttpFields();
fields1.add("123456789012345678901234567890123456788901234567890", "other_value");
fields1.add("x", "y");
MetaData original1 = new MetaData(HttpVersion.HTTP_2, fields1);
BufferUtils.clearToFill(buffer);
encoder.encode(buffer, original1);
BufferUtils.flipToFlush(buffer, 0);
MetaData decoded1 = (MetaData) decoder.decode(buffer);
assertMetadataSame(original1, decoded1);
assertEquals(2, encoder.getHpackContext().size());
assertEquals(2, decoder.getHpackContext().size());
assertEquals("x", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 0).getHttpField().getName());
assertEquals("foo", encoder.getHpackContext().get(HpackContext.STATIC_TABLE.length + 1).getHttpField().getName());
}
private void assertMetadataSame(MetaData.Response expected, MetaData.Response actual) {
assertThat("Response.status", actual.getStatus(), is(expected.getStatus()));
assertThat("Response.reason", actual.getReason(), is(expected.getReason()));
assertMetadataSame((MetaData) expected, (MetaData) actual);
}
private void assertMetadataSame(MetaData expected, MetaData actual) {
assertThat("Metadata.contentLength", actual.getContentLength(), is(expected.getContentLength()));
assertThat("Metadata.version" + ".version", actual.getHttpVersion(), is(expected.getHttpVersion()));
assertHttpFieldsSame("Metadata.fields", expected.getFields(), actual.getFields());
}
private void assertHttpFieldsSame(String msg, HttpFields expected, HttpFields actual) {
assertThat(msg + ".size", actual.size(), is(expected.size()));
for (HttpField actualField : actual) {
if ("DATE".equalsIgnoreCase(actualField.getName())) {
// skip comparison on Date, as these values can often differ by 1 second
// during testing.
continue;
}
assertThat(msg + ".contains(" + actualField + ")", expected.contains(actualField), is(true));
}
}
}