diff --git a/src/HttpServer.cc b/src/HttpServer.cc index 5ce17b46..e58e800f 100644 --- a/src/HttpServer.cc +++ b/src/HttpServer.cc @@ -96,7 +96,7 @@ Config::Config() session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1), header_table_size(-1), port(0), verbose(false), daemon(false), verify_client(false), no_tls(false), error_gzip(false), - early_response(false) { + early_response(false), hexdump(false) { nghttp2_option_new(&session_option); nghttp2_option_set_recv_client_preface(session_option, 1); } @@ -426,6 +426,11 @@ int Http2Handler::read_clear() { if (nread == 0) { return -1; } + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); if (rv < 0) { if (rv != NGHTTP2_ERR_BAD_PREFACE) { @@ -548,6 +553,11 @@ int Http2Handler::read_tls() { } auto nread = rv; + + if (get_config()->hexdump) { + util::hexdump(stdout, buf.data(), nread); + } + rv = nghttp2_session_mem_recv(session_, buf.data(), nread); if (rv < 0) { if (rv != NGHTTP2_ERR_BAD_PREFACE) { diff --git a/src/HttpServer.h b/src/HttpServer.h index 5b8de7ef..8ca52775 100644 --- a/src/HttpServer.h +++ b/src/HttpServer.h @@ -71,6 +71,7 @@ struct Config { bool no_tls; bool error_gzip; bool early_response; + bool hexdump; Config(); ~Config(); }; diff --git a/src/nghttp.cc b/src/nghttp.cc index 10b7e712..bfed0372 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -79,7 +79,7 @@ Config::Config() timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0), null_out(false), remote_name(false), get_assets(false), stat(false), upgrade(false), continuation(false), no_content_length(false), - no_dep(false), dep_idle(false) { + no_dep(false), dep_idle(false), hexdump(false) { nghttp2_option_new(&http2_option); nghttp2_option_set_peer_max_concurrent_streams(http2_option, peer_max_concurrent_streams); @@ -1048,6 +1048,10 @@ int HttpClient::on_connect() { } int HttpClient::on_read(const uint8_t *data, size_t len) { + if (config.hexdump) { + util::hexdump(stdout, data, len); + } + auto rv = nghttp2_session_mem_recv(session, data, len); if (rv < 0) { std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: " @@ -2421,6 +2425,8 @@ Options: Don't send content-length header field. --no-dep Don't send dependency based priority hint to server. --dep-idle Use idle streams as anchor nodes to express priority. + --hexdump Output incoming traffic in `hexdump -C` format. If + SSL/TLS is used, decrypted data are used. --version Display version information and exit. -h, --help Display this help and exit. @@ -2461,6 +2467,7 @@ int main(int argc, char **argv) { {"no-dep", no_argument, &flag, 7}, {"dep-idle", no_argument, &flag, 8}, {"trailer", required_argument, &flag, 9}, + {"hexdump", no_argument, &flag, 10}, {nullptr, 0, nullptr, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:", @@ -2647,6 +2654,10 @@ int main(int argc, char **argv) { util::inp_strlower(config.trailer.back().name); break; } + case 10: + // hexdump option + config.hexdump = true; + break; } break; default: diff --git a/src/nghttp.h b/src/nghttp.h index dc45fa6d..7466e099 100644 --- a/src/nghttp.h +++ b/src/nghttp.h @@ -84,6 +84,7 @@ struct Config { bool no_content_length; bool no_dep; bool dep_idle; + bool hexdump; }; enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE }; diff --git a/src/nghttpd.cc b/src/nghttpd.cc index 275a4827..f2c14f48 100644 --- a/src/nghttpd.cc +++ b/src/nghttpd.cc @@ -145,6 +145,8 @@ Options: include pseudo header field (header field name starting with ':'). The trailer is sent only if a response has body part. Example: --trailer 'foo: bar'. + --hexdump Output incoming traffic in `hexdump -C` format. If + SSL/TLS is used, decrypted data are used. --version Display version information and exit. -h, --help Display this help and exit. @@ -176,6 +178,7 @@ int main(int argc, char **argv) { {"dh-param-file", required_argument, &flag, 4}, {"early-response", no_argument, &flag, 5}, {"trailer", required_argument, &flag, 6}, + {"hexdump", no_argument, &flag, 7}, {nullptr, 0, nullptr, 0}}; int option_index = 0; int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options, @@ -284,6 +287,10 @@ int main(int argc, char **argv) { util::inp_strlower(config.trailer.back().name); break; } + case 7: + // hexdump option + config.hexdump = true; + break; } break; default: diff --git a/src/util.cc b/src/util.cc index 1b6ffb74..ab948d7e 100644 --- a/src/util.cc +++ b/src/util.cc @@ -1046,6 +1046,70 @@ std::string make_hostport(const char *host, uint16_t port) { return hostport; } +#define NGHTTP2_SPACES \ + " " \ + " " + +namespace { +void hexdump8(FILE *out, const uint8_t *first, const uint8_t *last) { + auto stop = std::min(first + 8, last); + for (auto k = first; k != stop; ++k) { + fprintf(out, "%02x ", *k); + } + /* each byte needs 3 spaces (2 hex value and space). we have extra + space after 8 bytes */ + fprintf(out, "%.*s", static_cast((first + 8 - stop) * 3 + 1), + NGHTTP2_SPACES); +} +} // namespace + +void hexdump(FILE *out, const uint8_t *src, size_t len) { + if (len == 0) { + return; + } + size_t buflen = 0; + auto repeated = false; + std::array buf{}; + auto end = src + len; + auto i = src; + for (;;) { + auto nextlen = std::min(static_cast(16), end - i); + if (nextlen == buflen && + std::equal(std::begin(buf), std::begin(buf) + buflen, i)) { + /* as long as adjacent 16 bytes block are the same, we just + print single '*'. */ + if (!repeated) { + repeated = true; + fwrite("*\n", 2, 1, out); + } + i += nextlen; + continue; + } + repeated = false; + fprintf(out, "%08lx", static_cast(i - src)); + if (i == end) { + fwrite("\n", 2, 1, out); + break; + } + fwrite(" ", 2, 1, out); + hexdump8(out, i, end); + hexdump8(out, i + 8, std::max(i + 8, end)); + fwrite("|", 1, 1, out); + auto stop = std::min(i + 16, end); + buflen = stop - i; + auto p = buf.data(); + for (; i != stop; ++i) { + *p++ = *i; + if (0x20 <= *i && *i <= 0x7e) { + fwrite(i, 1, 1, out); + } else { + fwrite(".", 1, 1, out); + } + } + fwrite("|\n", 2, 1, out); + } +} + } // namespace util } // namespace nghttp2 diff --git a/src/util.h b/src/util.h index b30eb707..f20624d5 100644 --- a/src/util.h +++ b/src/util.h @@ -612,6 +612,9 @@ std::string format_duration(const std::chrono::microseconds &u); // and "]". If |port| is 80 or 443, port part is omitted. std::string make_hostport(const char *host, uint16_t port); +// Dumps |src| of length |len| in the format similar to `hexdump -C`. +void hexdump(FILE *out, const uint8_t *src, size_t len); + } // namespace util } // namespace nghttp2