2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2013 Tatsuhiro Tsujikawa
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #endif // HAVE_CONFIG_H
31 #endif // HAVE_UNISTD_H
46 #include "nghttp2_hd.h"
47 #include "nghttp2_frame.h"
49 #include "comp_helper.h"
52 typedef struct { int dump_header_table; } inflate_config;
54 static inflate_config config;
56 static uint8_t to_ud(char c) {
57 if (c >= 'A' && c <= 'Z') {
59 } else if (c >= 'a' && c <= 'z') {
66 static void decode_hex(uint8_t *dest, const char *src, size_t len) {
68 for (i = 0; i < len; i += 2) {
69 *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]);
73 static void to_json(nghttp2_hd_inflater *inflater, json_t *headers,
74 json_t *wire, int seq, size_t old_settings_table_size) {
75 auto obj = json_object();
76 json_object_set_new(obj, "seq", json_integer(seq));
77 json_object_set(obj, "wire", wire);
78 json_object_set(obj, "headers", headers);
79 if (old_settings_table_size != inflater->settings_hd_table_bufsize_max) {
80 json_object_set_new(obj, "header_table_size",
81 json_integer(inflater->settings_hd_table_bufsize_max));
83 if (config.dump_header_table) {
84 json_object_set_new(obj, "header_table", dump_header_table(&inflater->ctx));
86 json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
91 static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
95 size_t old_settings_table_size = inflater->settings_hd_table_bufsize_max;
97 auto wire = json_object_get(obj, "wire");
99 if (wire == nullptr) {
100 fprintf(stderr, "'wire' key is missing at %d\n", seq);
104 if (!json_is_string(wire)) {
105 fprintf(stderr, "'wire' value is not string at %d\n", seq);
109 auto table_size = json_object_get(obj, "header_table_size");
112 if (!json_is_integer(table_size)) {
114 "The value of 'header_table_size key' is not integer at %d\n",
118 rv = nghttp2_hd_inflate_change_table_size(inflater,
119 json_integer_value(table_size));
122 "nghttp2_hd_change_table_size() failed with error %s at %d\n",
123 nghttp2_strerror(rv), seq);
128 auto inputlen = strlen(json_string_value(wire));
131 fprintf(stderr, "Badly formatted output value at %d\n", seq);
135 auto buflen = inputlen / 2;
136 auto buf = std::vector<uint8_t>(buflen);
138 decode_hex(buf.data(), json_string_value(wire), inputlen);
140 auto headers = json_array();
145 rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
147 fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
152 if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
153 json_array_append_new(
154 headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen));
156 if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
161 nghttp2_hd_inflate_end_headers(inflater);
162 to_json(inflater, headers, wire, seq, old_settings_table_size);
163 json_decref(headers);
168 static int perform(void) {
169 nghttp2_hd_inflater *inflater = NULL;
172 auto json = json_loadf(stdin, 0, &error);
174 if (json == nullptr) {
175 fprintf(stderr, "JSON loading failed\n");
179 auto cases = json_object_get(json, "cases");
181 if (cases == nullptr) {
182 fprintf(stderr, "Missing 'cases' key in root object\n");
186 if (!json_is_array(cases)) {
187 fprintf(stderr, "'cases' must be JSON array\n");
191 nghttp2_hd_inflate_new(&inflater);
192 output_json_header();
193 auto len = json_array_size(cases);
195 for (size_t i = 0; i < len; ++i) {
196 auto obj = json_array_get(cases, i);
197 if (!json_is_object(obj)) {
198 fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i);
201 if (inflate_hd(obj, inflater, i) != 0) {
208 output_json_footer();
209 nghttp2_hd_inflate_del(inflater);
215 static void print_help(void) {
216 std::cout << R"(HPACK HTTP/2 header decoder
217 Usage: inflatehd [OPTIONS] < INPUT
219 Reads JSON data from stdin and outputs inflated name/value pairs in
222 The root JSON object must contain "context" key, which indicates which
223 compression context is used. If it is "request", request compression
224 context is used. Otherwise, response compression context is used.
225 The value of "cases" key contains the sequence of compressed header
226 block. They share the same compression context and are processed in
227 the order they appear. Each item in the sequence is a JSON object and
228 it must have at least "wire" key. Its value is a string containing
229 compressed header block in hex string.
234 "context": "request",
237 { "wire": "0284f77778ff" },
238 { "wire": "0185fafd3c3c7f81" }
242 The output of this program can be used as input for deflatehd.
245 -d, --dump-header-table
246 Output dynamic header table.)" << std::endl;
250 static struct option long_options[] = {
251 {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
253 int main(int argc, char **argv) {
254 config.dump_header_table = 0;
256 int option_index = 0;
257 int c = getopt_long(argc, argv, "dh", long_options, &option_index);
266 // --dump-header-table
267 config.dump_header_table = 1;