2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2012 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
25 #include <sys/types.h>
26 #ifdef HAVE_SYS_SOCKET_H
27 #include <sys/socket.h>
28 #endif // HAVE_SYS_SOCKET_H
31 #endif // HAVE_NETDB_H
34 #endif // HAVE_UNISTD_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>
58 #include "app_helper.h"
65 const char *strstatus(uint32_t error_code) {
67 case NGHTTP2_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";
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";
102 const char *strsettingsid(int32_t 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";
123 const char *strframetype(uint8_t type) {
127 case NGHTTP2_HEADERS:
129 case NGHTTP2_PRIORITY:
131 case NGHTTP2_RST_STREAM:
133 case NGHTTP2_SETTINGS:
135 case NGHTTP2_PUSH_PROMISE:
136 return "PUSH_PROMISE";
141 case NGHTTP2_WINDOW_UPDATE:
142 return "WINDOW_UPDATE";
150 bool color_output = false;
153 void set_color_output(bool f) { color_output = f; }
156 FILE *outfile = stdout;
159 void set_output(FILE *file) { outfile = file; }
162 void print_frame_attr_indent() { fprintf(outfile, " "); }
166 const char *ansi_esc(const char *code) { return color_output ? code : ""; }
170 const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
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);
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();
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),
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);
205 void print_flags(const nghttp2_frame_hd &hd) {
209 if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
212 if (hd.flags & NGHTTP2_FLAG_PADDED) {
219 case NGHTTP2_HEADERS:
220 if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
223 if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
229 if (hd.flags & NGHTTP2_FLAG_PADDED) {
235 if (hd.flags & NGHTTP2_FLAG_PRIORITY) {
243 case NGHTTP2_PRIORITY:
245 case NGHTTP2_SETTINGS:
246 if (hd.flags & NGHTTP2_FLAG_ACK) {
250 case NGHTTP2_PUSH_PROMISE:
251 if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
254 if (hd.flags & NGHTTP2_FLAG_PADDED) {
262 if (hd.flags & NGHTTP2_FLAG_ACK) {
267 fprintf(outfile, "; %s\n", s.c_str());
271 enum print_type { PRINT_SEND, PRINT_RECV };
274 const char *frame_name_ansi_esc(print_type ptype) {
275 return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m");
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);
288 switch (frame->hd.type) {
290 if (frame->data.padlen > 0) {
291 print_frame_attr_indent();
292 fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
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);
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");
309 case NGHTTP2_HCAT_RESPONSE:
310 print_frame_attr_indent();
311 fprintf(outfile, "; First response header\n");
313 case NGHTTP2_HCAT_PUSH_RESPONSE:
314 print_frame_attr_indent();
315 fprintf(outfile, "; First push response header\n");
320 print_nv(frame->headers.nva, frame->headers.nvlen);
322 case NGHTTP2_PRIORITY:
323 print_frame_attr_indent();
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);
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);
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);
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);
354 print_frame_attr_indent();
355 fprintf(outfile, "(opaque_data=%s)\n",
356 util::format_hex(frame->ping.opaque_data, 8).c_str());
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());
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);
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,
384 nghttp2_nv nv = {const_cast<uint8_t *>(name), const_cast<uint8_t *>(value),
388 fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
389 if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
390 fprintf(outfile, ", sensitive");
392 fprintf(outfile, ") ");
400 int verbose_on_frame_recv_callback(nghttp2_session *session,
401 const nghttp2_frame *frame,
404 fprintf(outfile, " recv ");
405 print_frame(PRINT_RECV, frame);
410 int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
411 const nghttp2_frame *frame,
415 fprintf(outfile, " [INVALID; error=%s] recv ",
416 nghttp2_strerror(lib_error_code));
417 print_frame(PRINT_RECV, frame);
422 int verbose_on_frame_send_callback(nghttp2_session *session,
423 const nghttp2_frame *frame,
426 fprintf(outfile, " send ");
427 print_frame(PRINT_SEND, frame);
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) {
437 nghttp2_session_get_stream_effective_recv_data_length(session, stream_id);
438 auto crecv = nghttp2_session_get_effective_recv_data_length(session);
441 " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n",
442 stream_id, len, srecv, crecv);
449 std::chrono::steady_clock::time_point base_tv;
452 void reset_timer() { base_tv = std::chrono::steady_clock::now(); }
454 std::chrono::milliseconds get_timer() {
455 return time_delta(std::chrono::steady_clock::now(), base_tv);
458 std::chrono::steady_clock::time_point get_time() {
459 return std::chrono::steady_clock::now();
462 ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
466 uint8_t temp_out[8192];
467 auto temp_outlen = sizeof(temp_out);
469 zst.next_in = Z_NULL;
474 rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
481 zst.avail_in = inlen;
482 zst.next_in = (uint8_t *)in;
483 zst.avail_out = temp_outlen;
484 zst.next_out = temp_out;
486 rv = deflate(&zst, Z_FINISH);
490 if (rv != Z_STREAM_END) {
494 temp_outlen -= zst.avail_out;
496 if (temp_outlen > outlen) {
500 memcpy(out, temp_out, temp_outlen);
505 } // namespace nghttp2