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