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:
parent
b7473c2117
commit
6e2c29bcab
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -63,6 +63,7 @@ class InspectorSocket {
|
||||
bool ws_mode;
|
||||
bool shutting_down;
|
||||
bool connection_eof;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(InspectorSocket);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user