| // |
| // ======================================================================== |
| // 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()); |
| |
| } |
| } |