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