• 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 #define NGHTTP2_NO_SSIZE_T
45 #include <nghttp2/nghttp2.h>
46 
47 #include "template.h"
48 #include "comp_helper.h"
49 
50 namespace nghttp2 {
51 
52 typedef struct {
53   int dump_header_table;
54 } inflate_config;
55 
56 static inflate_config config;
57 
to_ud(char c)58 static uint8_t to_ud(char c) {
59   if (c >= 'A' && c <= 'Z') {
60     return c - 'A' + 10;
61   } else if (c >= 'a' && c <= 'z') {
62     return c - 'a' + 10;
63   } else {
64     return c - '0';
65   }
66 }
67 
decode_hex(uint8_t * dest,const char * src,size_t len)68 static void decode_hex(uint8_t *dest, const char *src, size_t len) {
69   size_t i;
70   for (i = 0; i < len; i += 2) {
71     *dest++ = to_ud(src[i]) << 4 | to_ud(src[i + 1]);
72   }
73 }
74 
to_json(nghttp2_hd_inflater * inflater,json_t * headers,json_t * wire,int seq,size_t old_settings_table_size)75 static void to_json(nghttp2_hd_inflater *inflater, json_t *headers,
76                     json_t *wire, int seq, size_t old_settings_table_size) {
77   auto obj = json_object();
78   json_object_set_new(obj, "seq", json_integer(seq));
79   json_object_set(obj, "wire", wire);
80   json_object_set(obj, "headers", headers);
81   auto max_dyn_table_size =
82       nghttp2_hd_inflate_get_max_dynamic_table_size(inflater);
83   if (old_settings_table_size != max_dyn_table_size) {
84     json_object_set_new(obj, "header_table_size",
85                         json_integer(max_dyn_table_size));
86   }
87   if (config.dump_header_table) {
88     json_object_set_new(obj, "header_table",
89                         dump_inflate_header_table(inflater));
90   }
91   json_dumpf(obj, stdout, JSON_INDENT(2) | JSON_PRESERVE_ORDER);
92   json_decref(obj);
93   printf("\n");
94 }
95 
inflate_hd(json_t * obj,nghttp2_hd_inflater * inflater,int seq)96 static int inflate_hd(json_t *obj, nghttp2_hd_inflater *inflater, int seq) {
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     auto rv = nghttp2_hd_inflate_change_table_size(
124         inflater, 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     auto rv =
151         nghttp2_hd_inflate_hd3(inflater, &nv, &inflate_flags, p, buflen, 1);
152     if (rv < 0) {
153       fprintf(stderr, "inflate failed with error code %zd at %d\n", rv, seq);
154       exit(EXIT_FAILURE);
155     }
156     p += rv;
157     buflen -= rv;
158     if (inflate_flags & NGHTTP2_HD_INFLATE_EMIT) {
159       json_array_append_new(
160           headers, dump_header(nv.name, nv.namelen, nv.value, nv.valuelen));
161     }
162     if (inflate_flags & NGHTTP2_HD_INFLATE_FINAL) {
163       break;
164     }
165   }
166   assert(buflen == 0);
167   nghttp2_hd_inflate_end_headers(inflater);
168   to_json(inflater, headers, wire, seq, old_settings_table_size);
169   json_decref(headers);
170 
171   return 0;
172 }
173 
perform(void)174 static int perform(void) {
175   nghttp2_hd_inflater *inflater = nullptr;
176   json_error_t error;
177 
178   auto json = json_loadf(stdin, 0, &error);
179 
180   if (json == nullptr) {
181     fprintf(stderr, "JSON loading failed\n");
182     exit(EXIT_FAILURE);
183   }
184 
185   auto cases = json_object_get(json, "cases");
186 
187   if (cases == nullptr) {
188     fprintf(stderr, "Missing 'cases' key in root object\n");
189     exit(EXIT_FAILURE);
190   }
191 
192   if (!json_is_array(cases)) {
193     fprintf(stderr, "'cases' must be JSON array\n");
194     exit(EXIT_FAILURE);
195   }
196 
197   nghttp2_hd_inflate_new(&inflater);
198   output_json_header();
199   auto len = json_array_size(cases);
200 
201   for (size_t i = 0; i < len; ++i) {
202     auto obj = json_array_get(cases, i);
203     if (!json_is_object(obj)) {
204       fprintf(stderr, "Unexpected JSON type at %zu. It should be object.\n", i);
205       continue;
206     }
207     if (inflate_hd(obj, inflater, i) != 0) {
208       continue;
209     }
210     if (i + 1 < len) {
211       printf(",\n");
212     }
213   }
214   output_json_footer();
215   nghttp2_hd_inflate_del(inflater);
216   json_decref(json);
217 
218   return 0;
219 }
220 
print_help(void)221 static void print_help(void) {
222   std::cout << R"(HPACK HTTP/2 header decoder
223 Usage: inflatehd [OPTIONS] < INPUT
224 
225 Reads JSON  data from stdin  and outputs inflated name/value  pairs in
226 JSON.
227 
228 The root JSON object must contain "context" key, which indicates which
229 compression context is used.  If  it is "request", request compression
230 context  is used.   Otherwise, response  compression context  is used.
231 The value  of "cases" key  contains the sequence of  compressed header
232 block.  They share  the same compression context and  are processed in
233 the order they appear.  Each item in the sequence is a JSON object and
234 it must  have at least "wire"  key.  Its value is  a string containing
235 compressed header block in hex string.
236 
237 Example:
238 
239 {
240   "context": "request",
241   "cases":
242   [
243     { "wire": "0284f77778ff" },
244     { "wire": "0185fafd3c3c7f81" }
245   ]
246 }
247 
248 The output of this program can be used as input for deflatehd.
249 
250 OPTIONS:
251     -d, --dump-header-table
252                       Output dynamic header table.)"
253             << std::endl;
254   ;
255 }
256 
257 constexpr static struct option long_options[] = {
258     {"dump-header-table", no_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}};
259 
main(int argc,char ** argv)260 int main(int argc, char **argv) {
261   config.dump_header_table = 0;
262   while (1) {
263     int option_index = 0;
264     int c = getopt_long(argc, argv, "dh", long_options, &option_index);
265     if (c == -1) {
266       break;
267     }
268     switch (c) {
269     case 'h':
270       print_help();
271       exit(EXIT_SUCCESS);
272     case 'd':
273       // --dump-header-table
274       config.dump_header_table = 1;
275       break;
276     case '?':
277       exit(EXIT_FAILURE);
278     default:
279       break;
280     }
281   }
282   perform();
283   return 0;
284 }
285 
286 } // namespace nghttp2
287 
main(int argc,char ** argv)288 int main(int argc, char **argv) {
289   return nghttp2::run_app(nghttp2::main, argc, argv);
290 }
291