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