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