Skip to content

Commit

Permalink
gh-30 add huffman coding for header
Browse files Browse the repository at this point in the history
  • Loading branch information
Unknow0 committed Jun 23, 2024
1 parent dbf4890 commit 047761a
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,16 @@ private static String readData(InputStream in) throws IOException {

private static void writeData(Buffers out, String value) throws InterruptedException {
byte[] bytes = value.getBytes(StandardCharsets.US_ASCII);
// TODO huffman

writeInt(out, 0, 7, bytes.length);
out.write(bytes);
Buffers b = new Buffers();
Http2Huffman.encode(b, bytes);
if (b.length() < bytes.length) {
writeInt(out, 0x80, 7, b.length());
b.read(out, -1, false);
} else {
writeInt(out, 0, 7, bytes.length);
out.write(bytes);
}
}

protected Entry get(int i) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.io.IOException;
import java.io.InputStream;

import unknow.server.util.io.Buffers;

/**
* HPACK static huffman table
* https://httpwg.org/specs/rfc7541.html#huffman.code
Expand Down Expand Up @@ -37,6 +39,29 @@ public class Http2Huffman {
10, 13, 22, 256 // 29, 30
};

private static final int[] 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 };

private static final int[] sizes = { 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 };

private Http2Huffman() {
}

Expand Down Expand Up @@ -71,6 +96,33 @@ public static String decode(InputStream b, int max, StringBuilder sb) throws IOE
return sb.toString();
}

public static void encode(Buffers b, byte[] data) throws InterruptedException {
C c = new C();
for (int i = 0; i < data.length; i++)
encode(c, b, data[i]);
if (c.cnt != 0)
b.write(c.bit | (0xff >> c.cnt));
}

private static void encode(C c, Buffers buf, byte b) throws InterruptedException {
int code = codes[b];
int size = sizes[b];

while (size > 0) {
int r = 8 - c.cnt;
if (size <= r) {
c.cnt += size;
c.bit |= (code << (r - size)) & 0xFF;
return;
}

size -= r;
buf.write(c.bit | (code >> size) & 0xFF);
c.bit = 0;
c.cnt = 0;
}
}

private static final char read(S s) throws IOException {
int first = 0; /* first code of length len */
int index = 0; /* index of first code of length len in symbol table */
Expand All @@ -97,12 +149,14 @@ private static final char read(S s) throws IOException {
}
}

static final class S {
final InputStream b;
int max;

static class C {
int bit;
int cnt;
}

static class S extends C {
final InputStream b;
int max;

public S(InputStream b, int max) {
this.b = b;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package unknow.server.servlet.http2;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.stream.Stream;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import unknow.server.util.io.Buffers;
import unknow.server.util.io.BuffersUtils;

public class Http2HuffmanTest {
public static Stream<Arguments> input() {
//@formatter:off
return Stream.of(
Arguments.of("www.example.com", new byte[] { b(0xf1), b(0xe3), b(0xc2), b(0xe5), b(0xf2), b(0x3a), b(0x6b), b(0xa0), b(0xab), b(0x90), b(0xf4), b(0xff) }),
Arguments.of("no-cache", new byte[]{b(0xa8),b(0xeb),b(0x10),b(0x64),b(0x9c),b(0xbf)}),
Arguments.of("custom-key", new byte[]{b(0x25),b(0xa8),b(0x49),b(0xe9),b(0x5b),b(0xa9),b(0x7d),b(0x7f)}),
Arguments.of("custom-value", new byte[]{b(0x25),b(0xa8),b(0x49),b(0xe9),b(0x5b),b(0xb8),b(0xe8),b(0xb4),b(0xbf)})
); //@formatter:on
}

@ParameterizedTest
@MethodSource("input")
public void decode(String decoded, byte[] encoded) throws IOException {
StringBuilder sb = new StringBuilder();
try (InputStream b = new ByteArrayInputStream(encoded)) {
Http2Huffman.decode(b, encoded.length, sb);
}
assertEquals(decoded, sb.toString());
}

@ParameterizedTest
@MethodSource("input")
public void encode(String decoded, byte[] encoded) throws InterruptedException {
byte[] bytes = decoded.getBytes(StandardCharsets.US_ASCII);
Buffers b = new Buffers();
Http2Huffman.encode(b, bytes);
byte[] array = BuffersUtils.toArray(b, 0, -1);
assertArrayEquals(encoded, array);
}

public static final byte b(int i) {
return (byte) (i & 0xFF);
}
}

0 comments on commit 047761a

Please sign in to comment.