inspector: perform DNS lookup for host

PR-URL: https://github.com/nodejs/node/pull/13478
Fixes: https://github.com/nodejs/node/issues/13477
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit is contained in:
Eugene Ostroukhov 2017-06-05 10:17:20 -07:00
parent b7473c2117
commit 6e2c29bcab
7 changed files with 345 additions and 165 deletions

View File

@ -702,13 +702,7 @@ void Url(const FunctionCallbackInfo<Value>& args) {
if (ids.empty()) return;
std::string url = "ws://";
url += io->host();
url += ":";
url += std::to_string(io->port());
url += "/";
url += ids[0];
std::string url = FormatWsAddress(io->host(), io->port(), ids[0], true);
args.GetReturnValue().Set(OneByteString(env->isolate(), url.c_str()));
}

View File

@ -313,7 +313,7 @@ void InspectorIo::ThreadMain() {
uv_sem_post(&thread_start_sem_);
return;
}
port_ = server.port(); // Safe, main thread is waiting on semaphore.
port_ = server.Port(); // Safe, main thread is waiting on semaphore.
if (!wait_for_connect_) {
uv_sem_post(&thread_start_sem_);
}

View File

@ -28,6 +28,10 @@ class StringView;
namespace node {
namespace inspector {
std::string FormatWsAddress(const std::string& host, int port,
const std::string& target_id,
bool include_protocol);
class InspectorIoDelegate;
enum class InspectorAction {

View File

@ -63,6 +63,7 @@ class InspectorSocket {
bool ws_mode;
bool shutting_down;
bool connection_eof;
private:
DISALLOW_COPY_AND_ASSIGN(InspectorSocket);
};

View File

@ -12,6 +12,28 @@
namespace node {
namespace inspector {
// Function is declared in inspector_io.h so the rest of the node does not
// depend on inspector_socket_server.h
std::string FormatWsAddress(const std::string& host, int port,
const std::string& target_id,
bool include_protocol) {
// Host is valid (socket was bound) so colon means it's a v6 IP address
bool v6 = host.find(':') != std::string::npos;
std::ostringstream url;
if (include_protocol)
url << "ws://";
if (v6) {
url << '[';
}
url << host;
if (v6) {
url << ']';
}
url << ':' << port << '/' << target_id;
return url.str();
}
namespace {
static const uint8_t PROTOCOL_JSON[] = {
@ -24,12 +46,6 @@ void Escape(std::string* string) {
}
}
std::string GetWsUrl(const std::string& host, int port, const std::string& id) {
char buf[1024];
snprintf(buf, sizeof(buf), "%s:%d/%s", host.c_str(), port, id.c_str());
return buf;
}
std::string MapToString(const std::map<std::string, std::string>& object) {
bool first = true;
std::ostringstream json;
@ -82,8 +98,8 @@ void PrintDebuggerReadyMessage(const std::string& host,
return;
}
for (const std::string& id : ids) {
fprintf(out, "Debugger listening on ws://%s\n",
GetWsUrl(host, port, id).c_str());
fprintf(out, "Debugger listening on %s\n",
FormatWsAddress(host, port, id, true).c_str());
}
fprintf(out, "For help see %s\n",
"https://nodejs.org/en/docs/inspector");
@ -151,24 +167,6 @@ int GetSocketHost(uv_tcp_t* socket, std::string* out_host) {
*out_host = ip;
return err;
}
int GetPort(uv_tcp_t* socket, int* out_port) {
sockaddr_storage addr;
int len = sizeof(addr);
int err = uv_tcp_getsockname(socket,
reinterpret_cast<struct sockaddr*>(&addr),
&len);
if (err != 0)
return err;
int port;
if (addr.ss_family == AF_INET6)
port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
else
port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
*out_port = ntohs(port);
return err;
}
} // namespace
@ -211,34 +209,77 @@ class Closer {
class SocketSession {
public:
SocketSession(InspectorSocketServer* server, int id);
static int Accept(InspectorSocketServer* server, int server_port,
uv_stream_t* server_socket);
void Send(const std::string& message);
void Close();
void Declined() { state_ = State::kDeclined; }
int id() const { return id_; }
bool IsForTarget(const std::string& target_id) const {
return target_id_ == target_id;
}
static int ServerPortForClient(InspectorSocket* client) {
return From(client)->server_port_;
}
private:
SocketSession(InspectorSocketServer* server, int server_port);
static SocketSession* From(InspectorSocket* socket) {
return node::ContainerOf(&SocketSession::socket_, socket);
}
enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
static bool HandshakeCallback(InspectorSocket* socket,
enum inspector_handshake_event state,
const std::string& path);
static void ReadCallback(uv_stream_t* stream, ssize_t read,
const uv_buf_t* buf);
static void CloseCallback(InspectorSocket* socket, int code);
void FrontendConnected();
InspectorSocketServer* GetServer() { return server_; }
int Id() { return id_; }
void Send(const std::string& message);
void SetDeclined() { state_ = State::kDeclined; }
void SetTargetId(const std::string& target_id) {
CHECK(target_id_.empty());
target_id_ = target_id;
}
InspectorSocket* Socket() { return &socket_; }
const std::string TargetId() { return target_id_; }
private:
enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
static void CloseCallback(InspectorSocket* socket, int code);
static void ReadCallback(uv_stream_t* stream, ssize_t read,
const uv_buf_t* buf);
void OnRemoteDataIO(ssize_t read, const uv_buf_t* buf);
const int id_;
InspectorSocket socket_;
InspectorSocketServer* server_;
std::string target_id_;
State state_;
const int server_port_;
};
class ServerSocket {
public:
static int Listen(InspectorSocketServer* inspector_server,
sockaddr* addr, uv_loop_t* loop);
void Close() {
uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_),
SocketClosedCallback);
}
int port() const { return port_; }
private:
explicit ServerSocket(InspectorSocketServer* server)
: tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {}
template<typename UvHandle>
static ServerSocket* FromTcpSocket(UvHandle* socket) {
return node::ContainerOf(&ServerSocket::tcp_socket_,
reinterpret_cast<uv_tcp_t*>(socket));
}
static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
static void SocketClosedCallback(uv_handle_t* tcp_socket);
static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
delete FromTcpSocket(tcp_socket_);
}
int DetectPort();
uv_tcp_t tcp_socket_;
InspectorSocketServer* server_;
int port_;
};
InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
@ -249,58 +290,29 @@ InspectorSocketServer::InspectorSocketServer(SocketServerDelegate* delegate,
delegate_(delegate),
host_(host),
port_(port),
server_(uv_tcp_t()),
closer_(nullptr),
next_session_id_(0),
out_(out) {
state_ = ServerState::kNew;
}
// static
bool InspectorSocketServer::HandshakeCallback(InspectorSocket* socket,
inspector_handshake_event event,
const std::string& path) {
InspectorSocketServer* server = SocketSession::From(socket)->GetServer();
const std::string& id = path.empty() ? path : path.substr(1);
switch (event) {
case kInspectorHandshakeHttpGet:
return server->RespondToGet(socket, path);
case kInspectorHandshakeUpgrading:
return server->SessionStarted(SocketSession::From(socket), id);
case kInspectorHandshakeUpgraded:
SocketSession::From(socket)->FrontendConnected();
return true;
case kInspectorHandshakeFailed:
server->SessionTerminated(SocketSession::From(socket));
return false;
default:
UNREACHABLE();
return false;
}
}
bool InspectorSocketServer::SessionStarted(SocketSession* session,
const std::string& id) {
bool connected = false;
if (TargetExists(id)) {
connected = delegate_->StartSession(session->Id(), id);
}
if (connected) {
connected_sessions_[session->Id()] = session;
session->SetTargetId(id);
if (TargetExists(id) && delegate_->StartSession(session->id(), id)) {
connected_sessions_[session->id()] = session;
return true;
} else {
session->Declined();
return false;
}
return connected;
}
void InspectorSocketServer::SessionTerminated(SocketSession* session) {
int id = session->Id();
int id = session->id();
if (connected_sessions_.erase(id) != 0) {
delegate_->EndSession(id);
if (connected_sessions_.empty()) {
if (state_ == ServerState::kRunning) {
PrintDebuggerReadyMessage(host_, port_,
if (state_ == ServerState::kRunning && !server_sockets_.empty()) {
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
delegate_->GetTargetIds(), out_);
}
if (state_ == ServerState::kStopped) {
@ -311,7 +323,7 @@ void InspectorSocketServer::SessionTerminated(SocketSession* session) {
delete session;
}
bool InspectorSocketServer::RespondToGet(InspectorSocket* socket,
bool InspectorSocketServer::HandleGetRequest(InspectorSocket* socket,
const std::string& path) {
const char* command = MatchPathSegment(path.c_str(), "/json");
if (command == nullptr)
@ -354,21 +366,22 @@ void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
bool connected = false;
for (const auto& session : connected_sessions_) {
if (session.second->TargetId() == id) {
if (session.second->IsForTarget(id)) {
connected = true;
break;
}
}
if (!connected) {
std::string host;
int port = SocketSession::ServerPortForClient(socket);
GetSocketHost(&socket->tcp, &host);
std::string address = GetWsUrl(host, port_, id);
std::ostringstream frontend_url;
frontend_url << "chrome-devtools://devtools/bundled";
frontend_url << "/inspector.html?experiments=true&v8only=true&ws=";
frontend_url << address;
frontend_url << FormatWsAddress(host, port, id, false);
target_map["devtoolsFrontendUrl"] += frontend_url.str();
target_map["webSocketDebuggerUrl"] = "ws://" + address;
target_map["webSocketDebuggerUrl"] =
FormatWsAddress(host, port, id, true);
}
}
SendHttpResponse(socket, MapsToString(response));
@ -376,30 +389,44 @@ void InspectorSocketServer::SendListResponse(InspectorSocket* socket) {
bool InspectorSocketServer::Start() {
CHECK_EQ(state_, ServerState::kNew);
sockaddr_in addr;
uv_tcp_init(loop_, &server_);
uv_ip4_addr(host_.c_str(), port_, &addr);
int err = uv_tcp_bind(&server_,
reinterpret_cast<const struct sockaddr*>(&addr), 0);
if (err == 0)
err = GetPort(&server_, &port_);
if (err == 0) {
err = uv_listen(reinterpret_cast<uv_stream_t*>(&server_), 1,
SocketConnectedCallback);
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_NUMERICSERV;
hints.ai_socktype = SOCK_STREAM;
uv_getaddrinfo_t req;
const std::string port_string = std::to_string(port_);
int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
port_string.c_str(), &hints);
if (err < 0) {
if (out_ != NULL) {
fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
uv_strerror(err));
}
if (err == 0 && connected_sessions_.empty()) {
state_ = ServerState::kRunning;
PrintDebuggerReadyMessage(host_, port_, delegate_->GetTargetIds(), out_);
return false;
}
if (err != 0 && connected_sessions_.empty()) {
for (addrinfo* address = req.addrinfo; address != nullptr;
address = address->ai_next) {
err = ServerSocket::Listen(this, address->ai_addr, loop_);
}
uv_freeaddrinfo(req.addrinfo);
if (!connected_sessions_.empty()) {
return true;
}
// We only show error if we failed to start server on all addresses. We only
// show one error, for the last address.
if (server_sockets_.empty()) {
if (out_ != NULL) {
fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
host_.c_str(), port_, uv_strerror(err));
fflush(out_);
}
uv_close(reinterpret_cast<uv_handle_t*>(&server_), nullptr);
return false;
}
state_ = ServerState::kRunning;
// getaddrinfo sorts the addresses, so the first port is most relevant.
PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
delegate_->GetTargetIds(), out_);
return true;
}
@ -411,7 +438,8 @@ void InspectorSocketServer::Stop(ServerCallback cb) {
closer_->AddCallback(cb);
closer_->IncreaseExpectedCount();
state_ = ServerState::kStopping;
uv_close(reinterpret_cast<uv_handle_t*>(&server_), ServerClosedCallback);
for (ServerSocket* server_socket : server_sockets_)
server_socket->Close();
closer_->NotifyIfDone();
}
@ -434,37 +462,41 @@ void InspectorSocketServer::Send(int session_id, const std::string& message) {
}
}
// static
void InspectorSocketServer::ServerClosedCallback(uv_handle_t* server) {
InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
CHECK_EQ(socket_server->state_, ServerState::kStopping);
if (socket_server->closer_) {
socket_server->closer_->DecreaseExpectedCount();
}
if (socket_server->connected_sessions_.empty()) {
socket_server->delegate_->ServerDone();
}
socket_server->state_ = ServerState::kStopped;
void InspectorSocketServer::ServerSocketListening(ServerSocket* server_socket) {
server_sockets_.push_back(server_socket);
}
// static
void InspectorSocketServer::SocketConnectedCallback(uv_stream_t* server,
int status) {
if (status == 0) {
InspectorSocketServer* socket_server = InspectorSocketServer::From(server);
// Memory is freed when the socket closes.
SocketSession* session =
new SocketSession(socket_server, socket_server->next_session_id_++);
if (inspector_accept(server, session->Socket(), HandshakeCallback) != 0) {
delete session;
void InspectorSocketServer::ServerSocketClosed(ServerSocket* server_socket) {
CHECK_EQ(state_, ServerState::kStopping);
server_sockets_.erase(std::remove(server_sockets_.begin(),
server_sockets_.end(), server_socket),
server_sockets_.end());
if (!server_sockets_.empty())
return;
if (closer_ != nullptr) {
closer_->DecreaseExpectedCount();
}
if (connected_sessions_.empty()) {
delegate_->ServerDone();
}
state_ = ServerState::kStopped;
}
int InspectorSocketServer::Port() const {
if (!server_sockets_.empty()) {
return server_sockets_[0]->port();
}
return port_;
}
// InspectorSession tracking
SocketSession::SocketSession(InspectorSocketServer* server, int id)
: id_(id), server_(server),
state_(State::kHttp) { }
SocketSession::SocketSession(InspectorSocketServer* server, int server_port)
: id_(server->GenerateSessionId()),
server_(server),
state_(State::kHttp),
server_port_(server_port) { }
void SocketSession::Close() {
CHECK_NE(state_, State::kClosing);
@ -472,6 +504,49 @@ void SocketSession::Close() {
inspector_close(&socket_, CloseCallback);
}
// static
int SocketSession::Accept(InspectorSocketServer* server, int server_port,
uv_stream_t* server_socket) {
// Memory is freed when the socket closes.
SocketSession* session = new SocketSession(server, server_port);
int err = inspector_accept(server_socket, &session->socket_,
HandshakeCallback);
if (err != 0) {
delete session;
}
return err;
}
// static
bool SocketSession::HandshakeCallback(InspectorSocket* socket,
inspector_handshake_event event,
const std::string& path) {
SocketSession* session = SocketSession::From(socket);
InspectorSocketServer* server = session->server_;
const std::string& id = path.empty() ? path : path.substr(1);
switch (event) {
case kInspectorHandshakeHttpGet:
return server->HandleGetRequest(socket, path);
case kInspectorHandshakeUpgrading:
if (server->SessionStarted(session, id)) {
session->SetTargetId(id);
return true;
} else {
session->SetDeclined();
return false;
}
case kInspectorHandshakeUpgraded:
session->FrontendConnected();
return true;
case kInspectorHandshakeFailed:
server->SessionTerminated(session);
return false;
default:
UNREACHABLE();
return false;
}
}
// static
void SocketSession::CloseCallback(InspectorSocket* socket, int code) {
SocketSession* session = SocketSession::From(socket);
@ -489,14 +564,12 @@ void SocketSession::FrontendConnected() {
void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
const uv_buf_t* buf) {
InspectorSocket* socket = inspector_from_stream(stream);
SocketSession::From(socket)->OnRemoteDataIO(read, buf);
}
void SocketSession::OnRemoteDataIO(ssize_t read, const uv_buf_t* buf) {
SocketSession* session = SocketSession::From(socket);
if (read > 0) {
server_->Delegate()->MessageReceived(id_, std::string(buf->base, read));
session->server_->MessageReceived(session->id_,
std::string(buf->base, read));
} else {
Close();
session->Close();
}
if (buf != nullptr && buf->base != nullptr)
delete[] buf->base;
@ -506,5 +579,62 @@ void SocketSession::Send(const std::string& message) {
inspector_write(&socket_, message.data(), message.length());
}
// ServerSocket implementation
int ServerSocket::DetectPort() {
sockaddr_storage addr;
int len = sizeof(addr);
int err = uv_tcp_getsockname(&tcp_socket_,
reinterpret_cast<struct sockaddr*>(&addr), &len);
if (err != 0)
return err;
int port;
if (addr.ss_family == AF_INET6)
port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
else
port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
port_ = ntohs(port);
return err;
}
// static
int ServerSocket::Listen(InspectorSocketServer* inspector_server,
sockaddr* addr, uv_loop_t* loop) {
ServerSocket* server_socket = new ServerSocket(inspector_server);
uv_tcp_t* server = &server_socket->tcp_socket_;
CHECK_EQ(0, uv_tcp_init(loop, server));
int err = uv_tcp_bind(server, addr, 0);
if (err == 0) {
err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 1,
ServerSocket::SocketConnectedCallback);
}
if (err == 0) {
err = server_socket->DetectPort();
}
if (err == 0) {
inspector_server->ServerSocketListening(server_socket);
} else {
uv_close(reinterpret_cast<uv_handle_t*>(server), FreeOnCloseCallback);
}
return err;
}
// static
void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
int status) {
if (status == 0) {
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
// Memory is freed when the socket closes.
SocketSession::Accept(server_socket->server_, server_socket->port_,
tcp_socket);
}
}
// static
void ServerSocket::SocketClosedCallback(uv_handle_t* tcp_socket) {
ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
server_socket->server_->ServerSocketClosed(server_socket);
delete server_socket;
}
} // namespace inspector
} // namespace node

View File

@ -18,6 +18,7 @@ namespace inspector {
class Closer;
class SocketSession;
class ServerSocket;
class SocketServerDelegate {
public:
@ -54,28 +55,27 @@ class InspectorSocketServer {
// kKill
void TerminateConnections();
int port() {
return port_;
int Port() const;
// Server socket lifecycle. There may be multiple sockets
void ServerSocketListening(ServerSocket* server_socket);
void ServerSocketClosed(ServerSocket* server_socket);
// Session connection lifecycle
bool HandleGetRequest(InspectorSocket* socket, const std::string& path);
bool SessionStarted(SocketSession* session, const std::string& id);
void SessionTerminated(SocketSession* session);
void MessageReceived(int session_id, const std::string& message) {
delegate_->MessageReceived(session_id, message);
}
int GenerateSessionId() {
return next_session_id_++;
}
private:
static bool HandshakeCallback(InspectorSocket* socket,
enum inspector_handshake_event state,
const std::string& path);
static void SocketConnectedCallback(uv_stream_t* server, int status);
static void ServerClosedCallback(uv_handle_t* server);
template<typename SomeUvStruct>
static InspectorSocketServer* From(SomeUvStruct* server) {
return node::ContainerOf(&InspectorSocketServer::server_,
reinterpret_cast<uv_tcp_t*>(server));
}
bool RespondToGet(InspectorSocket* socket, const std::string& path);
void SendListResponse(InspectorSocket* socket);
void ReadCallback(InspectorSocket* socket, ssize_t read, const uv_buf_t* buf);
bool SessionStarted(SocketSession* session, const std::string& id);
void SessionTerminated(SocketSession* session);
bool TargetExists(const std::string& id);
SocketServerDelegate* Delegate() { return delegate_; }
enum class ServerState {kNew, kRunning, kStopping, kStopped};
uv_loop_t* loop_;
@ -83,14 +83,13 @@ class InspectorSocketServer {
const std::string host_;
int port_;
std::string path_;
uv_tcp_t server_;
std::vector<ServerSocket*> server_sockets_;
Closer* closer_;
std::map<int, SocketSession*> connected_sessions_;
int next_session_id_;
FILE* out_;
ServerState state_;
friend class SocketSession;
friend class Closer;
};

View File

@ -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); \
} \
ASSERT_FALSE((condition)); \
}
@ -41,6 +41,7 @@ class Timeout {
explicit Timeout(uv_loop_t* loop) : timed_out(false), done_(false) {
uv_timer_init(loop, &timer_);
uv_timer_start(&timer_, Timeout::set_flag, 5000, 0);
uv_unref(reinterpret_cast<uv_handle_t*>(&timer_));
}
~Timeout() {
@ -163,18 +164,20 @@ class SocketWrapper {
connected_(false),
sending_(false) { }
void Connect(std::string host, int port) {
void Connect(std::string host, int port, bool v6 = false) {
closed_ = false;
connection_failed_ = false;
connected_ = false;
eof_ = false;
contents_.clear();
uv_tcp_init(loop_, &socket_);
sockaddr_in addr;
uv_ip4_addr(host.c_str(), port, &addr);
int err = uv_tcp_connect(&connect_, &socket_,
reinterpret_cast<const sockaddr*>(&addr),
Connected_);
union {sockaddr generic; sockaddr_in v4; sockaddr_in6 v6;} addr;
if (v6) {
uv_ip6_addr(host.c_str(), port, &addr.v6);
} else {
uv_ip4_addr(host.c_str(), port, &addr.v4);
}
int err = uv_tcp_connect(&connect_, &socket_, &addr.generic, Connected_);
ASSERT_EQ(0, err);
SPIN_WHILE(!connected_)
uv_read_start(reinterpret_cast<uv_stream_t*>(&socket_), AllocCallback,
@ -306,9 +309,14 @@ class SocketWrapper {
class ServerHolder {
public:
template <typename Delegate>
ServerHolder(Delegate* delegate, uv_loop_t* loop, int port, FILE* out = NULL)
ServerHolder(Delegate* delegate, uv_loop_t* loop, int port)
: ServerHolder(delegate, loop, HOST, port, NULL) { }
template <typename Delegate>
ServerHolder(Delegate* delegate, uv_loop_t* loop, const std::string host,
int port, FILE* out)
: closed(false), paused(false),
server_(delegate, loop, HOST, port, out) {
server_(delegate, loop, host, port, out) {
delegate->Connect(&server_);
}
@ -317,7 +325,7 @@ class ServerHolder {
}
int port() {
return server_.port();
return server_.Port();
}
static void CloseCallback(InspectorSocketServer* server) {
@ -575,3 +583,47 @@ TEST_F(InspectorSocketServerTest, TerminatingSessionReportsDone) {
socket1.ExpectEOF();
SPIN_WHILE(!delegate.done);
}
TEST_F(InspectorSocketServerTest, FailsToBindToNodejsHost) {
TestInspectorServerDelegate delegate;
ServerHolder server(&delegate, &loop, "nodejs.org", 0, nullptr);
ASSERT_FALSE(server->Start());
SPIN_WHILE(uv_loop_alive(&loop));
}
bool has_ipv6_address() {
uv_interface_address_s* addresses = nullptr;
int address_count = 0;
int err = uv_interface_addresses(&addresses, &address_count);
if (err != 0) {
return false;
}
bool has_address = false;
for (int i = 0; i < address_count; i++) {
if (addresses[i].address.address6.sin6_family == AF_INET6) {
has_address = true;
}
}
uv_free_interface_addresses(addresses, address_count);
return has_address;
}
TEST_F(InspectorSocketServerTest, BindsToIpV6) {
if (!has_ipv6_address()) {
fprintf(stderr, "No IPv6 network detected\n");
return;
}
TestInspectorServerDelegate delegate;
ServerHolder server(&delegate, &loop, "::", 0, NULL);
ASSERT_TRUE(server->Start());
SocketWrapper socket1(&loop);
socket1.Connect("[::]", server.port(), true);
socket1.Write(WsHandshakeRequest(MAIN_TARGET_ID));
socket1.Expect(WS_HANDSHAKE_RESPONSE);
server->Stop(ServerHolder::CloseCallback);
SPIN_WHILE(!server.closed);
ASSERT_FALSE(delegate.done);
socket1.Close();
SPIN_WHILE(!delegate.done);
}