So I'm building a HTTP/2.0 server library for a school project, and I've stumbled upon a problem. Whenever I try to send a headers frame I get a compression error from Chrome (and Firefox, no response from Edge). I'm using the twitter/hpack library to compress and decompress:
public static ByteBuffer compress(ByteBuffer bb) {
bb.rewind();
Encoder encoder = new Encoder(4096);
byte[] bytes = new byte[bb.remaining()];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = bb.get();
}
String string = new String(bytes);
String[] split = string.split("[\\n\\r]+");
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
for (String s1 : split) {
String[] s1Split = s1.split(":", 3);
if (s1Split.length == 3)
encoder.encodeHeader(os, (":" + s1Split[1]).getBytes(), s1Split[2].getBytes(), false);
else if (s1Split.length == 2)
encoder.encodeHeader(os, s1Split[0].getBytes(), s1Split[1].getBytes(), false);
}
} catch (IOException e) {
e.printStackTrace();
}
return ByteBuffer.wrap(os.toByteArray());
}
Where bb is just a byte buffer containing the string:
":status:200\r\ncontent-length:155\r\ncontent-type:text/html;charset=utf-8\r\n"
I am able to successfully decompress the result again, as well as the compressed GET header initially received from Chrome, but when I try to send the compressed result to Chrome, it gives me an error. Here are prints of the frames sent/received:
Send: frames.SettingsFrame: length=0, flags=0b0, streamId=0, settings={Settings: [SETTINGS_HEADER_TABLE_SIZE=UNDEFINED, SETTINGS_ENABLE_PUSH=UNDEFINED, SETTINGS_MAX_CONCURRENT_STREAMS=UNDEFINED, SETTINGS_INITIAL_WINDOW_SIZE=UNDEFINED, SETTINGS_MAX_FRAME_SIZE=UNDEFINED, SETTINGS_MAX_HEADER_LIST_SIZE=UNDEFINED]}
Recv: frames.HeadersFrame: length=301, flags=0b100101, streamId=1, padLength=0, E=true, streamDependency=0, weight=256, headerBlockFragment={:method: GET\r\n:authority: localhost\r\n:scheme: https\r\n:path: /\r\ncache-control: max-age=0\r\nupgrade-insecure-requests: 1\r\nuser-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\r\naccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\naccept-encoding: gzip, deflate, br\r\naccept-language: nb-NO,nb;q=0.9,no;q=0.8,nn;q=0.7,en-US;q=0.6,en;q=0.5\r\ncookie: Idea-cda28813=32d9f659-df76-4e58-9116-41939eaf3d24\r\n}
Send: frames.HeadersFrame: length=76, flags=0b100100, streamId=1, padLength=0, E=true, streamDependency=0, weight=256, headerBlockFragment={:status:200\r\ncontent-length:155\r\ncontent-type:text/html;charset=utf-8\r\n}
Send: frames.DataFrame: length=152, flags=0b1, streamId=1, padLength=0, data={<!DOCTYPE html>\r\n<html lang="en">\r\n<head>\r\n <meta charset="UTF-8">\r\n <title>Hello</title>\r\n</head>\r\n<body>\r\n<p>Hello world!</p>\r\n</body>\r\n</html>}
Recv: frames.SettingsFrame: length=0, flags=0b1, streamId=0, settings={Settings: [SETTINGS_HEADER_TABLE_SIZE=UNDEFINED, SETTINGS_ENABLE_PUSH=UNDEFINED, SETTINGS_MAX_CONCURRENT_STREAMS=UNDEFINED, SETTINGS_INITIAL_WINDOW_SIZE=UNDEFINED, SETTINGS_MAX_FRAME_SIZE=UNDEFINED, SETTINGS_MAX_HEADER_LIST_SIZE=UNDEFINED]}
Recv: frames.GoAwayFrame: length=45, flags=0b0, streamId=0, lastStreamId=0, errorCode=Compression error, additionalData={Framer error: 6 (DECOMPRESS_FAILURE).}
Also if I make any of the header names uppercase then Chrome responds with the error:
HTTP2_SESSION_RECV_INVALID_HEADER
--> error = "Upper case characters in header name."
--> header_name = "Content-type"
--> header_value = "text/html;charset=utf-8"
Meaning that Chrome is also able to decompress some of it, but still fails when going through the entire thing.
Are you sure the length of the frame is correct? You have to use the length of the frame compressed. Not the length before compression.