summaryrefslogtreecommitdiffstats
path: root/src/app_helper.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/app_helper.cc')
-rw-r--r--src/app_helper.cc518
1 files changed, 518 insertions, 0 deletions
diff --git a/src/app_helper.cc b/src/app_helper.cc
new file mode 100644
index 0000000..ef92762
--- /dev/null
+++ b/src/app_helper.cc
@@ -0,0 +1,518 @@
+/*
+ * nghttp2 - HTTP/2 C Library
+ *
+ * Copyright (c) 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif // HAVE_SYS_SOCKET_H
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif // HAVE_NETDB_H
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif // HAVE_UNISTD_H
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif // HAVE_FCNTL_H
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif // HAVE_NETINET_IN_H
+#include <netinet/tcp.h>
+#include <poll.h>
+
+#include <cassert>
+#include <cstdio>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <iostream>
+#include <string>
+#include <set>
+#include <iomanip>
+#include <fstream>
+
+#include <zlib.h>
+
+#include "app_helper.h"
+#include "util.h"
+#include "http2.h"
+#include "template.h"
+
+namespace nghttp2 {
+
+namespace {
+const char *strsettingsid(int32_t id) {
+ switch (id) {
+ case NGHTTP2_SETTINGS_HEADER_TABLE_SIZE:
+ return "SETTINGS_HEADER_TABLE_SIZE";
+ case NGHTTP2_SETTINGS_ENABLE_PUSH:
+ return "SETTINGS_ENABLE_PUSH";
+ case NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
+ return "SETTINGS_MAX_CONCURRENT_STREAMS";
+ case NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
+ return "SETTINGS_INITIAL_WINDOW_SIZE";
+ case NGHTTP2_SETTINGS_MAX_FRAME_SIZE:
+ return "SETTINGS_MAX_FRAME_SIZE";
+ case NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
+ return "SETTINGS_MAX_HEADER_LIST_SIZE";
+ case NGHTTP2_SETTINGS_ENABLE_CONNECT_PROTOCOL:
+ return "SETTINGS_ENABLE_CONNECT_PROTOCOL";
+ case NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES:
+ return "SETTINGS_NO_RFC7540_PRIORITIES";
+ default:
+ return "UNKNOWN";
+ }
+}
+} // namespace
+
+namespace {
+std::string strframetype(uint8_t type) {
+ switch (type) {
+ case NGHTTP2_DATA:
+ return "DATA";
+ case NGHTTP2_HEADERS:
+ return "HEADERS";
+ case NGHTTP2_PRIORITY:
+ return "PRIORITY";
+ case NGHTTP2_RST_STREAM:
+ return "RST_STREAM";
+ case NGHTTP2_SETTINGS:
+ return "SETTINGS";
+ case NGHTTP2_PUSH_PROMISE:
+ return "PUSH_PROMISE";
+ case NGHTTP2_PING:
+ return "PING";
+ case NGHTTP2_GOAWAY:
+ return "GOAWAY";
+ case NGHTTP2_WINDOW_UPDATE:
+ return "WINDOW_UPDATE";
+ case NGHTTP2_ALTSVC:
+ return "ALTSVC";
+ case NGHTTP2_ORIGIN:
+ return "ORIGIN";
+ case NGHTTP2_PRIORITY_UPDATE:
+ return "PRIORITY_UPDATE";
+ }
+
+ std::string s = "extension(0x";
+ s += util::format_hex(&type, 1);
+ s += ')';
+
+ return s;
+};
+} // namespace
+
+namespace {
+bool color_output = false;
+} // namespace
+
+void set_color_output(bool f) { color_output = f; }
+
+namespace {
+FILE *outfile = stdout;
+} // namespace
+
+void set_output(FILE *file) { outfile = file; }
+
+namespace {
+void print_frame_attr_indent() { fprintf(outfile, " "); }
+} // namespace
+
+namespace {
+const char *ansi_esc(const char *code) { return color_output ? code : ""; }
+} // namespace
+
+namespace {
+const char *ansi_escend() { return color_output ? "\033[0m" : ""; }
+} // namespace
+
+namespace {
+void print_nv(nghttp2_nv *nv) {
+ fprintf(outfile, "%s%s%s: %s\n", ansi_esc("\033[1;34m"), nv->name,
+ ansi_escend(), nv->value);
+}
+} // namespace
+namespace {
+void print_nv(nghttp2_nv *nva, size_t nvlen) {
+ auto end = nva + nvlen;
+ for (; nva != end; ++nva) {
+ print_frame_attr_indent();
+
+ print_nv(nva);
+ }
+}
+} // namespace
+
+void print_timer() {
+ auto millis = get_timer();
+ fprintf(outfile, "%s[%3ld.%03ld]%s", ansi_esc("\033[33m"),
+ (long int)(millis.count() / 1000), (long int)(millis.count() % 1000),
+ ansi_escend());
+}
+
+namespace {
+void print_frame_hd(const nghttp2_frame_hd &hd) {
+ fprintf(outfile, "<length=%zu, flags=0x%02x, stream_id=%d>\n", hd.length,
+ hd.flags, hd.stream_id);
+}
+} // namespace
+
+namespace {
+void print_flags(const nghttp2_frame_hd &hd) {
+ std::string s;
+ switch (hd.type) {
+ case NGHTTP2_DATA:
+ if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ s += "END_STREAM";
+ }
+ if (hd.flags & NGHTTP2_FLAG_PADDED) {
+ if (!s.empty()) {
+ s += " | ";
+ }
+ s += "PADDED";
+ }
+ break;
+ case NGHTTP2_HEADERS:
+ if (hd.flags & NGHTTP2_FLAG_END_STREAM) {
+ s += "END_STREAM";
+ }
+ if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
+ if (!s.empty()) {
+ s += " | ";
+ }
+ s += "END_HEADERS";
+ }
+ if (hd.flags & NGHTTP2_FLAG_PADDED) {
+ if (!s.empty()) {
+ s += " | ";
+ }
+ s += "PADDED";
+ }
+ if (hd.flags & NGHTTP2_FLAG_PRIORITY) {
+ if (!s.empty()) {
+ s += " | ";
+ }
+ s += "PRIORITY";
+ }
+
+ break;
+ case NGHTTP2_PRIORITY:
+ break;
+ case NGHTTP2_SETTINGS:
+ if (hd.flags & NGHTTP2_FLAG_ACK) {
+ s += "ACK";
+ }
+ break;
+ case NGHTTP2_PUSH_PROMISE:
+ if (hd.flags & NGHTTP2_FLAG_END_HEADERS) {
+ s += "END_HEADERS";
+ }
+ if (hd.flags & NGHTTP2_FLAG_PADDED) {
+ if (!s.empty()) {
+ s += " | ";
+ }
+ s += "PADDED";
+ }
+ break;
+ case NGHTTP2_PING:
+ if (hd.flags & NGHTTP2_FLAG_ACK) {
+ s += "ACK";
+ }
+ break;
+ }
+ fprintf(outfile, "; %s\n", s.c_str());
+}
+} // namespace
+
+enum print_type { PRINT_SEND, PRINT_RECV };
+
+namespace {
+const char *frame_name_ansi_esc(print_type ptype) {
+ return ansi_esc(ptype == PRINT_SEND ? "\033[1;35m" : "\033[1;36m");
+}
+} // namespace
+
+namespace {
+void print_frame(print_type ptype, const nghttp2_frame *frame) {
+ fprintf(outfile, "%s%s%s frame ", frame_name_ansi_esc(ptype),
+ strframetype(frame->hd.type).c_str(), ansi_escend());
+ print_frame_hd(frame->hd);
+ if (frame->hd.flags) {
+ print_frame_attr_indent();
+ print_flags(frame->hd);
+ }
+ switch (frame->hd.type) {
+ case NGHTTP2_DATA:
+ if (frame->data.padlen > 0) {
+ print_frame_attr_indent();
+ fprintf(outfile, "(padlen=%zu)\n", frame->data.padlen);
+ }
+ break;
+ case NGHTTP2_HEADERS:
+ print_frame_attr_indent();
+ fprintf(outfile, "(padlen=%zu", frame->headers.padlen);
+ if (frame->hd.flags & NGHTTP2_FLAG_PRIORITY) {
+ fprintf(outfile, ", dep_stream_id=%d, weight=%u, exclusive=%d",
+ frame->headers.pri_spec.stream_id, frame->headers.pri_spec.weight,
+ frame->headers.pri_spec.exclusive);
+ }
+ fprintf(outfile, ")\n");
+ switch (frame->headers.cat) {
+ case NGHTTP2_HCAT_REQUEST:
+ print_frame_attr_indent();
+ fprintf(outfile, "; Open new stream\n");
+ break;
+ case NGHTTP2_HCAT_RESPONSE:
+ print_frame_attr_indent();
+ fprintf(outfile, "; First response header\n");
+ break;
+ case NGHTTP2_HCAT_PUSH_RESPONSE:
+ print_frame_attr_indent();
+ fprintf(outfile, "; First push response header\n");
+ break;
+ default:
+ break;
+ }
+ print_nv(frame->headers.nva, frame->headers.nvlen);
+ break;
+ case NGHTTP2_PRIORITY:
+ print_frame_attr_indent();
+
+ fprintf(outfile, "(dep_stream_id=%d, weight=%u, exclusive=%d)\n",
+ frame->priority.pri_spec.stream_id, frame->priority.pri_spec.weight,
+ frame->priority.pri_spec.exclusive);
+
+ break;
+ case NGHTTP2_RST_STREAM:
+ print_frame_attr_indent();
+ fprintf(outfile, "(error_code=%s(0x%02x))\n",
+ nghttp2_http2_strerror(frame->rst_stream.error_code),
+ frame->rst_stream.error_code);
+ break;
+ case NGHTTP2_SETTINGS:
+ print_frame_attr_indent();
+ fprintf(outfile, "(niv=%lu)\n",
+ static_cast<unsigned long>(frame->settings.niv));
+ for (size_t i = 0; i < frame->settings.niv; ++i) {
+ print_frame_attr_indent();
+ fprintf(outfile, "[%s(0x%02x):%u]\n",
+ strsettingsid(frame->settings.iv[i].settings_id),
+ frame->settings.iv[i].settings_id, frame->settings.iv[i].value);
+ }
+ break;
+ case NGHTTP2_PUSH_PROMISE:
+ print_frame_attr_indent();
+ fprintf(outfile, "(padlen=%zu, promised_stream_id=%d)\n",
+ frame->push_promise.padlen, frame->push_promise.promised_stream_id);
+ print_nv(frame->push_promise.nva, frame->push_promise.nvlen);
+ break;
+ case NGHTTP2_PING:
+ print_frame_attr_indent();
+ fprintf(outfile, "(opaque_data=%s)\n",
+ util::format_hex(frame->ping.opaque_data, 8).c_str());
+ break;
+ case NGHTTP2_GOAWAY:
+ print_frame_attr_indent();
+ fprintf(outfile,
+ "(last_stream_id=%d, error_code=%s(0x%02x), "
+ "opaque_data(%u)=[%s])\n",
+ frame->goaway.last_stream_id,
+ nghttp2_http2_strerror(frame->goaway.error_code),
+ frame->goaway.error_code,
+ static_cast<unsigned int>(frame->goaway.opaque_data_len),
+ util::ascii_dump(frame->goaway.opaque_data,
+ frame->goaway.opaque_data_len)
+ .c_str());
+ break;
+ case NGHTTP2_WINDOW_UPDATE:
+ print_frame_attr_indent();
+ fprintf(outfile, "(window_size_increment=%d)\n",
+ frame->window_update.window_size_increment);
+ break;
+ case NGHTTP2_ALTSVC: {
+ auto altsvc = static_cast<nghttp2_ext_altsvc *>(frame->ext.payload);
+ print_frame_attr_indent();
+ fprintf(outfile, "(origin=[%.*s], altsvc_field_value=[%.*s])\n",
+ static_cast<int>(altsvc->origin_len), altsvc->origin,
+ static_cast<int>(altsvc->field_value_len), altsvc->field_value);
+ break;
+ }
+ case NGHTTP2_ORIGIN: {
+ auto origin = static_cast<nghttp2_ext_origin *>(frame->ext.payload);
+ for (size_t i = 0; i < origin->nov; ++i) {
+ auto ent = &origin->ov[i];
+ print_frame_attr_indent();
+ fprintf(outfile, "[%.*s]\n", (int)ent->origin_len, ent->origin);
+ }
+ break;
+ }
+ case NGHTTP2_PRIORITY_UPDATE: {
+ auto priority_update =
+ static_cast<nghttp2_ext_priority_update *>(frame->ext.payload);
+ print_frame_attr_indent();
+ fprintf(outfile,
+ "(prioritized_stream_id=%d, priority_field_value=[%.*s])\n",
+ priority_update->stream_id,
+ static_cast<int>(priority_update->field_value_len),
+ priority_update->field_value);
+ break;
+ }
+ default:
+ break;
+ }
+}
+} // namespace
+
+int 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) {
+ nghttp2_nv nv = {const_cast<uint8_t *>(name), const_cast<uint8_t *>(value),
+ namelen, valuelen};
+
+ print_timer();
+ fprintf(outfile, " recv (stream_id=%d", frame->hd.stream_id);
+ if (flags & NGHTTP2_NV_FLAG_NO_INDEX) {
+ fprintf(outfile, ", sensitive");
+ }
+ fprintf(outfile, ") ");
+
+ print_nv(&nv);
+ fflush(outfile);
+
+ return 0;
+}
+
+int verbose_on_frame_recv_callback(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ void *user_data) {
+ print_timer();
+ fprintf(outfile, " recv ");
+ print_frame(PRINT_RECV, frame);
+ fflush(outfile);
+ return 0;
+}
+
+int verbose_on_invalid_frame_recv_callback(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ int lib_error_code,
+ void *user_data) {
+ print_timer();
+ fprintf(outfile, " [INVALID; error=%s] recv ",
+ nghttp2_strerror(lib_error_code));
+ print_frame(PRINT_RECV, frame);
+ fflush(outfile);
+ return 0;
+}
+
+int verbose_on_frame_send_callback(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ void *user_data) {
+ print_timer();
+ fprintf(outfile, " send ");
+ print_frame(PRINT_SEND, frame);
+ fflush(outfile);
+ return 0;
+}
+
+int 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) {
+ print_timer();
+ auto srecv =
+ nghttp2_session_get_stream_effective_recv_data_length(session, stream_id);
+ auto crecv = nghttp2_session_get_effective_recv_data_length(session);
+
+ fprintf(outfile,
+ " recv (stream_id=%d, length=%zu, srecv=%d, crecv=%d) DATA\n",
+ stream_id, len, srecv, crecv);
+ fflush(outfile);
+
+ return 0;
+}
+
+int verbose_error_callback(nghttp2_session *session, int lib_error_code,
+ const char *msg, size_t len, void *user_data) {
+ print_timer();
+ fprintf(outfile, " [ERROR] %.*s\n", (int)len, msg);
+ fflush(outfile);
+
+ return 0;
+}
+
+namespace {
+std::chrono::steady_clock::time_point base_tv;
+} // namespace
+
+void reset_timer() { base_tv = std::chrono::steady_clock::now(); }
+
+std::chrono::milliseconds get_timer() {
+ return time_delta(std::chrono::steady_clock::now(), base_tv);
+}
+
+std::chrono::steady_clock::time_point get_time() {
+ return std::chrono::steady_clock::now();
+}
+
+ssize_t deflate_data(uint8_t *out, size_t outlen, const uint8_t *in,
+ size_t inlen) {
+ int rv;
+ z_stream zst{};
+ uint8_t temp_out[8_k];
+ auto temp_outlen = sizeof(temp_out);
+
+ rv = deflateInit2(&zst, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 9,
+ Z_DEFAULT_STRATEGY);
+
+ if (rv != Z_OK) {
+ return -1;
+ }
+
+ zst.avail_in = inlen;
+ zst.next_in = (uint8_t *)in;
+ zst.avail_out = temp_outlen;
+ zst.next_out = temp_out;
+
+ rv = deflate(&zst, Z_FINISH);
+
+ deflateEnd(&zst);
+
+ if (rv != Z_STREAM_END) {
+ return -1;
+ }
+
+ temp_outlen -= zst.avail_out;
+
+ if (temp_outlen > outlen) {
+ return -1;
+ }
+
+ memcpy(out, temp_out, temp_outlen);
+
+ return temp_outlen;
+}
+
+} // namespace nghttp2