nghttp, nghttpd: Add --hexdump option to hexdump incoming traffic
The output format is similar to `hexdump -C`
This commit is contained in:
parent
72843b33d0
commit
89b8039466
|
@ -96,7 +96,7 @@ Config::Config()
|
||||||
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
session_option(nullptr), data_ptr(nullptr), padding(0), num_worker(1),
|
||||||
header_table_size(-1), port(0), verbose(false), daemon(false),
|
header_table_size(-1), port(0), verbose(false), daemon(false),
|
||||||
verify_client(false), no_tls(false), error_gzip(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_new(&session_option);
|
||||||
nghttp2_option_set_recv_client_preface(session_option, 1);
|
nghttp2_option_set_recv_client_preface(session_option, 1);
|
||||||
}
|
}
|
||||||
|
@ -426,6 +426,11 @@ int Http2Handler::read_clear() {
|
||||||
if (nread == 0) {
|
if (nread == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_config()->hexdump) {
|
||||||
|
util::hexdump(stdout, buf.data(), nread);
|
||||||
|
}
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||||
|
@ -548,6 +553,11 @@ int Http2Handler::read_tls() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nread = rv;
|
auto nread = rv;
|
||||||
|
|
||||||
|
if (get_config()->hexdump) {
|
||||||
|
util::hexdump(stdout, buf.data(), nread);
|
||||||
|
}
|
||||||
|
|
||||||
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
if (rv != NGHTTP2_ERR_BAD_PREFACE) {
|
||||||
|
|
|
@ -71,6 +71,7 @@ struct Config {
|
||||||
bool no_tls;
|
bool no_tls;
|
||||||
bool error_gzip;
|
bool error_gzip;
|
||||||
bool early_response;
|
bool early_response;
|
||||||
|
bool hexdump;
|
||||||
Config();
|
Config();
|
||||||
~Config();
|
~Config();
|
||||||
};
|
};
|
||||||
|
|
|
@ -79,7 +79,7 @@ Config::Config()
|
||||||
timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0),
|
timeout(0.), window_bits(-1), connection_window_bits(-1), verbose(0),
|
||||||
null_out(false), remote_name(false), get_assets(false), stat(false),
|
null_out(false), remote_name(false), get_assets(false), stat(false),
|
||||||
upgrade(false), continuation(false), no_content_length(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_new(&http2_option);
|
||||||
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
nghttp2_option_set_peer_max_concurrent_streams(http2_option,
|
||||||
peer_max_concurrent_streams);
|
peer_max_concurrent_streams);
|
||||||
|
@ -1048,6 +1048,10 @@ int HttpClient::on_connect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int HttpClient::on_read(const uint8_t *data, size_t len) {
|
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);
|
auto rv = nghttp2_session_mem_recv(session, data, len);
|
||||||
if (rv < 0) {
|
if (rv < 0) {
|
||||||
std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: "
|
std::cerr << "[ERROR] nghttp2_session_mem_recv() returned error: "
|
||||||
|
@ -2421,6 +2425,8 @@ Options:
|
||||||
Don't send content-length header field.
|
Don't send content-length header field.
|
||||||
--no-dep Don't send dependency based priority hint to server.
|
--no-dep Don't send dependency based priority hint to server.
|
||||||
--dep-idle Use idle streams as anchor nodes to express priority.
|
--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.
|
--version Display version information and exit.
|
||||||
-h, --help Display this help 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},
|
{"no-dep", no_argument, &flag, 7},
|
||||||
{"dep-idle", no_argument, &flag, 8},
|
{"dep-idle", no_argument, &flag, 8},
|
||||||
{"trailer", required_argument, &flag, 9},
|
{"trailer", required_argument, &flag, 9},
|
||||||
|
{"hexdump", no_argument, &flag, 10},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long(argc, argv, "M:Oab:c:d:gm:np:r:hH:vst:uw:W:",
|
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);
|
util::inp_strlower(config.trailer.back().name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 10:
|
||||||
|
// hexdump option
|
||||||
|
config.hexdump = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct Config {
|
||||||
bool no_content_length;
|
bool no_content_length;
|
||||||
bool no_dep;
|
bool no_dep;
|
||||||
bool dep_idle;
|
bool dep_idle;
|
||||||
|
bool hexdump;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
|
enum class RequestState { INITIAL, ON_REQUEST, ON_RESPONSE, ON_COMPLETE };
|
||||||
|
|
|
@ -145,6 +145,8 @@ Options:
|
||||||
include pseudo header field (header field name starting
|
include pseudo header field (header field name starting
|
||||||
with ':'). The trailer is sent only if a response has
|
with ':'). The trailer is sent only if a response has
|
||||||
body part. Example: --trailer 'foo: bar'.
|
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.
|
--version Display version information and exit.
|
||||||
-h, --help Display this help 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},
|
{"dh-param-file", required_argument, &flag, 4},
|
||||||
{"early-response", no_argument, &flag, 5},
|
{"early-response", no_argument, &flag, 5},
|
||||||
{"trailer", required_argument, &flag, 6},
|
{"trailer", required_argument, &flag, 6},
|
||||||
|
{"hexdump", no_argument, &flag, 7},
|
||||||
{nullptr, 0, nullptr, 0}};
|
{nullptr, 0, nullptr, 0}};
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c = getopt_long(argc, argv, "DVb:c:d:ehn:p:va:", long_options,
|
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);
|
util::inp_strlower(config.trailer.back().name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 7:
|
||||||
|
// hexdump option
|
||||||
|
config.hexdump = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
64
src/util.cc
64
src/util.cc
|
@ -1046,6 +1046,70 @@ std::string make_hostport(const char *host, uint16_t port) {
|
||||||
return hostport;
|
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<int>((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<uint8_t, 16> buf{};
|
||||||
|
auto end = src + len;
|
||||||
|
auto i = src;
|
||||||
|
for (;;) {
|
||||||
|
auto nextlen = std::min(static_cast<ptrdiff_t>(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<unsigned long>(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 util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
|
@ -612,6 +612,9 @@ std::string format_duration(const std::chrono::microseconds &u);
|
||||||
// and "]". If |port| is 80 or 443, port part is omitted.
|
// and "]". If |port| is 80 or 443, port part is omitted.
|
||||||
std::string make_hostport(const char *host, uint16_t port);
|
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 util
|
||||||
|
|
||||||
} // namespace nghttp2
|
} // namespace nghttp2
|
||||||
|
|
Loading…
Reference in New Issue