report: add fallback for uv_getnameinfo() failures
Attempt to report the host and port in the case that uv_getnameinfo() fails. PR-URL: https://github.com/nodejs/node/pull/26140 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
parent
d64e4deb73
commit
fc4c0de92f
@ -7,6 +7,45 @@ using node::MallocedBuffer;
|
|||||||
|
|
||||||
static constexpr auto null = JSONWriter::Null{};
|
static constexpr auto null = JSONWriter::Null{};
|
||||||
|
|
||||||
|
// Utility function to format socket information.
|
||||||
|
static void ReportEndpoint(uv_handle_t* h,
|
||||||
|
struct sockaddr* addr,
|
||||||
|
const char* name,
|
||||||
|
JSONWriter* writer) {
|
||||||
|
if (addr == nullptr) {
|
||||||
|
writer->json_keyvalue(name, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uv_getnameinfo_t endpoint;
|
||||||
|
char* host = nullptr;
|
||||||
|
char hostbuf[INET6_ADDRSTRLEN];
|
||||||
|
const int family = addr->sa_family;
|
||||||
|
const int port = ntohs(family == AF_INET ?
|
||||||
|
reinterpret_cast<sockaddr_in*>(addr)->sin_port :
|
||||||
|
reinterpret_cast<sockaddr_in6*>(addr)->sin6_port);
|
||||||
|
|
||||||
|
if (uv_getnameinfo(h->loop, &endpoint, nullptr, addr, NI_NUMERICSERV) == 0) {
|
||||||
|
host = endpoint.host;
|
||||||
|
DCHECK_EQ(port, std::stoi(endpoint.service));
|
||||||
|
} else {
|
||||||
|
const void* src = family == AF_INET ?
|
||||||
|
static_cast<void*>(
|
||||||
|
&(reinterpret_cast<sockaddr_in*>(addr)->sin_addr)) :
|
||||||
|
static_cast<void*>(
|
||||||
|
&(reinterpret_cast<sockaddr_in6*>(addr)->sin6_addr));
|
||||||
|
if (uv_inet_ntop(family, src, hostbuf, sizeof(hostbuf)) == 0) {
|
||||||
|
host = hostbuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer->json_objectstart(name);
|
||||||
|
if (host != nullptr) {
|
||||||
|
writer->json_keyvalue("host", host);
|
||||||
|
}
|
||||||
|
writer->json_keyvalue("port", port);
|
||||||
|
writer->json_objectend();
|
||||||
|
}
|
||||||
|
|
||||||
// Utility function to format libuv socket information.
|
// Utility function to format libuv socket information.
|
||||||
static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
|
static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
|
||||||
struct sockaddr_storage addr_storage;
|
struct sockaddr_storage addr_storage;
|
||||||
@ -14,8 +53,6 @@ static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
|
|||||||
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
|
uv_any_handle* handle = reinterpret_cast<uv_any_handle*>(h);
|
||||||
int addr_size = sizeof(addr_storage);
|
int addr_size = sizeof(addr_storage);
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
bool wrote_local_endpoint = false;
|
|
||||||
bool wrote_remote_endpoint = false;
|
|
||||||
|
|
||||||
switch (h->type) {
|
switch (h->type) {
|
||||||
case UV_UDP:
|
case UV_UDP:
|
||||||
@ -27,38 +64,13 @@ static void ReportEndpoints(uv_handle_t* h, JSONWriter* writer) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (rc == 0) {
|
ReportEndpoint(h, rc == 0 ? addr : nullptr, "localEndpoint", writer);
|
||||||
// uv_getnameinfo will format host and port and handle IPv4/IPv6.
|
|
||||||
uv_getnameinfo_t local;
|
|
||||||
rc = uv_getnameinfo(h->loop, &local, nullptr, addr, NI_NUMERICSERV);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
writer->json_objectstart("localEndpoint");
|
|
||||||
writer->json_keyvalue("host", local.host);
|
|
||||||
writer->json_keyvalue("port", local.service);
|
|
||||||
writer->json_objectend();
|
|
||||||
wrote_local_endpoint = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!wrote_local_endpoint) writer->json_keyvalue("localEndpoint", null);
|
|
||||||
|
|
||||||
if (h->type == UV_TCP) {
|
if (h->type == UV_TCP) {
|
||||||
// Get the remote end of the connection.
|
// Get the remote end of the connection.
|
||||||
rc = uv_tcp_getpeername(&handle->tcp, addr, &addr_size);
|
rc = uv_tcp_getpeername(&handle->tcp, addr, &addr_size);
|
||||||
if (rc == 0) {
|
ReportEndpoint(h, rc == 0 ? addr : nullptr, "remoteEndpoint", writer);
|
||||||
uv_getnameinfo_t remote;
|
|
||||||
rc = uv_getnameinfo(h->loop, &remote, nullptr, addr, NI_NUMERICSERV);
|
|
||||||
|
|
||||||
if (rc == 0) {
|
|
||||||
writer->json_objectstart("remoteEndpoint");
|
|
||||||
writer->json_keyvalue("host", remote.host);
|
|
||||||
writer->json_keyvalue("port", remote.service);
|
|
||||||
writer->json_objectend();
|
|
||||||
wrote_local_endpoint = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!wrote_remote_endpoint) writer->json_keyvalue("remoteEndpoint", null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function to format libuv path information.
|
// Utility function to format libuv path information.
|
||||||
|
@ -60,9 +60,7 @@ if (process.argv[2] === 'child') {
|
|||||||
const data = { pid: child_process.pid,
|
const data = { pid: child_process.pid,
|
||||||
tcp_address: server.address(),
|
tcp_address: server.address(),
|
||||||
udp_address: udp_socket.address(),
|
udp_address: udp_socket.address(),
|
||||||
skip_fs_watch: (watcher === undefined ?
|
skip_fs_watch: (watcher === undefined) };
|
||||||
'fs.watch() unavailable' :
|
|
||||||
false) };
|
|
||||||
process.send(data);
|
process.send(data);
|
||||||
http.get({ port: server.address().port });
|
http.get({ port: server.address().port });
|
||||||
});
|
});
|
||||||
@ -74,6 +72,8 @@ if (process.argv[2] === 'child') {
|
|||||||
tmpdir.refresh();
|
tmpdir.refresh();
|
||||||
const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path };
|
const options = { encoding: 'utf8', silent: true, cwd: tmpdir.path };
|
||||||
const child = fork('--experimental-report', [__filename, 'child'], options);
|
const child = fork('--experimental-report', [__filename, 'child'], options);
|
||||||
|
let child_data;
|
||||||
|
child.on('message', (data) => { child_data = data; });
|
||||||
let stderr = '';
|
let stderr = '';
|
||||||
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
||||||
let stdout = '';
|
let stdout = '';
|
||||||
@ -94,36 +94,61 @@ if (process.argv[2] === 'child') {
|
|||||||
assert.deepStrictEqual(reports, [], report_msg, reports);
|
assert.deepStrictEqual(reports, [], report_msg, reports);
|
||||||
|
|
||||||
const report = JSON.parse(stdout);
|
const report = JSON.parse(stdout);
|
||||||
let fs = 0;
|
const prefix = common.isWindows ? '\\\\?\\' : '';
|
||||||
let poll = 0;
|
const expected_filename = `${prefix}${__filename}`;
|
||||||
let process = 0;
|
const found_tcp = [];
|
||||||
let timer = 0;
|
// Functions are named to aid debugging when they are not called.
|
||||||
let pipe = 0;
|
const validators = {
|
||||||
let tcp = 0;
|
fs_event: common.mustCall(function fs_event_validator(handle) {
|
||||||
let udp = 0;
|
if (!child_data.skip_fs_watch) {
|
||||||
const fs_msg = 'fs_event not found';
|
assert.strictEqual(handle.filename, expected_filename);
|
||||||
const poll_msg = 'poll_event not found';
|
assert(handle.is_referenced);
|
||||||
const process_msg = 'process event not found';
|
}
|
||||||
const timer_msg = 'timer event not found';
|
}),
|
||||||
const pipe_msg = 'pipe event not found';
|
fs_poll: common.mustCall(function fs_poll_validator(handle) {
|
||||||
const tcp_msg = 'tcp event not found';
|
assert.strictEqual(handle.filename, expected_filename);
|
||||||
const udp_msg = 'udp event not found';
|
assert(handle.is_referenced);
|
||||||
for (const entry in report.libuv) {
|
}),
|
||||||
if (report.libuv[entry].type === 'fs_event') fs = 1;
|
pipe: common.mustCallAtLeast(function pipe_validator(handle) {
|
||||||
else if (report.libuv[entry].type === 'fs_poll') poll = 1;
|
assert(handle.is_referenced);
|
||||||
else if (report.libuv[entry].type === 'process') process = 1;
|
}),
|
||||||
else if (report.libuv[entry].type === 'timer') timer = 1;
|
process: common.mustCall(function process_validator(handle) {
|
||||||
else if (report.libuv[entry].type === 'pipe') pipe = 1;
|
assert.strictEqual(handle.pid, child_data.pid);
|
||||||
else if (report.libuv[entry].type === 'tcp') tcp = 1;
|
assert(handle.is_referenced);
|
||||||
else if (report.libuv[entry].type === 'udp') udp = 1;
|
}),
|
||||||
|
tcp: common.mustCall(function tcp_validator(handle) {
|
||||||
|
// TCP handles. The report should contain three sockets:
|
||||||
|
// 1. The server's listening socket.
|
||||||
|
// 2. The inbound socket making the request.
|
||||||
|
// 3. The outbound socket sending the response.
|
||||||
|
const port = child_data.tcp_address.port;
|
||||||
|
if (handle.localEndpoint.port === port) {
|
||||||
|
if (handle.remoteEndpoint === null) {
|
||||||
|
found_tcp.push('listening');
|
||||||
|
} else {
|
||||||
|
found_tcp.push('inbound');
|
||||||
|
}
|
||||||
|
} else if (handle.remoteEndpoint.port === port) {
|
||||||
|
found_tcp.push('outbound');
|
||||||
|
}
|
||||||
|
assert(handle.is_referenced);
|
||||||
|
}, 3),
|
||||||
|
timer: common.mustCall(function timer_validator(handle) {
|
||||||
|
assert(!handle.is_referenced);
|
||||||
|
assert.strictEqual(handle.repeat, 0);
|
||||||
|
}),
|
||||||
|
udp: common.mustCall(function udp_validator(handle) {
|
||||||
|
assert.strictEqual(handle.localEndpoint.port,
|
||||||
|
child_data.udp_address.port);
|
||||||
|
assert(handle.is_referenced);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
for (const entry of report.libuv) {
|
||||||
|
if (validators[entry.type]) validators[entry.type](entry);
|
||||||
|
}
|
||||||
|
for (const socket of ['listening', 'inbound', 'outbound']) {
|
||||||
|
assert(found_tcp.includes(socket), `${socket} TCP socket was not found`);
|
||||||
}
|
}
|
||||||
assert.deepStrictEqual(fs, 1, fs_msg);
|
|
||||||
assert.deepStrictEqual(poll, 1, poll_msg);
|
|
||||||
assert.deepStrictEqual(process, 1, process_msg);
|
|
||||||
assert.deepStrictEqual(timer, 1, timer_msg);
|
|
||||||
assert.deepStrictEqual(pipe, 1, pipe_msg);
|
|
||||||
assert.deepStrictEqual(tcp, 1, tcp_msg);
|
|
||||||
assert.deepStrictEqual(udp, 1, udp_msg);
|
|
||||||
|
|
||||||
// Common report tests.
|
// Common report tests.
|
||||||
helper.validateContent(stdout);
|
helper.validateContent(stdout);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user