18 const std::string& target_id,
19 bool include_protocol) {
21 bool v6 = host.find(
':') != std::string::npos;
22 std::ostringstream url;
32 url <<
':' << port <<
'/' << target_id;
39 static const uint8_t PROTOCOL_JSON[] = {
40 #include "v8_inspector_protocol_json.h" 43 void Escape(std::string*
string) {
44 for (
char& c : *
string) {
45 c = (c ==
'\"' || c ==
'\\') ?
'_' : c;
49 std::string MapToString(
const std::map<std::string, std::string>&
object) {
51 std::ostringstream json;
53 for (
const auto& name_value :
object) {
57 json <<
" \"" << name_value.first <<
"\": \"";
58 json << name_value.second <<
"\"";
64 std::string MapsToString(
65 const std::vector<std::map<std::string, std::string>>& array) {
67 std::ostringstream json;
69 for (
const auto&
object : array) {
73 json << MapToString(
object);
79 const char* MatchPathSegment(
const char* path,
const char* expected) {
80 size_t len = strlen(expected);
81 if (StringEqualNoCaseN(path, expected, len)) {
82 if (path[len] ==
'/')
return path + len + 1;
83 if (path[len] ==
'\0')
return path +
len;
88 void OnBufferAlloc(uv_handle_t* handle,
size_t len, uv_buf_t*
buf) {
89 buf->base =
new char[
len];
93 void PrintDebuggerReadyMessage(
const std::string&
host,
95 const std::vector<std::string>& ids,
100 for (
const std::string&
id : ids) {
101 fprintf(out,
"Debugger listening on %s\n",
104 fprintf(out,
"For help see %s\n",
105 "https://nodejs.org/en/docs/inspector");
109 void SendHttpResponse(
InspectorSocket* socket,
const std::string& response) {
110 const char HEADERS[] =
"HTTP/1.0 200 OK\r\n" 111 "Content-Type: application/json; charset=UTF-8\r\n" 112 "Cache-Control: no-cache\r\n" 113 "Content-Length: %zu\r\n" 115 char header[
sizeof(HEADERS) + 20];
116 int header_len = snprintf(header,
sizeof(header), HEADERS, response.size());
122 std::map<std::string, std::string> response;
124 response[
"Protocol-Version"] =
"1.1";
125 SendHttpResponse(socket, MapToString(response));
130 strm.zalloc = Z_NULL;
132 strm.opaque = Z_NULL;
133 CHECK_EQ(Z_OK, inflateInit(&strm));
134 static const size_t kDecompressedSize =
135 PROTOCOL_JSON[0] * 0x10000u +
136 PROTOCOL_JSON[1] * 0x100u +
138 strm.next_in =
const_cast<uint8_t*
>(PROTOCOL_JSON + 3);
139 strm.avail_in =
sizeof(PROTOCOL_JSON) - 3;
140 std::string
data(kDecompressedSize,
'\0');
141 strm.next_out =
reinterpret_cast<Byte*
>(&data[0]);
142 strm.avail_out = data.size();
143 CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
144 CHECK_EQ(0, strm.avail_out);
145 CHECK_EQ(Z_OK, inflateEnd(&strm));
146 SendHttpResponse(socket, data);
149 int GetSocketHost(uv_tcp_t* socket, std::string* out_host) {
150 char ip[INET6_ADDRSTRLEN];
151 sockaddr_storage addr;
152 int len =
sizeof(addr);
153 int err = uv_tcp_getsockname(socket,
154 reinterpret_cast<struct sockaddr*>(&addr),
158 if (addr.ss_family == AF_INET6) {
159 const sockaddr_in6* v6 =
reinterpret_cast<const sockaddr_in6*
>(&addr);
160 err = uv_ip6_name(v6, ip,
sizeof(ip));
162 const sockaddr_in* v4 =
reinterpret_cast<const sockaddr_in*
>(&addr);
163 err = uv_ip4_name(v4, ip,
sizeof(ip));
179 if (callback ==
nullptr)
181 callbacks_.insert(callback);
194 if (close_count_ == 0) {
195 for (
auto callback : callbacks_) {
199 delete server->closer_;
200 server->closer_ =
nullptr;
206 std::set<InspectorSocketServer::ServerCallback> callbacks_;
213 uv_stream_t* server_socket);
214 void Send(
const std::string& message);
217 int id()
const {
return id_; }
219 return target_id_ == target_id;
222 return From(client)->server_port_;
228 return node::ContainerOf(&SocketSession::socket_, socket);
231 enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
234 const std::string& path);
235 static void ReadCallback(uv_stream_t* stream, ssize_t read,
236 const uv_buf_t*
buf);
239 void FrontendConnected();
240 void SetDeclined() { state_ = State::kDeclined; }
241 void SetTargetId(
const std::string& target_id) {
242 CHECK(target_id_.empty());
243 target_id_ = target_id;
249 std::string target_id_;
251 const int server_port_;
257 sockaddr* addr, uv_loop_t* loop);
259 uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_),
260 SocketClosedCallback);
262 int port()
const {
return port_; }
266 : tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {}
267 template<
typename UvHandle>
269 return node::ContainerOf(&ServerSocket::tcp_socket_,
270 reinterpret_cast<uv_tcp_t*>(socket));
273 static void SocketConnectedCallback(uv_stream_t* tcp_socket,
int status);
274 static void SocketClosedCallback(uv_handle_t* tcp_socket);
275 static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
276 delete FromTcpSocket(tcp_socket_);
280 uv_tcp_t tcp_socket_;
287 const std::string&
host,
289 FILE* out) : loop_(loop),
296 state_ = ServerState::kNew;
300 const std::string&
id) {
301 if (TargetExists(
id) && delegate_->
StartSession(session->
id(), id)) {
302 connected_sessions_[session->
id()] = session;
310 int id = session->
id();
311 if (connected_sessions_.erase(
id) != 0) {
313 if (connected_sessions_.empty()) {
314 if (state_ == ServerState::kRunning && !server_sockets_.empty()) {
315 PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
318 if (state_ == ServerState::kStopped) {
327 const std::string& path) {
328 const char* command = MatchPathSegment(path.c_str(),
"/json");
329 if (command ==
nullptr)
332 if (MatchPathSegment(command,
"list") || command[0] ==
'\0') {
333 SendListResponse(socket);
335 }
else if (MatchPathSegment(command,
"protocol")) {
336 SendProtocolJson(socket);
338 }
else if (MatchPathSegment(command,
"version")) {
339 SendVersionResponse(socket);
341 }
else if (
const char* target_id = MatchPathSegment(command,
"activate")) {
342 if (TargetExists(target_id)) {
343 SendHttpResponse(socket,
"Target activated");
351 void InspectorSocketServer::SendListResponse(
InspectorSocket* socket) {
352 std::vector<std::map<std::string, std::string>> response;
353 for (
const std::string&
id : delegate_->
GetTargetIds()) {
354 response.push_back(std::map<std::string, std::string>());
355 std::map<std::string, std::string>& target_map = response.back();
356 target_map[
"description"] =
"node.js instance";
357 target_map[
"faviconUrl"] =
"https://nodejs.org/static/favicon.ico";
358 target_map[
"id"] = id;
360 Escape(&target_map[
"title"]);
361 target_map[
"type"] =
"node";
365 Escape(&target_map[
"url"]);
367 bool connected =
false;
368 for (
const auto& session : connected_sessions_) {
369 if (session.second->IsForTarget(
id)) {
377 GetSocketHost(&socket->
tcp, &host);
378 std::ostringstream frontend_url;
379 frontend_url <<
"chrome-devtools://devtools/bundled";
380 frontend_url <<
"/inspector.html?experiments=true&v8only=true&ws=";
382 target_map[
"devtoolsFrontendUrl"] += frontend_url.str();
383 target_map[
"webSocketDebuggerUrl"] =
387 SendHttpResponse(socket, MapsToString(response));
391 CHECK_EQ(state_, ServerState::kNew);
392 struct addrinfo hints;
393 memset(&hints, 0,
sizeof(hints));
394 hints.ai_flags = AI_NUMERICSERV;
395 hints.ai_socktype = SOCK_STREAM;
396 uv_getaddrinfo_t
req;
397 const std::string port_string = std::to_string(port_);
398 int err = uv_getaddrinfo(loop_, &req,
nullptr, host_.c_str(),
399 port_string.c_str(), &hints);
402 fprintf(out_,
"Unable to resolve \"%s\": %s\n", host_.c_str(),
407 for (addrinfo* address = req.addrinfo; address !=
nullptr;
408 address = address->ai_next) {
411 uv_freeaddrinfo(req.addrinfo);
413 if (!connected_sessions_.empty()) {
418 if (server_sockets_.empty()) {
420 fprintf(out_,
"Starting inspector on %s:%d failed: %s\n",
421 host_.c_str(), port_, uv_strerror(err));
426 state_ = ServerState::kRunning;
428 PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
434 CHECK_EQ(state_, ServerState::kRunning);
435 if (closer_ ==
nullptr) {
436 closer_ =
new Closer(
this);
440 state_ = ServerState::kStopping;
442 server_socket->Close();
447 for (
const auto& session : connected_sessions_) {
448 session.second->Close();
452 bool InspectorSocketServer::TargetExists(
const std::string&
id) {
453 const std::vector<std::string>& target_ids = delegate_->
GetTargetIds();
454 const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
455 return found != target_ids.end();
459 auto session_iterator = connected_sessions_.find(session_id);
460 if (session_iterator != connected_sessions_.end()) {
461 session_iterator->second->Send(message);
466 server_sockets_.push_back(server_socket);
470 CHECK_EQ(state_, ServerState::kStopping);
472 server_sockets_.erase(std::remove(server_sockets_.begin(),
473 server_sockets_.end(), server_socket),
474 server_sockets_.end());
475 if (!server_sockets_.empty())
478 if (closer_ !=
nullptr) {
481 if (connected_sessions_.empty()) {
484 state_ = ServerState::kStopped;
488 if (!server_sockets_.empty()) {
489 return server_sockets_[0]->port();
498 state_(State::kHttp),
499 server_port_(server_port) { }
502 CHECK_NE(state_, State::kClosing);
503 state_ = State::kClosing;
509 uv_stream_t* server_socket) {
523 const std::string& path) {
526 const std::string&
id = path.empty() ? path : path.substr(1);
532 session->SetTargetId(
id);
535 session->SetDeclined();
539 session->FrontendConnected();
553 CHECK_EQ(State::kClosing, session->state_);
557 void SocketSession::FrontendConnected() {
558 CHECK_EQ(State::kHttp, state_);
559 state_ = State::kWebSocket;
564 void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
565 const uv_buf_t*
buf) {
570 std::string(buf->base, read));
574 if (buf !=
nullptr && buf->base !=
nullptr)
583 int ServerSocket::DetectPort() {
584 sockaddr_storage addr;
585 int len =
sizeof(addr);
586 int err = uv_tcp_getsockname(&tcp_socket_,
587 reinterpret_cast<struct sockaddr*>(&addr), &len);
591 if (addr.ss_family == AF_INET6)
592 port =
reinterpret_cast<const sockaddr_in6*
>(&addr)->sin6_port;
594 port =
reinterpret_cast<const sockaddr_in*
>(&addr)->sin_port;
601 sockaddr* addr, uv_loop_t* loop) {
603 uv_tcp_t* server = &server_socket->tcp_socket_;
604 CHECK_EQ(0, uv_tcp_init(loop, server));
605 int err = uv_tcp_bind(server, addr, 0);
607 err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 1,
608 ServerSocket::SocketConnectedCallback);
611 err = server_socket->DetectPort();
616 uv_close(reinterpret_cast<uv_handle_t*>(server), FreeOnCloseCallback);
622 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
625 ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
633 void ServerSocket::SocketClosedCallback(uv_handle_t* tcp_socket) {
634 ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
636 delete server_socket;
void inspector_close(InspectorSocket *inspector, inspector_cb callback)
static int ServerPortForClient(InspectorSocket *client)
void DecreaseExpectedCount()
bool IsForTarget(const std::string &target_id) const
void Stop(ServerCallback callback)
void Send(const std::string &message)
void inspector_write(InspectorSocket *inspector, const char *data, size_t len)
inspector_handshake_event
union node::cares_wrap::@8::CaresAsyncData::@0 data
virtual bool StartSession(int session_id, const std::string &target_id)=0
void TerminateConnections()
void IncreaseExpectedCount()
InspectorSocket * inspector_from_stream(uv_tcp_t *stream)
void MessageReceived(int session_id, const std::string &message)
std::string FormatWsAddress(const std::string &host, int port, const std::string &target_id, bool include_protocol)
virtual std::vector< std::string > GetTargetIds()=0
void(*)(InspectorSocketServer *) ServerCallback
static int Accept(InspectorSocketServer *server, int server_port, uv_stream_t *server_socket)
int inspector_accept(uv_stream_t *server, InspectorSocket *socket, handshake_cb callback)
InspectorSocketServer(SocketServerDelegate *delegate, uv_loop_t *loop, const std::string &host, int port, FILE *out=stderr)
virtual void ServerDone()=0
bool SessionStarted(SocketSession *session, const std::string &id)
virtual std::string GetTargetTitle(const std::string &id)=0
virtual std::string GetTargetUrl(const std::string &id)=0
virtual void EndSession(int session_id)=0
int inspector_read_start(InspectorSocket *inspector, uv_alloc_cb alloc_cb, uv_read_cb read_cb)
void AddCallback(InspectorSocketServer::ServerCallback callback)
void ServerSocketClosed(ServerSocket *server_socket)
void SessionTerminated(SocketSession *session)
bool HandleGetRequest(InspectorSocket *socket, const std::string &path)
void ServerSocketListening(ServerSocket *server_socket)
Closer(InspectorSocketServer *server)
static int Listen(InspectorSocketServer *inspector_server, sockaddr *addr, uv_loop_t *loop)
void Send(int session_id, const std::string &message)