Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / app_helper.cc
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2012 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 #include <sys/types.h>
26 #ifdef HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #endif // HAVE_SYS_SOCKET_H
29 #ifdef HAVE_NETDB_H
30 #include <netdb.h>
31 #endif // HAVE_NETDB_H
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif // HAVE_UNISTD_H
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif // HAVE_FCNTL_H
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif // HAVE_NETINET_IN_H
41 #include <netinet/tcp.h>
42 #include <poll.h>
43
44 #include <cassert>
45 #include <cstdio>
46 #include <cerrno>
47 #include <cstdlib>
48 #include <cstring>
49 #include <string>
50 #include <iostream>
51 #include <string>
52 #include <set>
53 #include <iomanip>
54 #include <fstream>
55
56 #include <zlib.h>
57
58 #include "app_helper.h"
59 #include "util.h"
60 #include "http2.h"
61
62 namespace nghttp2 {
63
64 namespace {
65 const char *strstatus(uint32_t error_code) {
66   switch (error_code) {
67   case NGHTTP2_NO_ERROR:
68     return "NO_ERROR";
69   case NGHTTP2_PROTOCOL_ERROR:
70     return "PROTOCOL_ERROR";
71   case NGHTTP2_INTERNAL_ERROR:
72     return "INTERNAL_ERROR";
73   case NGHTTP2_FLOW_CONTROL_ERROR:
74     return "FLOW_CONTROL_ERROR";
75   case NGHTTP2_SETTINGS_TIMEOUT:
76     return "SETTINGS_TIMEOUT";
77   case NGHTTP2_STREAM_CLOSED:
78     return "STREAM_CLOSED";
79   case NGHTTP2_FRAME_SIZE_ERROR:
80     return "FRAME_SIZE_ERROR";
81   case NGHTTP2_REFUSED_STREAM:
82     return "REFUSED_STREAM";
83   case NGHTTP2_CANCEL:
84     return "CANCEL";
85   case NGHTTP2_COMPRESSION_ERROR:
86     return "COMPRESSION_ERROR";
87   case NGHTTP2_CONNECT_ERROR:
88     return "CONNECT_ERROR";
89   case NGHTTP2_ENHANCE_YOUR_CALM:
90     return "ENHANCE_YOUR_CALM";
91   case NGHTTP2_INADEQUATE_SECURITY:
92     return "INADEQUATE_SECURITY";
93   case NGHTTP2_HTTP_1_1_REQUIRED:
94     return "HTTP_1_1_REQUIRED";
95   default:
96     return "UNKNOWN";
97   }
98 }
99 } // namespace
100
101 namespace {
102 const char *strsettingsid(int32_t id) {
103   switch (id) {
104   case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
105     return "SETTINGS_HEADER_TABLE_SIZE";
106   case NGHTTP2_SETTINGS_ENABLE_PUSH:
107     return "SETTINGS_ENABLE_PUSH";
108   case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
109     return "SETTINGS_MAX_CONCURRENT_STREAMS";
110   case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
111     return "SETTINGS_INITIAL_WINDOW_SIZE";
112   case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
113     return "SETTINGS_MAX_FRAME_SIZE";
114   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
115     return "SETTINGS_MAX_HEADER_LIST_SIZE";
116   default:
117     return "UNKNOWN";
118   }
119 }
120 } // namespace
121
122 namespace {
123 const char *strframetype(uint8_t type) {
124   switch (type) {
125   case NGHTTP2_DATA:
126     return "DATA";
127   case NGHTTP2_HEADERS:
128     return "HEADERS";
129   case NGHTTP2_PRIORITY:
130     return "PRIORITY";
131   case NGHTTP2_RST_STREAM:
132     return "RST_STREAM";
133   case NGHTTP2_SETTINGS:
134     return "SETTINGS";
135   case NGHTTP2_PUSH_PROMISE:
136     return "PUSH_PROMISE";
137   case NGHTTP2_PING:
138     return "PING";
139   case NGHTTP2_GOAWAY:
140     return "GOAWAY";
141   case NGHTTP2_WINDOW_UPDATE:
142     return "WINDOW_UPDATE";
143   default:
144     return "UNKNOWN";
145   }
146 };
147 } // namespace
148
149 namespace {
150 bool color_output = false;
151 } // namespace
152
153 void set_color_output(bool f) { color_output = f; }
154
155 namespace {
156 FILE *outfile = stdout;
157 } // namespace
158
159 void set_output(FILE *file) { outfile = file; }
160
161 namespace {
162 void print_frame_attr_indent() { fprintf(outfile, "          "); }
163 } // namespace
164
165 namespace {
166 const char *ansi_esc(const char *code) { return color_output ? code : ""; }
167 } // namespace
168
169 namespace {
170 const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
171 } // namespace
172
173 namespace {
174 void print_nv(nghttp2_nv *nv) {
175   fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
176           ansi_escend(), nv->value);
177 }
178 } // namespace
179 namespace {
180 void print_nv(nghttp2_nv *nva, size_t nvlen) {
181   auto end = nva + nvlen;
182   for (; nva != end; ++nva) {
183     print_frame_attr_indent();
184
185     print_nv(nva);
186   }
187 }
188 } // namelen
189
190 void print_timer() {
191   auto millis = get_timer();
192   fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"),
193           (long int)(millis.count() / 1000), (long int)(millis.count() % 1000),
194           ansi_escend());
195 }
196
197 namespace {
198 void print_frame_hd(const nghttp2_frame_hd &hd) {
199   fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n", hd.length,
200           hd.flags, hd.stream_id);
201 }
202 } // namespace
203
204 namespace {
205 void print_flags(const nghttp2_frame_hd &hd) {
206   std::string s;
207   switch (hd.type) {
208   case NGHTTP2_DATA:
209     if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
210       s += "END_STREAM";
211     }
212     if (hd.flags & NGHTTP2_FLAG_PADDED) {
213       if (!s.empty()) {
214         s += " | ";
215       }
216       s += "PADDED";
217     }
218     break;
219   case NGHTTP2_HEADERS:
220     if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
221       s += "END_STREAM";
222     }
223     if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
224       if (!s.empty()) {
225         s += " | ";
226       }
227       s += "END_HEADERS";
228     }
229     if (hd.flags & NGHTTP2_FLAG_PADDED) {
230       if (!s.empty()) {
231         s += " | ";
232       }
233       s += "PADDED";
234     }
235     if (hd.flags & NGHTTP2_FLAG_PRIORITY) {
236       if (!s.empty()) {
237         s += " | ";
238       }
239       s += "PRIORITY";
240     }
241
242     break;
243   case NGHTTP2_PRIORITY:
244     break;
245   case NGHTTP2_SETTINGS:
246     if (hd.flags & NGHTTP2_FLAG_ACK) {
247       s += "ACK";
248     }
249     break;
250   case NGHTTP2_PUSH_PROMISE:
251     if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
252       s += "END_HEADERS";
253     }
254     if (hd.flags & NGHTTP2_FLAG_PADDED) {
255       if (!s.empty()) {
256         s += " | ";
257       }
258       s += "PADDED";
259     }
260     break;
261   case NGHTTP2_PING:
262     if (hd.flags & NGHTTP2_FLAG_ACK) {
263       s += "ACK";
264     }
265     break;
266   }
267   fprintf(outfile, "; %s\n", s.c_str());
268 }
269 } // namespace
270
271 enum print_type { PRINT_SEND, PRINT_RECV };
272
273 namespace {
274 const char *frame_name_ansi_esc(print_type ptype) {
275   return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m");
276 }
277 } // namespace
278
279 namespace {
280 void print_frame(print_type ptype, const nghttp2_frame *frame) {
281   fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
282           strframetype(frame->hd.type), ansi_escend());
283   print_frame_hd(frame->hd);
284   if (frame->hd.flags) {
285     print_frame_attr_indent();
286     print_flags(frame->hd);
287   }
288   switch (frame->hd.type) {
289   case NGHTTP2_DATA:
290     if (frame->data.padlen > 0) {
291       print_frame_attr_indent();
292       fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
293     }
294     break;
295   case NGHTTP2_HEADERS:
296     print_frame_attr_indent();
297     fprintf(outfile, "(padlen=%zu", frame->headers.padlen);
298     if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
299       fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d",
300               frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight,
301               frame->headers.pri_spec.exclusive);
302     }
303     fprintf(outfile, ")\n");
304     switch (frame->headers.cat) {
305     case NGHTTP2_HCAT_REQUEST:
306       print_frame_attr_indent();
307       fprintf(outfile, "; Open new stream\n");
308       break;
309     case NGHTTP2_HCAT_RESPONSE:
310       print_frame_attr_indent();
311       fprintf(outfile, "; First response header\n");
312       break;
313     case NGHTTP2_HCAT_PUSH_RESPONSE:
314       print_frame_attr_indent();
315       fprintf(outfile, "; First push response header\n");
316       break;
317     default:
318       break;
319     }
320     print_nv(frame->headers.nva, frame->headers.nvlen);
321     break;
322   case NGHTTP2_PRIORITY:
323     print_frame_attr_indent();
324
325     fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n",
326             frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight,
327             frame->priority.pri_spec.exclusive);
328
329     break;
330   case NGHTTP2_RST_STREAM:
331     print_frame_attr_indent();
332     fprintf(outfile, "(error_code=%s(0x%02x))\n",
333             strstatus(frame->rst_stream.error_code),
334             frame->rst_stream.error_code);
335     break;
336   case NGHTTP2_SETTINGS:
337     print_frame_attr_indent();
338     fprintf(outfile, "(niv=%lu)\n",
339             static_cast<unsigned long>(frame->settings.niv));
340     for (size_t i = 0; i < frame->settings.niv; ++i) {
341       print_frame_attr_indent();
342       fprintf(outfile, "[%s(0x%02x):%u]\n",
343               strsettingsid(frame->settings.iv[i].settings_id),
344               frame->settings.iv[i].settings_id, frame->settings.iv[i].value);
345     }
346     break;
347   case NGHTTP2_PUSH_PROMISE:
348     print_frame_attr_indent();
349     fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n",
350             frame->push_promise.padlen, frame->push_promise.promised_stream_id);
351     print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
352     break;
353   case NGHTTP2_PING:
354     print_frame_attr_indent();
355     fprintf(outfile, "(opaque_data=%s)\n",
356             util::format_hex(frame->ping.opaque_data, 8).c_str());
357     break;
358   case NGHTTP2_GOAWAY:
359     print_frame_attr_indent();
360     fprintf(outfile, "(last_stream_id=%d, error_code=%s(0x%02x), "
361                      "opaque_data(%u)=[%s])\n",
362             frame->goaway.last_stream_id, strstatus(frame->goaway.error_code),
363             frame->goaway.error_code,
364             static_cast<unsigned int>(frame->goaway.opaque_data_len),
365             util::ascii_dump(frame->goaway.opaque_data,
366                              frame->goaway.opaque_data_len).c_str());
367     break;
368   case NGHTTP2_WINDOW_UPDATE:
369     print_frame_attr_indent();
370     fprintf(outfile, "(window_size_increment=%d)\n",
371             frame->window_update.window_size_increment);
372     break;
373   default:
374     break;
375   }
376 }
377 } // namespace
378
379 int verbose_on_header_callback(nghttp2_session *session,
380                                const nghttp2_frame *frame, const uint8_t *name,
381                                size_t namelen, const uint8_t *value,
382                                size_t valuelen, uint8_t flags,
383                                void *user_data) {
384   nghttp2_nv nv = {const_cast<uint8_t *>(name), const_cast<uint8_t *>(value),
385                    namelen, valuelen};
386
387   print_timer();
388   fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
389   if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
390     fprintf(outfile, ", sensitive");
391   }
392   fprintf(outfile, ") ");
393
394   print_nv(&nv);
395   fflush(outfile);
396
397   return 0;
398 }
399
400 int verbose_on_frame_recv_callback(nghttp2_session *session,
401                                    const nghttp2_frame *frame,
402                                    void *user_data) {
403   print_timer();
404   fprintf(outfile, " recv ");
405   print_frame(PRINT_RECV, frame);
406   fflush(outfile);
407   return 0;
408 }
409
410 int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
411                                            const nghttp2_frame *frame,
412                                            int lib_error_code,
413                                            void *user_data) {
414   print_timer();
415   fprintf(outfile, " [INVALID; error=%s] recv ",
416           nghttp2_strerror(lib_error_code));
417   print_frame(PRINT_RECV, frame);
418   fflush(outfile);
419   return 0;
420 }
421
422 int verbose_on_frame_send_callback(nghttp2_session *session,
423                                    const nghttp2_frame *frame,
424                                    void *user_data) {
425   print_timer();
426   fprintf(outfile, " send ");
427   print_frame(PRINT_SEND, frame);
428   fflush(outfile);
429   return 0;
430 }
431
432 int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
433                                         int32_t stream_id, const uint8_t *data,
434                                         size_t len, void *user_data) {
435   print_timer();
436   auto srecv =
437       nghttp2_session_get_stream_effective_recv_data_length(session, stream_id);
438   auto crecv = nghttp2_session_get_effective_recv_data_length(session);
439
440   fprintf(outfile,
441           " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n",
442           stream_id, len, srecv, crecv);
443   fflush(outfile);
444
445   return 0;
446 }
447
448 namespace {
449 std::chrono::steady_clock::time_point base_tv;
450 } // namespace
451
452 void reset_timer() { base_tv = std::chrono::steady_clock::now(); }
453
454 std::chrono::milliseconds get_timer() {
455   return time_delta(std::chrono::steady_clock::now(), base_tv);
456 }
457
458 std::chrono::steady_clock::time_point get_time() {
459   return std::chrono::steady_clock::now();
460 }
461
462 ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
463                      size_t inlen) {
464   int rv;
465   z_stream zst;
466   uint8_t temp_out[8192];
467   auto temp_outlen = sizeof(temp_out);
468
469   zst.next_in = Z_NULL;
470   zst.zalloc = Z_NULL;
471   zst.zfree = Z_NULL;
472   zst.opaque = Z_NULL;
473
474   rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
475                     Z_DEFAULT_STRATEGY);
476
477   if (rv != Z_OK) {
478     return -1;
479   }
480
481   zst.avail_in = inlen;
482   zst.next_in = (uint8_t *)in;
483   zst.avail_out = temp_outlen;
484   zst.next_out = temp_out;
485
486   rv = deflate(&zst, Z_FINISH);
487
488   deflateEnd(&zst);
489
490   if (rv != Z_STREAM_END) {
491     return -1;
492   }
493
494   temp_outlen -= zst.avail_out;
495
496   if (temp_outlen > outlen) {
497     return -1;
498   }
499
500   memcpy(out, temp_out, temp_outlen);
501
502   return temp_outlen;
503 }
504
505 } // namespace nghttp2