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