• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 Tatsuhiro Tsujikawa
5  *
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:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
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.
24  */
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif // HAVE_CONFIG_H
28 
29 #ifdef HAVE_UNISTD_H
30 #  include <unistd.h>
31 #endif // HAVE_UNISTD_H
32 #include <getopt.h>
33 
34 #include <cstdio>
35 #include <cstring>
36 #include <assert.h>
37 #include <cerrno>
38 #include <cstdlib>
39 #include <vector>
40 #include <iostream>
41 
42 #include <jansson.h>
43 
44 #include <nghttp2/nghttp2.h>
45 
46 #include "template.h"
47 #include "comp_helper.h"
48 
49 namespace nghttp2 {
50 
51 typedef struct {
52   int dump_header_table;
53 } inflate_config;
54 
55 static inflate_config config;
56 
to_ud(char c)57 static uint8_t to_ud(char c) {
58   if (c >= 'A' && c <= 'Z') {
59     return c - 'A' + 10;
60   } else if (c >= 'a' && c <= 'z') {
61     return c - 'a' + 10;
62   } else {
63     return c - '0';
64   }
65 }
66 
decode_hex(uint8_t * dest,const char * src,size_t len)67 static void decode_hex(uint8_t *dest, const char *src, size_t len) {
68   size_t i;
69   for (i = 0; i < len; i += 2) {
70     *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]);
71   }
72 }
73 
to_json(nghttp2_hd_inflater * inflater,json_t * headers,json_t * wire,int seq,size_t old_settings_table_size)74 static void to_json(nghttp2_hd_inflater *inflater, json_t *headers,
75                     json_t *wire, int seq, size_t old_settings_table_size) {
76   auto obj = json_object();
77   json_object_set_new(obj, "seq", json_integer(seq));
78   json_object_set(obj, "wire", wire);
79   json_object_set(obj, "headers", headers);
80   auto max_dyn_table_size =
81       nghttp2_hd_inflate_get_max_dynamic_table_size(inflater);
82   if (old_settings_table_size != max_dyn_table_size) {
83     json_object_set_new(obj, "header_table_size",
84                         json_integer(max_dyn_table_size));
85   }
86   if (config.dump_header_table) {
87     json_object_set_new(obj, "header_table",
88                         dump_inflate_header_table(inflater));
89   }
90   json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
91   json_decref(obj);
92   printf("\n");
93 }
94 
inflate_hd(json_t * obj,nghttp2_hd_inflater * inflater,int seq)95 static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
96   ssize_t rv;
97   nghttp2_nv nv;
98   int inflate_flags;
99   size_t old_settings_table_size =
100       nghttp2_hd_inflate_get_max_dynamic_table_size(inflater);
101 
102   auto wire = json_object_get(obj, "wire");
103 
104   if (wire == nullptr) {
105     fprintf(stderr, "'wire' key is missing at %d\n", seq);
106     return -1;
107   }
108 
109   if (!json_is_string(wire)) {
110     fprintf(stderr, "'wire' value is not string at %d\n", seq);
111     return -1;
112   }
113 
114   auto table_size = json_object_get(obj, "header_table_size");
115 
116   if (table_size) {
117     if (!json_is_integer(table_size)) {
118       fprintf(stderr,
119               "The value of 'header_table_size key' is not integer at %d\n",
120               seq);
121       return -1;
122     }
123     rv = nghttp2_hd_inflate_change_table_size(inflater,
124                                               json_integer_value(table_size));
125     if (rv != 0) {
126       fprintf(stderr,
127               "nghttp2_hd_change_table_size() failed with error %s at %d\n",
128               nghttp2_strerror(rv), seq);
129       return -1;
130     }
131   }
132 
133   auto inputlen = strlen(json_string_value(wire));
134 
135   if (inputlen & 1) {
136     fprintf(stderr, "Badly formatted output value at %d\n", seq);
137     exit(EXIT_FAILURE);
138   }
139 
140   auto buflen = inputlen / 2;
141   auto buf = std::vector<uint8_t>(buflen);
142 
143   decode_hex(buf.data(), json_string_value(wire), inputlen);
144 
145   auto headers = json_array();
146 
147   auto p = buf.data();
148   for (;;) {
149     inflate_flags = 0;
150     rv = nghttp2_hd_inflate_hd(inflater, &nv, &inflate_flags, p, buflen, 1);
151     if (rv < 0) {
152       fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
153       exit(EXIT_FAILURE);
154     }
155     p += rv;
156     buflen -= rv;
157     if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
158       json_array_append_new(
159           headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen));
160     }
161     if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
162       break;
163     }
164   }
165   assert(buflen == 0);
166   nghttp2_hd_inflate_end_headers(inflater);
167   to_json(inflater, headers, wire, seq, old_settings_table_size);
168   json_decref(headers);
169 
170   return 0;
171 }
172 
perform(void)173 static int perform(void) {
174   nghttp2_hd_inflater *inflater = NULL;
175   json_error_t error;
176 
177   auto json = json_loadf(stdin, 0, &error);
178 
179   if (json == nullptr) {
180     fprintf(stderr, "JSON loading failed\n");
181     exit(EXIT_FAILURE);
182   }
183 
184   auto cases = json_object_get(json, "cases");
185 
186   if (cases == nullptr) {
187     fprintf(stderr, "Missing 'cases' key in root object\n");
188     exit(EXIT_FAILURE);
189   }
190 
191   if (!json_is_array(cases)) {
192     fprintf(stderr, "'cases' must be JSON array\n");
193     exit(EXIT_FAILURE);
194   }
195 
196   nghttp2_hd_inflate_new(&inflater);
197   output_json_header();
198   auto len = json_array_size(cases);
199 
200   for (size_t i = 0; i < len; ++i) {
201     auto obj = json_array_get(cases, i);
202     if (!json_is_object(obj)) {
203       fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i);
204       continue;
205     }
206     if (inflate_hd(obj, inflater, i) != 0) {
207       continue;
208     }
209     if (i + 1 < len) {
210       printf(",\n");
211     }
212   }
213   output_json_footer();
214   nghttp2_hd_inflate_del(inflater);
215   json_decref(json);
216 
217   return 0;
218 }
219 
print_help(void)220 static void print_help(void) {
221   std::cout << R"(HPACK HTTP/2 header decoder
222 Usage: inflatehd [OPTIONS] < INPUT
223 
224 Reads JSON  data from stdin  and outputs inflated name/value  pairs in
225 JSON.
226 
227 The root JSON object must contain "context" key, which indicates which
228 compression context is used.  If  it is "request", request compression
229 context  is used.   Otherwise, response  compression context  is used.
230 The value  of "cases" key  contains the sequence of  compressed header
231 block.  They share  the same compression context and  are processed in
232 the order they appear.  Each item in the sequence is a JSON object and
233 it must  have at least "wire"  key.  Its value is  a string containing
234 compressed header block in hex string.
235 
236 Example:
237 
238 {
239   "context": "request",
240   "cases":
241   [
242     { "wire": "0284f77778ff" },
243     { "wire": "0185fafd3c3c7f81" }
244   ]
245 }
246 
247 The output of this program can be used as input for deflatehd.
248 
249 OPTIONS:
250     -d, --dump-header-table
251                       Output dynamic header table.)"
252             << std::endl;
253   ;
254 }
255 
256 constexpr static struct option long_options[] = {
257     {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
258 
main(int argc,char ** argv)259 int main(int argc, char **argv) {
260   config.dump_header_table = 0;
261   while (1) {
262     int option_index = 0;
263     int c = getopt_long(argc, argv, "dh", long_options, &option_index);
264     if (c == -1) {
265       break;
266     }
267     switch (c) {
268     case 'h':
269       print_help();
270       exit(EXIT_SUCCESS);
271     case 'd':
272       // --dump-header-table
273       config.dump_header_table = 1;
274       break;
275     case '?':
276       exit(EXIT_FAILURE);
277     default:
278       break;
279     }
280   }
281   perform();
282   return 0;
283 }
284 
285 } // namespace nghttp2
286 
main(int argc,char ** argv)287 int main(int argc, char **argv) {
288   return nghttp2::run_app(nghttp2::main, argc, argv);
289 }
290