-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
inspector: perform DNS lookup for host #13478
Conversation
Code LGTM, the question is does it make sense? Do people need a DNS lookup? Won't it be simpler to check for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs new tests in test/cctest/test_inspector_socket_server.cc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, but the URL formatting isn't valid, will need to have the [...]
placed around the address so the output URLs are valid:
% ./node --inspect=::1:9876
Debugger listening on ws://::1:9876/6fcffeb2-55bf-438b-b886-582a2d517513
For help see https://nodejs.org/en/docs/inspector
> % core/node ((a392ece...) $) % ./node --inspect=[::1]:9876
Debugger listening on ws://::1:9876/1fa763a7-4ca3-4040-a666-6eb8d3b268f9
src/inspector_socket_server.cc
Outdated
int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(), | ||
std::to_string(port_).c_str(), nullptr); | ||
if (err < 0) { | ||
fprintf(out_, "%s is not a valid host name\n", host_.c_str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a more specific error message that can be reported? Is invalid host name the only reason for getaddrinfo to fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
One stupid thing I noticed was any symbolic name (non-existing, localhost) would always bind to *. With this lookup invalid names (e.g. with typos) will error out, v8.0.0:
(actually listening *:9229) This patch:
|
Yes, definitely works better now. |
Ack. |
@refack Tests added. Will see if they make it through the CI :) |
@sam-github Fixed the URL |
Please, don't review for now - I will upload a different version tomorrow. |
Ready for review. CI: https://ci.nodejs.org/job/node-test-pull-request/8520/ |
@@ -26,7 +26,7 @@ static const char WS_HANDSHAKE_RESPONSE[] = | |||
{ \ | |||
Timeout timeout(&loop); \ | |||
while ((condition) && !timeout.timed_out) { \ | |||
uv_run(&loop, UV_RUN_NOWAIT); \ | |||
uv_run(&loop, UV_RUN_ONCE); \ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this change the current tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not. Simply results in lower number of loops.
src/inspector_socket_server.cc
Outdated
@@ -376,11 +385,25 @@ void InspectorSocketServer::SendListResponse(InspectorSocket* socket) { | |||
|
|||
bool InspectorSocketServer::Start() { | |||
CHECK_EQ(state_, ServerState::kNew); | |||
sockaddr_in addr; | |||
struct addrinfo hints; | |||
memset(&hints, 0, sizeof(struct addrinfo)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sizeof(hints)
or just struct addrinfo hints()
.
I will admit I don't quite understand why you pass the port number if you aren't interested in service name lookups (AI_NUMERICSERV.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, made the code slightly easier - prefills the sockaddr structures.
src/inspector_socket_server.cc
Outdated
uv_ip4_addr(host_.c_str(), port_, &addr); | ||
int err = uv_tcp_bind(&server_, | ||
reinterpret_cast<const struct sockaddr*>(&addr), 0); | ||
err = uv_tcp_bind(&server_, req.addrinfo->ai_addr, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only binds to the first address. Acceptable for now but sooner or later someone is going to file a bug about that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now this class works with a vector of sockets. This should also help if there's ever a need to support listening on multiple ports (e.g. someone mentioned before something about changing the port at the runtime).
One issue this implementation is that different sockets will bind to different ports if requested port is 0 - while only first socket port is surfaced in API and console messages. I did not want to further complicate this implementation at this time, but an API that provides list of addresses and ports can be created later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One issue this implementation is that different sockets will bind to different ports if requested port is 0
That seems reasonable to me.
bool has_ipv6_address() { | ||
uv_interface_address_s* addresses; | ||
int address_count; | ||
int err = uv_interface_addresses(&addresses, &address_count); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs a matching call to uv_free_interface_addresses()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done. I'm bit fuzzy on this - but should uv_free_* be called if respective call (in this case to uv_interface_addresses
) failed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You normally only call it on success but it's safe to call on error if you initialize addresses
and address_count
to nullptr / zero.
(Please do not review for now, fixing) |
I ended up moving more code around then I originally intended. My goal was to better separate responsibilities between server, server socket and session objects. Please review. |
PrintDebuggerReadyMessage(host_, port_, delegate_->GetTargetIds(), out_); | ||
for (addrinfo* address = req.addrinfo; address != nullptr; | ||
address = address->ai_next) { | ||
err = ServerSocket::Listen(this, address->ai_addr, loop_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand this correctly for localhost
we'll probably get ['127.0.0.1', '[::1]'] and try to listen to them all?
But print only the first success?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are not printing out resolved address. Instead localhost:port will be printed. Only one port will be outputted - which is only a problem when port set to 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can live with that
src/inspector_socket_server.cc
Outdated
char buf[1024]; | ||
snprintf(buf, sizeof(buf), "%s:%d/%s", host.c_str(), port, id.c_str()); | ||
return buf; | ||
// Host is valid (socket was bound) so colon means it's a v6 IP address |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we merge this with the code in inspector_agent.cc@node::inspector::URL()
after the if?
/cc @sam-github
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not aware of that code. I will share the code for generating the URL. It will still be slightly different - e.g. in json/list response it echoes IP address the client used to access the server while other places will print the host name specified from the command line (--inspect=0.0.0.0
will respond with 127.0.0.1 or external IP depending on the client but print out 0.0.0.0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's new code (less than a week old)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found it. Moved the function to inspector_io.{h,cc}. Since the _server class does not depend on anything in the Node (so it is easier to test it) I copied a declaration to the inspector_socket_server.cc.
a few questions:
|
Yes.
Don't think so. This should not be user-observable - we never claimed IPv6 was supported (nor did we explicitly call it unsupported). Multiple interfaces is also a bug fix - there is still no way to start listening on different port numbers and such.
IMHO, this change has no observable impact on wire clients. IPv6 on the low level is tested in cctest.
This is likely platform and configuration specific. There may be setups without IPv6 interface (or even without IPv4), localhost may not resolve to all of them, etc. |
I looked at CI failures and they do not seem to be caused by this code as inspector server is not started there. |
Ack (on my machine 'localhost' resolves only to IPv4) |
url += "/"; | ||
url += ids[0]; | ||
|
||
std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Solves the IPv6 URL bug 👍
src/inspector_socket_server.cc
Outdated
@@ -12,6 +12,10 @@ | |||
namespace node { | |||
namespace inspector { | |||
|
|||
std::string FormatWsAddress(const std::string& host, int port, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Circular dependency thing?
You should put a comment...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment.
DNS lookup aside, the entire host parsing process looks fairly complicated. I understand it would be a separate venture from this PR, but is there any value in sharing the WHATWG URL parser's host parser or host state (includes port parsing), which distinguishes between domains, IPv4, and IPv6 addresses in a standard-compliant manner? |
@TimothyGu Thank you. I will look into it - but not for this PR (there's no address parsing/validation in this PR - it simply passes the string to DNS). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
Thank you for the reviews. New CI: https://ci.nodejs.org/job/node-test-pull-request/8694/ |
src/inspector_socket_server.cc
Outdated
|
||
private: | ||
explicit ServerSocket(InspectorSocketServer* server) | ||
: tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just 4-space indent relative to the previous line is enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
CI have not indicated any relevant failures. Landed as 6e2c29b |
I think it's a good fix, but I don't yet trust our test suite coverage, can we let it cook for a while before porting to |
I don't think it is pressing enough. I added labels - can remove (or manually backport) if need be. |
@refack I would not let it wait. For one, to some degree having the latest bugfixes and features is what Current releases are there for; and then, as I’ve mentioned before it would likely be a good idea to do a RC process for 8.2.0, because that’s going to come with V8 5.9 and we’ve handled V8 upgrades the same way in the past. If you feel really strongly, you can re-add the labels, but for now I removed them again. (edit: Also, I would apply dont-land-on-v6.x only if it really shouldn’t land; otherwise we have baking-for-lts for that) |
Ack. Ping @jkrems, @joshgav, @roblourens, @billti, @prigara if you have external tests you could run on tomorrow's nightly, that would be great. |
@refack We'll do a test pass with the Node Tools for Visual Studio over the weekend and I'll get back to you on Monday. FYI, I am setting up nightly automation runs on the latest nightly node builds that will give us early warning when we're broken. |
@mbraude that is great! |
Thanks for the ping! Ran |
@refack Thanks, it doesn't break IntelliJ. |
Could this be backported? v6.x inspector is pretty far behind |
Fixes: #13477
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
inspector: performs a sync DNS lookup when the server is starting.