Commit da9bbb58 authored by Tatsuhiro Tsujikawa's avatar Tatsuhiro Tsujikawa

HPACK tools: Use JSON data format used in hpack-test-case

The input and output data format now use same JSON format used in
hpack-test-case.
parent 331a2f89
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
#include "comp_helper.h" #include "comp_helper.h"
#include <string.h>
static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len) static void dump_val(json_t *jent, const char *key, uint8_t *val, size_t len)
{ {
...@@ -55,13 +56,46 @@ json_t* dump_header_table(nghttp2_hd_context *context) ...@@ -55,13 +56,46 @@ json_t* dump_header_table(nghttp2_hd_context *context)
} }
json_object_set_new(obj, "entries", entries); json_object_set_new(obj, "entries", entries);
json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize)); json_object_set_new(obj, "size", json_integer(context->hd_table_bufsize));
json_object_set_new(obj, "maxSize", json_object_set_new(obj, "max_size",
json_integer(context->hd_table_bufsize_max)); json_integer(context->hd_table_bufsize_max));
if(context->role == NGHTTP2_HD_ROLE_DEFLATE) { if(context->role == NGHTTP2_HD_ROLE_DEFLATE) {
json_object_set_new(obj, "deflateSize", json_object_set_new(obj, "deflate_size",
json_integer(context->deflate_hd_table_bufsize)); json_integer(context->deflate_hd_table_bufsize));
json_object_set_new(obj, "maxDeflateSize", json_object_set_new(obj, "max_deflate_size",
json_integer(context->deflate_hd_table_bufsize_max)); json_integer(context->deflate_hd_table_bufsize_max));
} }
return obj; return obj;
} }
json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen)
{
json_t *headers;
size_t i;
headers = json_array();
for(i = 0; i < nvlen; ++i) {
json_t *nv_pair = json_object();
char *name = strndup((const char*)nva[i].name, nva[i].namelen);
name[nva[i].namelen] = '\0';
json_object_set_new(nv_pair, name,
json_pack("s#", nva[i].value, nva[i].valuelen));
free(name);
json_array_append_new(headers, nv_pair);
}
return headers;
}
void output_json_header(int side)
{
printf("{\n"
" \"context\": \"%s\",\n"
" \"cases\":\n"
" [\n",
(side == NGHTTP2_HD_SIDE_REQUEST ? "request" : "response"));
}
void output_json_footer(void)
{
printf(" ]\n"
"}\n");
}
...@@ -35,4 +35,10 @@ ...@@ -35,4 +35,10 @@
json_t* dump_header_table(nghttp2_hd_context *context); json_t* dump_header_table(nghttp2_hd_context *context);
json_t* dump_headers(const nghttp2_nv *nva, size_t nvlen);
void output_json_header(int side);
void output_json_footer();
#endif /* NGHTTP2_COMP_HELPER_H */ #endif /* NGHTTP2_COMP_HELPER_H */
This diff is collapsed.
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
typedef struct { typedef struct {
size_t table_size; size_t table_size;
nghttp2_hd_side side;
int dump_header_table; int dump_header_table;
} inflate_config; } inflate_config;
...@@ -69,24 +68,19 @@ static void decode_hex(uint8_t *dest, const char *src, size_t len) ...@@ -69,24 +68,19 @@ static void decode_hex(uint8_t *dest, const char *src, size_t len)
} }
static void nva_to_json(nghttp2_hd_context *inflater, static void nva_to_json(nghttp2_hd_context *inflater,
const nghttp2_nv *nva, size_t nvlen, int seq) const nghttp2_nv *nva, size_t nvlen,
json_t *wire, int seq)
{ {
size_t i;
json_t *obj; json_t *obj;
json_t *headers;
obj = json_object(); obj = json_object();
json_object_set_new(obj, "seq", json_integer(seq)); json_object_set_new(obj, "seq", json_integer(seq));
headers = json_array(); json_object_set(obj, "wire", wire);
json_object_set_new(obj, "headers", headers); json_object_set_new(obj, "headers", dump_headers(nva, nvlen));
for(i = 0; i < nvlen; ++i) { json_object_set_new(obj, "header_table_size",
json_t *nv_pair = json_array(); json_integer(inflater->hd_table_bufsize_max));
const nghttp2_nv *nv = &nva[i];
json_array_append_new(nv_pair, json_pack("s#", nv->name, nv->namelen));
json_array_append_new(nv_pair, json_pack("s#", nv->value, nv->valuelen));
json_array_append_new(headers, nv_pair);
}
if(config.dump_header_table) { if(config.dump_header_table) {
json_object_set_new(obj, "headerTable", dump_header_table(inflater)); json_object_set_new(obj, "header_table", dump_header_table(inflater));
} }
json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER); json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
json_decref(obj); json_decref(obj);
...@@ -95,27 +89,42 @@ static void nva_to_json(nghttp2_hd_context *inflater, ...@@ -95,27 +89,42 @@ static void nva_to_json(nghttp2_hd_context *inflater,
static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq) static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq)
{ {
json_t *js; json_t *wire, *table_size;
size_t inputlen; size_t inputlen;
uint8_t buf[16*1024]; uint8_t *buf;
ssize_t resnvlen; ssize_t resnvlen;
nghttp2_nv *resnva; nghttp2_nv *resnva;
int rv;
js = json_object_get(obj, "output"); wire = json_object_get(obj, "wire");
if(js == NULL) { if(wire == NULL) {
fprintf(stderr, "output key is missing at %d\n", seq); fprintf(stderr, "'wire' key is missing at %d\n", seq);
return -1; return -1;
} }
inputlen = strlen(json_string_value(js)); table_size = json_object_get(obj, "header_table_size");
if(table_size) {
if(!json_is_integer(table_size)) {
fprintf(stderr,
"The value of 'header_table_size key' is not integer at %d\n",
seq);
return -1;
}
rv = nghttp2_hd_change_table_size(inflater,
json_integer_value(table_size));
if(rv != 0) {
fprintf(stderr,
"nghttp2_hd_change_table_size() failed with error %s at %d\n",
nghttp2_strerror(rv), seq);
return -1;
}
}
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);
} }
if(inputlen / 2 > sizeof(buf)) { buf = malloc(inputlen / 2);
fprintf(stderr, "Too big input length %zu at %d\n", inputlen / 2, seq); decode_hex(buf, json_string_value(wire), inputlen);
exit(EXIT_FAILURE);
}
decode_hex(buf, json_string_value(js), inputlen);
resnvlen = nghttp2_hd_inflate_hd(inflater, &resnva, buf, inputlen / 2); resnvlen = nghttp2_hd_inflate_hd(inflater, &resnva, buf, inputlen / 2);
if(resnvlen < 0) { if(resnvlen < 0) {
...@@ -123,9 +132,10 @@ static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq) ...@@ -123,9 +132,10 @@ static int inflate_hd(json_t *obj, nghttp2_hd_context *inflater, int seq)
resnvlen, seq); resnvlen, seq);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
nva_to_json(inflater, resnva, resnvlen, seq); nva_to_json(inflater, resnva, resnvlen, wire, seq);
nghttp2_hd_end_headers(inflater); nghttp2_hd_end_headers(inflater);
nghttp2_nv_array_del(resnva); nghttp2_nv_array_del(resnva);
free(buf);
return 0; return 0;
} }
...@@ -133,21 +143,38 @@ static int perform(void) ...@@ -133,21 +143,38 @@ static int perform(void)
{ {
nghttp2_hd_context inflater; nghttp2_hd_context inflater;
size_t i; size_t i;
json_t *json; json_t *json, *cases;
json_error_t error; json_error_t error;
size_t len; size_t len;
nghttp2_hd_side side;
json = json_loadf(stdin, 0, &error); json = json_loadf(stdin, 0, &error);
if(json == NULL) { if(json == NULL) {
fprintf(stderr, "JSON loading failed\n"); fprintf(stderr, "JSON loading failed\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
nghttp2_hd_inflate_init(&inflater, config.side); if(strcmp("request", json_string_value(json_object_get(json, "context")))
== 0) {
side = NGHTTP2_HD_SIDE_REQUEST;
} else {
side = NGHTTP2_HD_SIDE_RESPONSE;
}
cases = json_object_get(json, "cases");
if(cases == NULL) {
fprintf(stderr, "Missing 'cases' key in root object\n");
exit(EXIT_FAILURE);
}
if(!json_is_array(cases)) {
fprintf(stderr, "'cases' must be JSON array\n");
exit(EXIT_FAILURE);
}
nghttp2_hd_inflate_init(&inflater, side);
nghttp2_hd_change_table_size(&inflater, config.table_size); nghttp2_hd_change_table_size(&inflater, config.table_size);
printf("[\n");
len = json_array_size(json); output_json_header(side);
len = json_array_size(cases);
for(i = 0; i < len; ++i) { for(i = 0; i < len; ++i) {
json_t *obj = json_array_get(json, i); json_t *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);
...@@ -160,7 +187,7 @@ static int perform(void) ...@@ -160,7 +187,7 @@ static int perform(void)
printf(",\n"); printf(",\n");
} }
} }
printf("]\n"); output_json_footer();
nghttp2_hd_inflate_free(&inflater); nghttp2_hd_inflate_free(&inflater);
json_decref(json); json_decref(json);
return 0; return 0;
...@@ -171,24 +198,32 @@ static void print_help(void) ...@@ -171,24 +198,32 @@ static void print_help(void)
printf("HPACK HTTP/2.0 header decoder\n" printf("HPACK HTTP/2.0 header decoder\n"
"Usage: inflatehd [OPTIONS] < INPUT\n" "Usage: inflatehd [OPTIONS] < INPUT\n"
"\n" "\n"
"Reads JSON array from stdin and outputs inflated name/value pairs\n" "Reads JSON data from stdin and outputs inflated name/value pairs\n"
"in JSON array.\n" "in JSON.\n"
"The element of input array must be a JSON object. Each object must\n"
"have at least following key:\n"
"\n" "\n"
" output: deflated header block in hex-string.\n" "The root JSON object must contain \"context\" key, which indicates\n"
"which compression context is used. If it is \"request\", request\n"
"compression context is used. Otherwise, response compression\n"
"context is used. The value of \"cases\" key contains the sequence\n"
"of compressed header block. They share the same compression\n"
"context and are processed in the order they appear. Each item in\n"
"the sequence is a JSON object and it must have at least \"wire\"\n"
"key. Its value is a string containing compressed header block in\n"
"hex string.\n"
"\n" "\n"
"Example:\n" "Example:\n"
"[\n" "{\n"
" { \"output\": \"0284f77778ff\" },\n" " \"context\": \"request\",\n"
" { \"output\": \"0185fafd3c3c7f81\" }\n" " \"cases\":\n"
"]\n" " [\n"
" { \"wire\": \"0284f77778ff\" },\n"
" { \"wire\": \"0185fafd3c3c7f81\" }\n"
" ]\n"
"}\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"
"\n" "\n"
"OPTIONS:\n" "OPTIONS:\n"
" -r, --response Use response compression context instead of\n"
" request.\n"
" -s, --table-size=<N>\n" " -s, --table-size=<N>\n"
" Set dynamic table size. In the HPACK\n" " Set dynamic table size. In the HPACK\n"
" specification, this value is denoted by\n" " specification, this value is denoted by\n"
...@@ -199,7 +234,6 @@ static void print_help(void) ...@@ -199,7 +234,6 @@ static void print_help(void)
} }
static struct option long_options[] = { static struct option long_options[] = {
{"response", no_argument, NULL, 'r'},
{"table-size", required_argument, NULL, 's'}, {"table-size", required_argument, NULL, 's'},
{"dump-header-table", no_argument, NULL, 'd'}, {"dump-header-table", no_argument, NULL, 'd'},
{NULL, 0, NULL, 0 } {NULL, 0, NULL, 0 }
...@@ -208,20 +242,15 @@ static struct option long_options[] = { ...@@ -208,20 +242,15 @@ static struct option long_options[] = {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char *end; char *end;
config.side = NGHTTP2_HD_SIDE_REQUEST;
config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE; config.table_size = NGHTTP2_HD_DEFAULT_MAX_BUFFER_SIZE;
config.dump_header_table = 0; config.dump_header_table = 0;
while(1) { while(1) {
int option_index = 0; int option_index = 0;
int c = getopt_long(argc, argv, "dhrs:", long_options, &option_index); int c = getopt_long(argc, argv, "dhs:", long_options, &option_index);
if(c == -1) { if(c == -1) {
break; break;
} }
switch(c) { switch(c) {
case 'r':
/* --response */
config.side = NGHTTP2_HD_SIDE_RESPONSE;
break;
case 'h': case 'h':
print_help(); print_help();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment