• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "app_helper.h"
57 #include "util.h"
58 #include "http2.h"
59 #include "template.h"
60 
61 namespace nghttp2 {
62 
63 namespace {
strsettingsid(int32_t id)64 const char *strsettingsid(int32_t id) {
65   switch (id) {
66   case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
67     return "SETTINGS_HEADER_TABLE_SIZE";
68   case NGHTTP2_SETTINGS_ENABLE_PUSH:
69     return "SETTINGS_ENABLE_PUSH";
70   case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
71     return "SETTINGS_MAX_CONCURRENT_STREAMS";
72   case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
73     return "SETTINGS_INITIAL_WINDOW_SIZE";
74   case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
75     return "SETTINGS_MAX_FRAME_SIZE";
76   case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
77     return "SETTINGS_MAX_HEADER_LIST_SIZE";
78   case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
79     return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
80   case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
81     return "SETTINGS_NO_RFC7540_PRIORITIES";
82   default:
83     return "UNKNOWN";
84   }
85 }
86 } // namespace
87 
88 namespace {
strframetype(uint8_t type)89 std::string strframetype(uint8_t type) {
90   switch (type) {
91   case NGHTTP2_DATA:
92     return "DATA";
93   case NGHTTP2_HEADERS:
94     return "HEADERS";
95   case NGHTTP2_PRIORITY:
96     return "PRIORITY";
97   case NGHTTP2_RST_STREAM:
98     return "RST_STREAM";
99   case NGHTTP2_SETTINGS:
100     return "SETTINGS";
101   case NGHTTP2_PUSH_PROMISE:
102     return "PUSH_PROMISE";
103   case NGHTTP2_PING:
104     return "PING";
105   case NGHTTP2_GOAWAY:
106     return "GOAWAY";
107   case NGHTTP2_WINDOW_UPDATE:
108     return "WINDOW_UPDATE";
109   case NGHTTP2_ALTSVC:
110     return "ALTSVC";
111   case NGHTTP2_ORIGIN:
112     return "ORIGIN";
113   case NGHTTP2_PRIORITY_UPDATE:
114     return "PRIORITY_UPDATE";
115   }
116 
117   std::string s = "extension(0x";
118   s += util::format_hex(std::span{&type, 1});
119   s += ')';
120 
121   return s;
122 };
123 } // namespace
124 
125 namespace {
126 bool color_output = false;
127 } // namespace
128 
set_color_output(bool f)129 void set_color_output(bool f) { color_output = f; }
130 
131 namespace {
132 FILE *outfile = stdout;
133 } // namespace
134 
set_output(FILE * file)135 void set_output(FILE *file) { outfile = file; }
136 
137 namespace {
print_frame_attr_indent()138 void print_frame_attr_indent() { fprintf(outfile, "          "); }
139 } // namespace
140 
141 namespace {
ansi_esc(const char * code)142 const char *ansi_esc(const char *code) { return color_output ? code : ""; }
143 } // namespace
144 
145 namespace {
ansi_escend()146 const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
147 } // namespace
148 
149 namespace {
print_nv(nghttp2_nv * nv)150 void print_nv(nghttp2_nv *nv) {
151   fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
152           ansi_escend(), nv->value);
153 }
154 } // namespace
155 namespace {
print_nv(nghttp2_nv * nva,size_t nvlen)156 void print_nv(nghttp2_nv *nva, size_t nvlen) {
157   auto end = nva + nvlen;
158   for (; nva != end; ++nva) {
159     print_frame_attr_indent();
160 
161     print_nv(nva);
162   }
163 }
164 } // namespace
165 
print_timer()166 void print_timer() {
167   auto millis = get_timer();
168   fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"),
169           (long int)(millis.count() / 1000), (long int)(millis.count() % 1000),
170           ansi_escend());
171 }
172 
173 namespace {
print_frame_hd(const nghttp2_frame_hd & hd)174 void print_frame_hd(const nghttp2_frame_hd &hd) {
175   fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n", hd.length,
176           hd.flags, hd.stream_id);
177 }
178 } // namespace
179 
180 namespace {
print_flags(const nghttp2_frame_hd & hd)181 void print_flags(const nghttp2_frame_hd &hd) {
182   std::string s;
183   switch (hd.type) {
184   case NGHTTP2_DATA:
185     if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
186       s += "END_STREAM";
187     }
188     if (hd.flags & NGHTTP2_FLAG_PADDED) {
189       if (!s.empty()) {
190         s += " | ";
191       }
192       s += "PADDED";
193     }
194     break;
195   case NGHTTP2_HEADERS:
196     if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
197       s += "END_STREAM";
198     }
199     if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
200       if (!s.empty()) {
201         s += " | ";
202       }
203       s += "END_HEADERS";
204     }
205     if (hd.flags & NGHTTP2_FLAG_PADDED) {
206       if (!s.empty()) {
207         s += " | ";
208       }
209       s += "PADDED";
210     }
211     if (hd.flags & NGHTTP2_FLAG_PRIORITY) {
212       if (!s.empty()) {
213         s += " | ";
214       }
215       s += "PRIORITY";
216     }
217 
218     break;
219   case NGHTTP2_PRIORITY:
220     break;
221   case NGHTTP2_SETTINGS:
222     if (hd.flags & NGHTTP2_FLAG_ACK) {
223       s += "ACK";
224     }
225     break;
226   case NGHTTP2_PUSH_PROMISE:
227     if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
228       s += "END_HEADERS";
229     }
230     if (hd.flags & NGHTTP2_FLAG_PADDED) {
231       if (!s.empty()) {
232         s += " | ";
233       }
234       s += "PADDED";
235     }
236     break;
237   case NGHTTP2_PING:
238     if (hd.flags & NGHTTP2_FLAG_ACK) {
239       s += "ACK";
240     }
241     break;
242   }
243   fprintf(outfile, "; %s\n", s.c_str());
244 }
245 } // namespace
246 
247 enum print_type { PRINT_SEND, PRINT_RECV };
248 
249 namespace {
frame_name_ansi_esc(print_type ptype)250 const char *frame_name_ansi_esc(print_type ptype) {
251   return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m");
252 }
253 } // namespace
254 
255 namespace {
print_frame(print_type ptype,const nghttp2_frame * frame)256 void print_frame(print_type ptype, const nghttp2_frame *frame) {
257   fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
258           strframetype(frame->hd.type).c_str(), ansi_escend());
259   print_frame_hd(frame->hd);
260   if (frame->hd.flags) {
261     print_frame_attr_indent();
262     print_flags(frame->hd);
263   }
264   switch (frame->hd.type) {
265   case NGHTTP2_DATA:
266     if (frame->data.padlen > 0) {
267       print_frame_attr_indent();
268       fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
269     }
270     break;
271   case NGHTTP2_HEADERS:
272     print_frame_attr_indent();
273     fprintf(outfile, "(padlen=%zu", frame->headers.padlen);
274     if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
275       fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d",
276               frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight,
277               frame->headers.pri_spec.exclusive);
278     }
279     fprintf(outfile, ")\n");
280     switch (frame->headers.cat) {
281     case NGHTTP2_HCAT_REQUEST:
282       print_frame_attr_indent();
283       fprintf(outfile, "; Open new stream\n");
284       break;
285     case NGHTTP2_HCAT_RESPONSE:
286       print_frame_attr_indent();
287       fprintf(outfile, "; First response header\n");
288       break;
289     case NGHTTP2_HCAT_PUSH_RESPONSE:
290       print_frame_attr_indent();
291       fprintf(outfile, "; First push response header\n");
292       break;
293     default:
294       break;
295     }
296     print_nv(frame->headers.nva, frame->headers.nvlen);
297     break;
298   case NGHTTP2_PRIORITY:
299     print_frame_attr_indent();
300 
301     fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n",
302             frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight,
303             frame->priority.pri_spec.exclusive);
304 
305     break;
306   case NGHTTP2_RST_STREAM:
307     print_frame_attr_indent();
308     fprintf(outfile, "(error_code=%s(0x%02x))\n",
309             nghttp2_http2_strerror(frame->rst_stream.error_code),
310             frame->rst_stream.error_code);
311     break;
312   case NGHTTP2_SETTINGS:
313     print_frame_attr_indent();
314     fprintf(outfile, "(niv=%lu)\n",
315             static_cast<unsigned long>(frame->settings.niv));
316     for (size_t i = 0; i < frame->settings.niv; ++i) {
317       print_frame_attr_indent();
318       fprintf(outfile, "[%s(0x%02x):%u]\n",
319               strsettingsid(frame->settings.iv[i].settings_id),
320               frame->settings.iv[i].settings_id, frame->settings.iv[i].value);
321     }
322     break;
323   case NGHTTP2_PUSH_PROMISE:
324     print_frame_attr_indent();
325     fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n",
326             frame->push_promise.padlen, frame->push_promise.promised_stream_id);
327     print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
328     break;
329   case NGHTTP2_PING:
330     print_frame_attr_indent();
331     fprintf(outfile, "(opaque_data=%s)\n",
332             util::format_hex(frame->ping.opaque_data).c_str());
333     break;
334   case NGHTTP2_GOAWAY:
335     print_frame_attr_indent();
336     fprintf(outfile,
337             "(last_stream_id=%d, error_code=%s(0x%02x), "
338             "opaque_data(%u)=[%s])\n",
339             frame->goaway.last_stream_id,
340             nghttp2_http2_strerror(frame->goaway.error_code),
341             frame->goaway.error_code,
342             static_cast<unsigned int>(frame->goaway.opaque_data_len),
343             util::ascii_dump(frame->goaway.opaque_data,
344                              frame->goaway.opaque_data_len)
345                 .c_str());
346     break;
347   case NGHTTP2_WINDOW_UPDATE:
348     print_frame_attr_indent();
349     fprintf(outfile, "(window_size_increment=%d)\n",
350             frame->window_update.window_size_increment);
351     break;
352   case NGHTTP2_ALTSVC: {
353     auto altsvc = static_cast<nghttp2_ext_altsvc *>(frame->ext.payload);
354     print_frame_attr_indent();
355     fprintf(outfile, "(origin=[%.*s], altsvc_field_value=[%.*s])\n",
356             static_cast<int>(altsvc->origin_len), altsvc->origin,
357             static_cast<int>(altsvc->field_value_len), altsvc->field_value);
358     break;
359   }
360   case NGHTTP2_ORIGIN: {
361     auto origin = static_cast<nghttp2_ext_origin *>(frame->ext.payload);
362     for (size_t i = 0; i < origin->nov; ++i) {
363       auto ent = &origin->ov[i];
364       print_frame_attr_indent();
365       fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin);
366     }
367     break;
368   }
369   case NGHTTP2_PRIORITY_UPDATE: {
370     auto priority_update =
371         static_cast<nghttp2_ext_priority_update *>(frame->ext.payload);
372     print_frame_attr_indent();
373     fprintf(outfile,
374             "(prioritized_stream_id=%d, priority_field_value=[%.*s])\n",
375             priority_update->stream_id,
376             static_cast<int>(priority_update->field_value_len),
377             priority_update->field_value);
378     break;
379   }
380   default:
381     break;
382   }
383 }
384 } // namespace
385 
verbose_on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)386 int verbose_on_header_callback(nghttp2_session *session,
387                                const nghttp2_frame *frame, const uint8_t *name,
388                                size_t namelen, const uint8_t *value,
389                                size_t valuelen, uint8_t flags,
390                                void *user_data) {
391   nghttp2_nv nv = {const_cast<uint8_t *>(name), const_cast<uint8_t *>(value),
392                    namelen, valuelen};
393 
394   print_timer();
395   fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
396   if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
397     fprintf(outfile, ", sensitive");
398   }
399   fprintf(outfile, ") ");
400 
401   print_nv(&nv);
402   fflush(outfile);
403 
404   return 0;
405 }
406 
verbose_on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)407 int verbose_on_frame_recv_callback(nghttp2_session *session,
408                                    const nghttp2_frame *frame,
409                                    void *user_data) {
410   print_timer();
411   fprintf(outfile, " recv ");
412   print_frame(PRINT_RECV, frame);
413   fflush(outfile);
414   return 0;
415 }
416 
verbose_on_invalid_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)417 int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
418                                            const nghttp2_frame *frame,
419                                            int lib_error_code,
420                                            void *user_data) {
421   print_timer();
422   fprintf(outfile, " [INVALID; error=%s] recv ",
423           nghttp2_strerror(lib_error_code));
424   print_frame(PRINT_RECV, frame);
425   fflush(outfile);
426   return 0;
427 }
428 
verbose_on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)429 int verbose_on_frame_send_callback(nghttp2_session *session,
430                                    const nghttp2_frame *frame,
431                                    void *user_data) {
432   print_timer();
433   fprintf(outfile, " send ");
434   print_frame(PRINT_SEND, frame);
435   fflush(outfile);
436   return 0;
437 }
438 
verbose_on_data_chunk_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)439 int verbose_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
440                                         int32_t stream_id, const uint8_t *data,
441                                         size_t len, void *user_data) {
442   print_timer();
443   auto srecv =
444       nghttp2_session_get_stream_effective_recv_data_length(session, stream_id);
445   auto crecv = nghttp2_session_get_effective_recv_data_length(session);
446 
447   fprintf(outfile,
448           " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n",
449           stream_id, len, srecv, crecv);
450   fflush(outfile);
451 
452   return 0;
453 }
454 
verbose_error_callback(nghttp2_session * session,int lib_error_code,const char * msg,size_t len,void * user_data)455 int verbose_error_callback(nghttp2_session *session, int lib_error_code,
456                            const char *msg, size_t len, void *user_data) {
457   print_timer();
458   fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
459   fflush(outfile);
460 
461   return 0;
462 }
463 
464 namespace {
465 std::chrono::steady_clock::time_point base_tv;
466 } // namespace
467 
reset_timer()468 void reset_timer() { base_tv = std::chrono::steady_clock::now(); }
469 
get_timer()470 std::chrono::milliseconds get_timer() {
471   return time_delta(std::chrono::steady_clock::now(), base_tv);
472 }
473 
get_time()474 std::chrono::steady_clock::time_point get_time() {
475   return std::chrono::steady_clock::now();
476 }
477 
478 } // namespace nghttp2
479