src: Compile defaltehd and inflatehd with c++
This commit also fixes defaltehd always reports output length is 0.
This commit is contained in:
parent
34581d830d
commit
f011bda377
|
@ -141,8 +141,8 @@ bin_PROGRAMS += inflatehd deflatehd
|
||||||
|
|
||||||
HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h
|
HPACK_TOOLS_COMMON_SRCS = comp_helper.c comp_helper.h
|
||||||
|
|
||||||
inflatehd_SOURCES = inflatehd.c $(HPACK_TOOLS_COMMON_SRCS)
|
inflatehd_SOURCES = inflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
|
||||||
|
|
||||||
deflatehd_SOURCES = deflatehd.c $(HPACK_TOOLS_COMMON_SRCS)
|
deflatehd_SOURCES = deflatehd.cc $(HPACK_TOOLS_COMMON_SRCS)
|
||||||
|
|
||||||
endif # ENABLE_HPACK_TOOLS
|
endif # ENABLE_HPACK_TOOLS
|
||||||
|
|
|
@ -26,21 +26,28 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif /* HAVE_CONFIG_H */
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
#include <cstdio>
|
||||||
#include <stdlib.h>
|
#include <cstring>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
#include "nghttp2_hd.h"
|
#include "nghttp2_hd.h"
|
||||||
#include "nghttp2_frame.h"
|
#include "nghttp2_frame.h"
|
||||||
|
|
||||||
#include "comp_helper.h"
|
#include "comp_helper.h"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
size_t table_size;
|
size_t table_size;
|
||||||
size_t deflate_table_size;
|
size_t deflate_table_size;
|
||||||
|
@ -73,31 +80,22 @@ static void to_hex(char *dest, const uint8_t *src, size_t len)
|
||||||
|
|
||||||
static void output_to_json(nghttp2_hd_deflater *deflater,
|
static void output_to_json(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_bufs *bufs, size_t inputlen,
|
nghttp2_bufs *bufs, size_t inputlen,
|
||||||
nghttp2_nv *nva, size_t nvlen,
|
const std::vector<nghttp2_nv> nva,
|
||||||
int seq)
|
int seq)
|
||||||
{
|
{
|
||||||
json_t *obj;
|
auto len = nghttp2_bufs_len(bufs);
|
||||||
char *hex = NULL, *hexp;
|
auto hex = std::vector<char>(len * 2);
|
||||||
size_t len;
|
auto obj = json_object();
|
||||||
nghttp2_buf_chain *ci;
|
|
||||||
nghttp2_buf *buf;
|
|
||||||
|
|
||||||
len = nghttp2_bufs_len(bufs);
|
|
||||||
|
|
||||||
if(len > 0) {
|
|
||||||
hex = malloc(len * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = json_object();
|
|
||||||
json_object_set_new(obj, "seq", json_integer(seq));
|
json_object_set_new(obj, "seq", json_integer(seq));
|
||||||
json_object_set_new(obj, "input_length", json_integer(inputlen));
|
json_object_set_new(obj, "input_length", json_integer(inputlen));
|
||||||
json_object_set_new(obj, "output_length", json_integer(len));
|
json_object_set_new(obj, "output_length", json_integer(len));
|
||||||
json_object_set_new(obj, "percentage_of_original_size",
|
json_object_set_new(obj, "percentage_of_original_size",
|
||||||
json_real((double)len / inputlen * 100));
|
json_real((double)len / inputlen * 100));
|
||||||
|
|
||||||
hexp = hex;
|
auto hexp = hex.data();
|
||||||
for(ci = bufs->head; ci; ci = ci->next) {
|
for(auto ci = bufs->head; ci; ci = ci->next) {
|
||||||
buf = &ci->buf;
|
auto buf = &ci->buf;
|
||||||
to_hex(hexp, buf->pos, nghttp2_buf_len(buf));
|
to_hex(hexp, buf->pos, nghttp2_buf_len(buf));
|
||||||
hexp += nghttp2_buf_len(buf);
|
hexp += nghttp2_buf_len(buf);
|
||||||
}
|
}
|
||||||
|
@ -105,9 +103,9 @@ static void output_to_json(nghttp2_hd_deflater *deflater,
|
||||||
if(len == 0) {
|
if(len == 0) {
|
||||||
json_object_set_new(obj, "wire", json_string(""));
|
json_object_set_new(obj, "wire", json_string(""));
|
||||||
} else {
|
} else {
|
||||||
json_object_set_new(obj, "wire", json_pack("s#", hex, len * 2));
|
json_object_set_new(obj, "wire", json_pack("s#", hex.data(), hex.size()));
|
||||||
}
|
}
|
||||||
json_object_set_new(obj, "headers", dump_headers(nva, nvlen));
|
json_object_set_new(obj, "headers", dump_headers(nva.data(), nva.size()));
|
||||||
if(seq == 0) {
|
if(seq == 0) {
|
||||||
/* We only change the header table size only once at the beginning */
|
/* We only change the header table size only once at the beginning */
|
||||||
json_object_set_new(obj, "header_table_size",
|
json_object_set_new(obj, "header_table_size",
|
||||||
|
@ -120,40 +118,37 @@ static void output_to_json(nghttp2_hd_deflater *deflater,
|
||||||
json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2));
|
json_dumpf(obj, stdout, JSON_PRESERVE_ORDER | JSON_INDENT(2));
|
||||||
printf("\n");
|
printf("\n");
|
||||||
json_decref(obj);
|
json_decref(obj);
|
||||||
free(hex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deflate_hd(nghttp2_hd_deflater *deflater,
|
static void deflate_hd(nghttp2_hd_deflater *deflater,
|
||||||
nghttp2_nv *nva, size_t nvlen, size_t inputlen, int seq)
|
const std::vector<nghttp2_nv>& nva,
|
||||||
|
size_t inputlen, int seq)
|
||||||
{
|
{
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
nghttp2_bufs bufs;
|
nghttp2_bufs bufs;
|
||||||
|
|
||||||
nghttp2_bufs_init2(&bufs, 4096, 16, 0);
|
nghttp2_bufs_init2(&bufs, 4096, 16, 0);
|
||||||
|
|
||||||
rv = nghttp2_hd_deflate_hd(deflater, &bufs, nva, nvlen);
|
rv = nghttp2_hd_deflate_hd(deflater, &bufs,
|
||||||
|
(nghttp2_nv*)nva.data(), nva.size());
|
||||||
if(rv < 0) {
|
if(rv < 0) {
|
||||||
fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq);
|
fprintf(stderr, "deflate failed with error code %zd at %d\n", rv, seq);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_sum += inputlen;
|
input_sum += inputlen;
|
||||||
output_sum += rv;
|
output_sum += nghttp2_bufs_len(&bufs);
|
||||||
|
|
||||||
output_to_json(deflater, &bufs, inputlen, nva, nvlen, seq);
|
output_to_json(deflater, &bufs, inputlen, nva, seq);
|
||||||
nghttp2_bufs_free(&bufs);
|
nghttp2_bufs_free(&bufs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||||
{
|
{
|
||||||
json_t *js;
|
|
||||||
nghttp2_nv nva[128];
|
|
||||||
size_t len;
|
|
||||||
size_t i;
|
|
||||||
size_t inputlen = 0;
|
size_t inputlen = 0;
|
||||||
|
|
||||||
js = json_object_get(obj, "headers");
|
auto js = json_object_get(obj, "headers");
|
||||||
if(js == NULL) {
|
if(js == nullptr) {
|
||||||
fprintf(stderr, "'headers' key is missing at %d\n", seq);
|
fprintf(stderr, "'headers' key is missing at %d\n", seq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -162,20 +157,20 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||||
"The value of 'headers' key must be an array at %d\n", seq);
|
"The value of 'headers' key must be an array at %d\n", seq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
len = json_array_size(js);
|
|
||||||
if(len > sizeof(nva)/sizeof(nva[0])) {
|
auto len = json_array_size(js);
|
||||||
fprintf(stderr, "Too many headers (> %zu) at %d\n",
|
auto nva = std::vector<nghttp2_nv>(len);
|
||||||
sizeof(nva)/sizeof(nva[0]), seq);
|
|
||||||
return -1;
|
for(size_t i = 0; i < len; ++i) {
|
||||||
}
|
auto nv_pair = json_array_get(js, i);
|
||||||
for(i = 0; i < len; ++i) {
|
|
||||||
json_t *nv_pair = json_array_get(js, i);
|
|
||||||
const char *name;
|
const char *name;
|
||||||
json_t *value;
|
json_t *value;
|
||||||
|
|
||||||
if(!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) {
|
if(!json_is_object(nv_pair) || json_object_size(nv_pair) != 1) {
|
||||||
fprintf(stderr, "bad formatted name/value pair object at %d\n", seq);
|
fprintf(stderr, "bad formatted name/value pair object at %d\n", seq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object_foreach(nv_pair, name, value) {
|
json_object_foreach(nv_pair, name, value) {
|
||||||
nva[i].name = (uint8_t*)name;
|
nva[i].name = (uint8_t*)name;
|
||||||
nva[i].namelen = strlen(name);
|
nva[i].namelen = strlen(name);
|
||||||
|
@ -184,12 +179,16 @@ static int deflate_hd_json(json_t *obj, nghttp2_hd_deflater *deflater, int seq)
|
||||||
fprintf(stderr, "value is not string at %d\n", seq);
|
fprintf(stderr, "value is not string at %d\n", seq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nva[i].value = (uint8_t*)json_string_value(value);
|
nva[i].value = (uint8_t*)json_string_value(value);
|
||||||
nva[i].valuelen = strlen(json_string_value(value));
|
nva[i].valuelen = strlen(json_string_value(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
inputlen += nva[i].namelen + nva[i].valuelen;
|
inputlen += nva[i].namelen + nva[i].valuelen;
|
||||||
}
|
}
|
||||||
deflate_hd(deflater, nva, len, inputlen, seq);
|
|
||||||
|
deflate_hd(deflater, nva, inputlen, seq);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,31 +206,34 @@ static void deinit_deflater(nghttp2_hd_deflater *deflater)
|
||||||
|
|
||||||
static int perform(void)
|
static int perform(void)
|
||||||
{
|
{
|
||||||
size_t i;
|
|
||||||
json_t *json, *cases;
|
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
size_t len;
|
|
||||||
nghttp2_hd_deflater deflater;
|
nghttp2_hd_deflater deflater;
|
||||||
|
|
||||||
json = json_loadf(stdin, 0, &error);
|
auto json = json_loadf(stdin, 0, &error);
|
||||||
if(json == NULL) {
|
|
||||||
|
if(json == nullptr) {
|
||||||
fprintf(stderr, "JSON loading failed\n");
|
fprintf(stderr, "JSON loading failed\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
cases = json_object_get(json, "cases");
|
|
||||||
if(cases == NULL) {
|
auto cases = json_object_get(json, "cases");
|
||||||
|
|
||||||
|
if(cases == nullptr) {
|
||||||
fprintf(stderr, "Missing 'cases' key in root object\n");
|
fprintf(stderr, "Missing 'cases' key in root object\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!json_is_array(cases)) {
|
if(!json_is_array(cases)) {
|
||||||
fprintf(stderr, "'cases' must be JSON array\n");
|
fprintf(stderr, "'cases' must be JSON array\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
init_deflater(&deflater);
|
init_deflater(&deflater);
|
||||||
output_json_header();
|
output_json_header();
|
||||||
len = json_array_size(cases);
|
auto len = json_array_size(cases);
|
||||||
for(i = 0; i < len; ++i) {
|
|
||||||
json_t *obj = json_array_get(cases, i);
|
for(size_t i = 0; i < len; ++i) {
|
||||||
|
auto obj = json_array_get(cases, i);
|
||||||
if(!json_is_object(obj)) {
|
if(!json_is_object(obj)) {
|
||||||
fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n",
|
fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n",
|
||||||
i);
|
i);
|
||||||
|
@ -253,7 +255,7 @@ static int perform(void)
|
||||||
static int perform_from_http1text(void)
|
static int perform_from_http1text(void)
|
||||||
{
|
{
|
||||||
char line[1 << 14];
|
char line[1 << 14];
|
||||||
nghttp2_nv nva[256];
|
std::vector<nghttp2_nv> nva;
|
||||||
int seq = 0;
|
int seq = 0;
|
||||||
nghttp2_hd_deflater deflater;
|
nghttp2_hd_deflater deflater;
|
||||||
init_deflater(&deflater);
|
init_deflater(&deflater);
|
||||||
|
@ -267,16 +269,18 @@ static int perform_from_http1text(void)
|
||||||
nghttp2_nv *nv;
|
nghttp2_nv *nv;
|
||||||
char *rv = fgets(line, sizeof(line), stdin);
|
char *rv = fgets(line, sizeof(line), stdin);
|
||||||
char *val, *val_end;
|
char *val, *val_end;
|
||||||
if(rv == NULL) {
|
if(rv == nullptr) {
|
||||||
end = 1;
|
end = 1;
|
||||||
break;
|
break;
|
||||||
} else if(line[0] == '\n') {
|
} else if(line[0] == '\n') {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assert(nvlen < sizeof(nva)/sizeof(nva[0]));
|
|
||||||
|
nva.resize(nvlen);
|
||||||
|
|
||||||
nv = &nva[nvlen];
|
nv = &nva[nvlen];
|
||||||
val = strchr(line+1, ':');
|
val = strchr(line+1, ':');
|
||||||
if(val == NULL) {
|
if(val == nullptr) {
|
||||||
fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq);
|
fprintf(stderr, "Bad HTTP/1 header field format at %d.\n", seq);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
@ -299,7 +303,7 @@ static int perform_from_http1text(void)
|
||||||
if(seq > 0) {
|
if(seq > 0) {
|
||||||
printf(",\n");
|
printf(",\n");
|
||||||
}
|
}
|
||||||
deflate_hd(&deflater, nva, nvlen, inputlen, seq);
|
deflate_hd(&deflater, nva, inputlen, seq);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 0; i < nvlen; ++i) {
|
for(i = 0; i < nvlen; ++i) {
|
||||||
|
@ -316,81 +320,82 @@ static int perform_from_http1text(void)
|
||||||
|
|
||||||
static void print_help(void)
|
static void print_help(void)
|
||||||
{
|
{
|
||||||
printf("HPACK HTTP/2 header encoder\n"
|
std::cout << R"(HPACK HTTP/2 header encoder
|
||||||
"Usage: deflatehd [OPTIONS] < INPUT\n"
|
Usage: deflatehd [OPTIONS] < INPUT
|
||||||
"\n"
|
|
||||||
"Reads JSON data or HTTP/1-style header fields from stdin and\n"
|
Reads JSON data or HTTP/1-style header fields from stdin and outputs
|
||||||
"outputs deflated header block in JSON array.\n"
|
deflated header block in JSON array.
|
||||||
"\n"
|
|
||||||
"For the JSON input, the root JSON object must contain \"context\"\n"
|
For the JSON input, the root JSON object must contain "context" key,
|
||||||
"key, which indicates which compression context is used. If it is\n"
|
which indicates which compression context is used. If it is
|
||||||
"\"request\", request compression context is used. Otherwise,\n"
|
"request", request compression context is used. Otherwise, response
|
||||||
"response compression context is used. The value of \"cases\" key\n"
|
compression context is used. The value of "cases" key contains the
|
||||||
"contains the sequence of input header set. They share the same\n"
|
sequence of input header set. They share the same compression context
|
||||||
"compression context and are processed in the order they appear.\n"
|
and are processed in the order they appear. Each item in the sequence
|
||||||
"Each item in the sequence is a JSON object and it must have at\n"
|
is a JSON object and it must have at least "headers" key. Its value
|
||||||
"least \"headers\" key. Its value is an array of a JSON object\n"
|
is an array of a JSON object containing exactly one name/value pair.
|
||||||
"containing exactly one name/value pair.\n"
|
|
||||||
"\n"
|
Example:
|
||||||
"Example:\n"
|
{
|
||||||
"{\n"
|
"context": "request",
|
||||||
" \"context\": \"request\",\n"
|
"cases":
|
||||||
" \"cases\":\n"
|
[
|
||||||
" [\n"
|
{
|
||||||
" {\n"
|
"headers": [
|
||||||
" \"headers\": [\n"
|
{ ":method": "GET" },
|
||||||
" { \":method\": \"GET\" },\n"
|
{ ":path": "/" }
|
||||||
" { \":path\": \"/\" }\n"
|
]
|
||||||
" ]\n"
|
},
|
||||||
" },\n"
|
{
|
||||||
" {\n"
|
"headers": [
|
||||||
" \"headers\": [\n"
|
{ ":method": "POST" },
|
||||||
" { \":method\": \"POST\" },\n"
|
{ ":path": "/" }
|
||||||
" { \":path\": \"/\" }\n"
|
]
|
||||||
" ]\n"
|
}
|
||||||
" }\n"
|
]
|
||||||
" ]\n"
|
}
|
||||||
"}\n"
|
|
||||||
"\n"
|
With -t option, the program can accept more familiar HTTP/1 style
|
||||||
"With -t option, the program can accept more familiar HTTP/1 style\n"
|
header field block. Each header set must be followed by one empty
|
||||||
"header field block. Each header set must be followed by one empty\n"
|
line:
|
||||||
"line:\n"
|
|
||||||
"\n"
|
Example:
|
||||||
"Example:\n"
|
|
||||||
":method: GET\n"
|
:method: GET
|
||||||
":scheme: https\n"
|
:scheme: https
|
||||||
":path: /\n"
|
:path: /
|
||||||
"\n"
|
|
||||||
":method: POST\n"
|
:method: POST
|
||||||
"user-agent: nghttp2\n"
|
user-agent: nghttp2
|
||||||
"\n"
|
|
||||||
"The output of this program can be used as input for inflatehd.\n"
|
The output of this program can be used as input for inflatehd.
|
||||||
"\n"
|
|
||||||
"OPTIONS:\n"
|
OPTIONS:
|
||||||
" -t, --http1text Use HTTP/1 style header field text as input.\n"
|
-t, --http1text Use HTTP/1 style header field text as input.
|
||||||
" Each header set is delimited by single empty\n"
|
Each header set is delimited by single empty
|
||||||
" line.\n"
|
line.
|
||||||
" -s, --table-size=<N>\n"
|
-s, --table-size=<N>
|
||||||
" Set dynamic table size. In the HPACK\n"
|
Set dynamic table size. In the HPACK
|
||||||
" specification, this value is denoted by\n"
|
specification, this value is denoted by
|
||||||
" SETTINGS_HEADER_TABLE_SIZE.\n"
|
SETTINGS_HEADER_TABLE_SIZE.
|
||||||
" Default: 4096\n"
|
Default: 4096
|
||||||
" -S, --deflate-table-size=<N>\n"
|
-S, --deflate-table-size=<N>
|
||||||
" Use first N bytes of dynamic header table\n"
|
Use first N bytes of dynamic header table
|
||||||
" buffer.\n"
|
buffer.
|
||||||
" Default: 4096\n"
|
Default: 4096
|
||||||
" -d, --dump-header-table\n"
|
-d, --dump-header-table
|
||||||
" Output dynamic header table.\n"
|
Output dynamic header table.
|
||||||
" -c, --no-refset Don't use reference set.\n");
|
-c, --no-refset Don't use reference set.)"
|
||||||
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"http1text", no_argument, NULL, 't'},
|
{"http1text", no_argument, nullptr, 't'},
|
||||||
{"table-size", required_argument, NULL, 's'},
|
{"table-size", required_argument, nullptr, 's'},
|
||||||
{"deflate-table-size", required_argument, NULL, 'S'},
|
{"deflate-table-size", required_argument, nullptr, 'S'},
|
||||||
{"dump-header-table", no_argument, NULL, 'd'},
|
{"dump-header-table", no_argument, nullptr, 'd'},
|
||||||
{"no-refset", no_argument, NULL, 'c'},
|
{"no-refset", no_argument, nullptr, 'c'},
|
||||||
{NULL, 0, NULL, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
|
@ -26,21 +26,28 @@
|
||||||
# include <config.h>
|
# include <config.h>
|
||||||
#endif /* HAVE_CONFIG_H */
|
#endif /* HAVE_CONFIG_H */
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <cerrno>
|
||||||
#include <stdlib.h>
|
#include <cstdlib>
|
||||||
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
#include "nghttp2_hd.h"
|
#include "nghttp2_hd.h"
|
||||||
#include "nghttp2_frame.h"
|
#include "nghttp2_frame.h"
|
||||||
|
|
||||||
#include "comp_helper.h"
|
#include "comp_helper.h"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int dump_header_table;
|
int dump_header_table;
|
||||||
} inflate_config;
|
} inflate_config;
|
||||||
|
@ -70,9 +77,7 @@ static void to_json(nghttp2_hd_inflater *inflater,
|
||||||
json_t *headers, json_t *wire, int seq,
|
json_t *headers, json_t *wire, int seq,
|
||||||
size_t old_settings_table_size)
|
size_t old_settings_table_size)
|
||||||
{
|
{
|
||||||
json_t *obj;
|
auto obj = json_object();
|
||||||
|
|
||||||
obj = json_object();
|
|
||||||
json_object_set_new(obj, "seq", json_integer(seq));
|
json_object_set_new(obj, "seq", json_integer(seq));
|
||||||
json_object_set(obj, "wire", wire);
|
json_object_set(obj, "wire", wire);
|
||||||
json_object_set(obj, "headers", headers);
|
json_object_set(obj, "headers", headers);
|
||||||
|
@ -91,21 +96,20 @@ static void to_json(nghttp2_hd_inflater *inflater,
|
||||||
|
|
||||||
static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq)
|
static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq)
|
||||||
{
|
{
|
||||||
json_t *wire, *table_size, *headers;
|
|
||||||
size_t inputlen;
|
|
||||||
uint8_t *buf, *p;
|
|
||||||
size_t buflen;
|
|
||||||
ssize_t rv;
|
ssize_t rv;
|
||||||
nghttp2_nv nv;
|
nghttp2_nv nv;
|
||||||
int inflate_flags;
|
int inflate_flags;
|
||||||
size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max;
|
size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max;
|
||||||
|
|
||||||
wire = json_object_get(obj, "wire");
|
auto wire = json_object_get(obj, "wire");
|
||||||
if(wire == NULL) {
|
|
||||||
|
if(wire == nullptr) {
|
||||||
fprintf(stderr, "'wire' key is missing at %d\n", seq);
|
fprintf(stderr, "'wire' key is missing at %d\n", seq);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
table_size = json_object_get(obj, "header_table_size");
|
|
||||||
|
auto table_size = json_object_get(obj, "header_table_size");
|
||||||
|
|
||||||
if(table_size) {
|
if(table_size) {
|
||||||
if(!json_is_integer(table_size)) {
|
if(!json_is_integer(table_size)) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -122,18 +126,22 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
inputlen = strlen(json_string_value(wire));
|
|
||||||
|
auto inputlen = strlen(json_string_value(wire));
|
||||||
|
|
||||||
if(inputlen & 1) {
|
if(inputlen & 1) {
|
||||||
fprintf(stderr, "Badly formatted output value at %d\n", seq);
|
fprintf(stderr, "Badly formatted output value at %d\n", seq);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
buflen = inputlen / 2;
|
|
||||||
buf = malloc(buflen);
|
|
||||||
decode_hex(buf, json_string_value(wire), inputlen);
|
|
||||||
|
|
||||||
headers = json_array();
|
auto buflen = inputlen / 2;
|
||||||
|
auto buf = std::vector<uint8_t>(buflen);
|
||||||
|
|
||||||
p = buf;
|
decode_hex(buf.data(), json_string_value(wire), inputlen);
|
||||||
|
|
||||||
|
auto headers = json_array();
|
||||||
|
|
||||||
|
auto p = buf.data();
|
||||||
for(;;) {
|
for(;;) {
|
||||||
inflate_flags = 0;
|
inflate_flags = 0;
|
||||||
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
|
rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
|
||||||
|
@ -155,37 +163,40 @@ static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq)
|
||||||
nghttp2_hd_inflate_end_headers(inflater);
|
nghttp2_hd_inflate_end_headers(inflater);
|
||||||
to_json(inflater, headers, wire, seq, old_settings_table_size);
|
to_json(inflater, headers, wire, seq, old_settings_table_size);
|
||||||
json_decref(headers);
|
json_decref(headers);
|
||||||
free(buf);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int perform(void)
|
static int perform(void)
|
||||||
{
|
{
|
||||||
nghttp2_hd_inflater inflater;
|
nghttp2_hd_inflater inflater;
|
||||||
size_t i;
|
|
||||||
json_t *json, *cases;
|
|
||||||
json_error_t error;
|
json_error_t error;
|
||||||
size_t len;
|
|
||||||
|
|
||||||
json = json_loadf(stdin, 0, &error);
|
auto json = json_loadf(stdin, 0, &error);
|
||||||
if(json == NULL) {
|
|
||||||
|
if(json == nullptr) {
|
||||||
fprintf(stderr, "JSON loading failed\n");
|
fprintf(stderr, "JSON loading failed\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
cases = json_object_get(json, "cases");
|
|
||||||
if(cases == NULL) {
|
auto cases = json_object_get(json, "cases");
|
||||||
|
|
||||||
|
if(cases == nullptr) {
|
||||||
fprintf(stderr, "Missing 'cases' key in root object\n");
|
fprintf(stderr, "Missing 'cases' key in root object\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!json_is_array(cases)) {
|
if(!json_is_array(cases)) {
|
||||||
fprintf(stderr, "'cases' must be JSON array\n");
|
fprintf(stderr, "'cases' must be JSON array\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
nghttp2_hd_inflate_init(&inflater);
|
nghttp2_hd_inflate_init(&inflater);
|
||||||
output_json_header();
|
output_json_header();
|
||||||
len = json_array_size(cases);
|
auto len = json_array_size(cases);
|
||||||
for(i = 0; i < len; ++i) {
|
|
||||||
json_t *obj = json_array_get(cases, i);
|
for(size_t i = 0; i < len; ++i) {
|
||||||
|
auto obj = json_array_get(cases, i);
|
||||||
if(!json_is_object(obj)) {
|
if(!json_is_object(obj)) {
|
||||||
fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n",
|
fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n",
|
||||||
i);
|
i);
|
||||||
|
@ -201,47 +212,49 @@ static int perform(void)
|
||||||
output_json_footer();
|
output_json_footer();
|
||||||
nghttp2_hd_inflate_free(&inflater);
|
nghttp2_hd_inflate_free(&inflater);
|
||||||
json_decref(json);
|
json_decref(json);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_help(void)
|
static void print_help(void)
|
||||||
{
|
{
|
||||||
printf("HPACK HTTP/2 header decoder\n"
|
std::cout << R"(HPACK HTTP/2 header decoder
|
||||||
"Usage: inflatehd [OPTIONS] < INPUT\n"
|
Usage: inflatehd [OPTIONS] < INPUT
|
||||||
"\n"
|
|
||||||
"Reads JSON data from stdin and outputs inflated name/value pairs\n"
|
Reads JSON data from stdin and outputs inflated name/value pairs in
|
||||||
"in JSON.\n"
|
JSON.
|
||||||
"\n"
|
|
||||||
"The root JSON object must contain \"context\" key, which indicates\n"
|
The root JSON object must contain "context" key, which indicates which
|
||||||
"which compression context is used. If it is \"request\", request\n"
|
compression context is used. If it is "request", request compression
|
||||||
"compression context is used. Otherwise, response compression\n"
|
context is used. Otherwise, response compression context is used.
|
||||||
"context is used. The value of \"cases\" key contains the sequence\n"
|
The value of "cases" key contains the sequence of compressed header
|
||||||
"of compressed header block. They share the same compression\n"
|
block. They share the same compression context and are processed in
|
||||||
"context and are processed in the order they appear. Each item in\n"
|
the order they appear. Each item in the sequence is a JSON object and
|
||||||
"the sequence is a JSON object and it must have at least \"wire\"\n"
|
it must have at least "wire" key. Its value is a string containing
|
||||||
"key. Its value is a string containing compressed header block in\n"
|
compressed header block in hex string.
|
||||||
"hex string.\n"
|
|
||||||
"\n"
|
Example:
|
||||||
"Example:\n"
|
|
||||||
"{\n"
|
{
|
||||||
" \"context\": \"request\",\n"
|
"context": "request",
|
||||||
" \"cases\":\n"
|
"cases":
|
||||||
" [\n"
|
[
|
||||||
" { \"wire\": \"0284f77778ff\" },\n"
|
{ "wire": "0284f77778ff" },
|
||||||
" { \"wire\": \"0185fafd3c3c7f81\" }\n"
|
{ "wire": "0185fafd3c3c7f81" }
|
||||||
" ]\n"
|
]
|
||||||
"}\n"
|
}
|
||||||
"\n"
|
|
||||||
"The output of this program can be used as input for deflatehd.\n"
|
The output of this program can be used as input for deflatehd.
|
||||||
"\n"
|
|
||||||
"OPTIONS:\n"
|
OPTIONS:
|
||||||
" -d, --dump-header-table\n"
|
-d, --dump-header-table
|
||||||
" Output dynamic header table.\n");
|
Output dynamic header table.)"
|
||||||
|
<< std::endl;;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"dump-header-table", no_argument, NULL, 'd'},
|
{"dump-header-table", no_argument, nullptr, 'd'},
|
||||||
{NULL, 0, NULL, 0 }
|
{nullptr, 0, nullptr, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
Loading…
Reference in New Issue