blob: 674978fca2138a173a143192f2752bc6dbec826d [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.http2.hpack;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
/* ------------------------------------------------------------ */
/**
*/
public class HpackContextTest
{
@Test
public void testStaticName()
{
HpackContext ctx = new HpackContext(4096);
Entry entry=ctx.get(":method");
assertEquals(":method",entry.getHttpField().getName());
Assert.assertTrue(entry.isStatic());
Assert.assertThat(entry.toString(),Matchers.startsWith("{S,2,:method: "));
}
@Test
public void testEmptyAdd()
{
HpackContext ctx = new HpackContext(0);
HttpField field = new HttpField("foo","bar");
Assert.assertNull(ctx.add(field));
}
@Test
public void testTooBigAdd()
{
HpackContext ctx = new HpackContext(37);
HttpField field = new HttpField("foo","bar");
Assert.assertNull(ctx.add(field));
}
@Test
public void testJustRight()
{
HpackContext ctx = new HpackContext(38);
HttpField field = new HttpField("foo","bar");
Entry entry=ctx.add(field);
Assert.assertNotNull(entry);
Assert.assertThat(entry.toString(),Matchers.startsWith("{D,0,foo: bar,"));
}
@Test
public void testEvictOne()
{
HpackContext ctx = new HpackContext(38);
HttpField field0 = new HttpField("foo","bar");
assertEquals(field0,ctx.add(field0).getHttpField());
assertEquals(field0,ctx.get("foo").getHttpField());
HttpField field1 = new HttpField("xxx","yyy");
assertEquals(field1,ctx.add(field1).getHttpField());
assertNull(ctx.get(field0));
assertNull(ctx.get("foo"));
assertEquals(field1,ctx.get(field1).getHttpField());
assertEquals(field1,ctx.get("xxx").getHttpField());
}
@Test
public void testEvictNames()
{
HpackContext ctx = new HpackContext(38*2);
HttpField[] field =
{
new HttpField("name","v0"),
new HttpField("name","v1"),
new HttpField("name","v2"),
new HttpField("name","v3"),
new HttpField("name","v4"),
new HttpField("name","v5"),
};
Entry[] entry = new Entry[field.length];
// Add 2 name entries to fill table
for (int i=0;i<=1;i++)
entry[i]=ctx.add(field[i]);
// check there is a name reference and it is the most recent added
assertEquals(entry[1],ctx.get("name"));
// Add 1 other entry to table and evict 1
ctx.add(new HttpField("xxx","yyy"));
// check the name reference has been not been evicted
assertEquals(entry[1],ctx.get("name"));
// Add 1 other entry to table and evict 1
ctx.add(new HttpField("foo","bar"));
// name is evicted
assertNull(ctx.get("name"));
}
@Test
public void testGetAddStatic()
{
HpackContext ctx = new HpackContext(4096);
// Look for the field. Should find static version.
HttpField methodGet = new HttpField(":method","GET");
assertEquals(methodGet,ctx.get(methodGet).getHttpField());
assertTrue(ctx.get(methodGet).isStatic());
// Add static version to dynamic table
Entry e0=ctx.add(ctx.get(methodGet).getHttpField());
// Look again and should see dynamic version
assertEquals(methodGet,ctx.get(methodGet).getHttpField());
assertFalse(methodGet==ctx.get(methodGet).getHttpField());
assertFalse(ctx.get(methodGet).isStatic());
// Duplicates allows
Entry e1=ctx.add(ctx.get(methodGet).getHttpField());
// Look again and should see dynamic version
assertEquals(methodGet,ctx.get(methodGet).getHttpField());
assertFalse(methodGet==ctx.get(methodGet).getHttpField());
assertFalse(ctx.get(methodGet).isStatic());
assertFalse(e0==e1);
}
@Test
public void testGetAddStaticName()
{
HpackContext ctx = new HpackContext(4096);
HttpField methodOther = new HttpField(":method","OTHER");
// Look for the field by name. Should find static version.
assertEquals(":method",ctx.get(":method").getHttpField().getName());
assertTrue(ctx.get(":method").isStatic());
// Add dynamic entry with method
ctx.add(methodOther);
// Look for the field by name. Should find static version.
assertEquals(":method",ctx.get(":method").getHttpField().getName());
assertTrue(ctx.get(":method").isStatic());
}
@Test
public void testIndexes()
{
// Only enough space for 5 entries
HpackContext ctx = new HpackContext(38*5);
HttpField methodPost = new HttpField(":method","POST");
HttpField[] field =
{
new HttpField("fo0","b0r"),
new HttpField("fo1","b1r"),
new HttpField("fo2","b2r"),
new HttpField("fo3","b3r"),
new HttpField("fo4","b4r"),
new HttpField("fo5","b5r"),
new HttpField("fo6","b6r"),
new HttpField("fo7","b7r"),
new HttpField("fo8","b8r"),
new HttpField("fo9","b9r"),
new HttpField("foA","bAr"),
};
Entry[] entry = new Entry[100];
// Lookup the index of a static field
assertEquals(0,ctx.size());
assertEquals(":authority",ctx.get(1).getHttpField().getName());
assertEquals(3,ctx.index(ctx.get(methodPost)));
assertEquals(methodPost,ctx.get(3).getHttpField());
assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
assertEquals(null,ctx.get(62));
// Add a single entry
entry[0]=ctx.add(field[0]);
// Check new entry is 62
assertEquals(1,ctx.size());
assertEquals(62,ctx.index(entry[0]));
assertEquals(entry[0],ctx.get(62));
// and statics still OK
assertEquals(":authority",ctx.get(1).getHttpField().getName());
assertEquals(3,ctx.index(ctx.get(methodPost)));
assertEquals(methodPost,ctx.get(3).getHttpField());
assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
assertEquals(null,ctx.get(62+ctx.size()));
// Add 4 more entries
for (int i=1;i<=4;i++)
entry[i]=ctx.add(field[i]);
// Check newest entry is at 62 oldest at 66
assertEquals(5,ctx.size());
int index=66;
for (int i=0;i<=4;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// and statics still OK
assertEquals(":authority",ctx.get(1).getHttpField().getName());
assertEquals(3,ctx.index(ctx.get(methodPost)));
assertEquals(methodPost,ctx.get(3).getHttpField());
assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
assertEquals(null,ctx.get(62+ctx.size()));
// add 1 more entry and this should cause an eviction!
entry[5]=ctx.add(field[5]);
// Check newest entry is at 1 oldest at 5
index=66;
for (int i=1;i<=5;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// check entry 0 evicted
assertNull(ctx.get(field[0]));
assertEquals(0,ctx.index(entry[0]));
// and statics still OK
assertEquals(":authority",ctx.get(1).getHttpField().getName());
assertEquals(3,ctx.index(ctx.get(methodPost)));
assertEquals(methodPost,ctx.get(3).getHttpField());
assertEquals("www-authenticate",ctx.get(61).getHttpField().getName());
assertEquals(null,ctx.get(62+ctx.size()));
// Add 4 more entries
for (int i=6;i<=9;i++)
entry[i]=ctx.add(field[i]);
// Check newest entry is at 1 oldest at 5
index=66;
for (int i=5;i<=9;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// check entry 0-4 evicted
for (int i=0;i<=4;i++)
{
assertNull(ctx.get(field[i]));
assertEquals(0,ctx.index(entry[i]));
}
// Add new entries enough so that array queue will wrap
for (int i=10;i<=52;i++)
entry[i]=ctx.add(new HttpField("n"+i,"v"+i));
index=66;
for (int i=48;i<=52;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
}
@Test
public void testResize()
{
// Only enough space for 5 entries
HpackContext ctx = new HpackContext(38*5);
HttpField[] field =
{
new HttpField("fo0","b0r"),
new HttpField("fo1","b1r"),
new HttpField("fo2","b2r"),
new HttpField("fo3","b3r"),
new HttpField("fo4","b4r"),
new HttpField("fo5","b5r"),
new HttpField("fo6","b6r"),
new HttpField("fo7","b7r"),
new HttpField("fo8","b8r"),
new HttpField("fo9","b9r"),
new HttpField("foA","bAr"),
};
Entry[] entry = new Entry[field.length];
// Add 5 entries
for (int i=0;i<=4;i++)
entry[i]=ctx.add(field[i]);
assertEquals(5,ctx.size());
// check indexes
int index=66;
for (int i=0;i<=4;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// resize so that only 2 entries may be held
ctx.resize(38*2);
assertEquals(2,ctx.size());
// check indexes
index=63;
for (int i=3;i<=4;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// resize so that 6.5 entries may be held
ctx.resize(38*6+19);
assertEquals(2,ctx.size());
// check indexes
index=63;
for (int i=3;i<=4;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// Add 5 entries
for (int i=5;i<=9;i++)
entry[i]=ctx.add(field[i]);
assertEquals(6,ctx.size());
// check indexes
index=67;
for (int i=4;i<=9;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// resize so that only 100 entries may be held
ctx.resize(38*100);
assertEquals(6,ctx.size());
// check indexes
index=67;
for (int i=4;i<=9;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
// add 50 fields
for (int i=0;i<50;i++)
ctx.add(new HttpField("n"+i,"v"+i));
// check indexes
index=67+50;
for (int i=4;i<=9;i++)
{
assertEquals(index,ctx.index(entry[i]));
assertEquals(entry[i],ctx.get(index));
index--;
}
}
@Test
public void testStaticHuffmanValues()
{
HpackContext ctx = new HpackContext(4096);
for (int i=2;i<=14;i++)
{
Entry entry=ctx.get(i);
assertTrue(entry.isStatic());
ByteBuffer buffer = ByteBuffer.wrap(entry.getStaticHuffmanValue());
int huff = 0xff&buffer.get();
assertTrue((0x80&huff)==0x80);
int len = NBitInteger.decode(buffer,7);
assertEquals(len,buffer.remaining());
String value = Huffman.decode(buffer);
assertEquals(entry.getHttpField().getValue(),value);
}
}
@Test
public void testNameInsensitivity()
{
HpackContext ctx = new HpackContext(4096);
assertEquals("content-length",ctx.get("content-length").getHttpField().getName());
assertEquals("content-length",ctx.get("Content-Length").getHttpField().getName());
assertTrue(ctx.get("Content-Length").isStatic());
assertTrue(ctx.get("Content-Type").isStatic());
ctx.add(new HttpField("Wibble","Wobble"));
assertEquals("Wibble",ctx.get("wibble").getHttpField().getName());
assertEquals("Wibble",ctx.get("Wibble").getHttpField().getName());
}
}