Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
inspector_socket_server.cc
Go to the documentation of this file.
2 
3 #include "node.h"
4 #include "uv.h"
5 #include "zlib.h"
6 
7 #include <algorithm>
8 #include <map>
9 #include <set>
10 #include <sstream>
11 
12 namespace node {
13 namespace inspector {
14 
15 // Function is declared in inspector_io.h so the rest of the node does not
16 // depend on inspector_socket_server.h
17 std::string FormatWsAddress(const std::string& host, int port,
18  const std::string& target_id,
19  bool include_protocol) {
20  // Host is valid (socket was bound) so colon means it's a v6 IP address
21  bool v6 = host.find(':') != std::string::npos;
22  std::ostringstream url;
23  if (include_protocol)
24  url << "ws://";
25  if (v6) {
26  url << '[';
27  }
28  url << host;
29  if (v6) {
30  url << ']';
31  }
32  url << ':' << port << '/' << target_id;
33  return url.str();
34 }
35 
36 
37 namespace {
38 
39 static const uint8_t PROTOCOL_JSON[] = {
40  #include "v8_inspector_protocol_json.h" // NOLINT(build/include_order)
41 };
42 
43 void Escape(std::string* string) {
44  for (char& c : *string) {
45  c = (c == '\"' || c == '\\') ? '_' : c;
46  }
47 }
48 
49 std::string MapToString(const std::map<std::string, std::string>& object) {
50  bool first = true;
51  std::ostringstream json;
52  json << "{\n";
53  for (const auto& name_value : object) {
54  if (!first)
55  json << ",\n";
56  first = false;
57  json << " \"" << name_value.first << "\": \"";
58  json << name_value.second << "\"";
59  }
60  json << "\n} ";
61  return json.str();
62 }
63 
64 std::string MapsToString(
65  const std::vector<std::map<std::string, std::string>>& array) {
66  bool first = true;
67  std::ostringstream json;
68  json << "[ ";
69  for (const auto& object : array) {
70  if (!first)
71  json << ", ";
72  first = false;
73  json << MapToString(object);
74  }
75  json << "]\n\n";
76  return json.str();
77 }
78 
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;
84  }
85  return nullptr;
86 }
87 
88 void OnBufferAlloc(uv_handle_t* handle, size_t len, uv_buf_t* buf) {
89  buf->base = new char[len];
90  buf->len = len;
91 }
92 
93 void PrintDebuggerReadyMessage(const std::string& host,
94  int port,
95  const std::vector<std::string>& ids,
96  FILE* out) {
97  if (out == NULL) {
98  return;
99  }
100  for (const std::string& id : ids) {
101  fprintf(out, "Debugger listening on %s\n",
102  FormatWsAddress(host, port, id, true).c_str());
103  }
104  fprintf(out, "For help see %s\n",
105  "https://nodejs.org/en/docs/inspector");
106  fflush(out);
107 }
108 
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"
114  "\r\n";
115  char header[sizeof(HEADERS) + 20];
116  int header_len = snprintf(header, sizeof(header), HEADERS, response.size());
117  inspector_write(socket, header, header_len);
118  inspector_write(socket, response.data(), response.size());
119 }
120 
121 void SendVersionResponse(InspectorSocket* socket) {
122  std::map<std::string, std::string> response;
123  response["Browser"] = "node.js/" NODE_VERSION;
124  response["Protocol-Version"] = "1.1";
125  SendHttpResponse(socket, MapToString(response));
126 }
127 
128 void SendProtocolJson(InspectorSocket* socket) {
129  z_stream strm;
130  strm.zalloc = Z_NULL;
131  strm.zfree = 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 +
137  PROTOCOL_JSON[2];
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);
147 }
148 
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),
155  &len);
156  if (err != 0)
157  return err;
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));
161  } else {
162  const sockaddr_in* v4 = reinterpret_cast<const sockaddr_in*>(&addr);
163  err = uv_ip4_name(v4, ip, sizeof(ip));
164  }
165  if (err != 0)
166  return err;
167  *out_host = ip;
168  return err;
169 }
170 } // namespace
171 
172 
173 class Closer {
174  public:
175  explicit Closer(InspectorSocketServer* server) : server_(server),
176  close_count_(0) { }
177 
179  if (callback == nullptr)
180  return;
181  callbacks_.insert(callback);
182  }
183 
185  --close_count_;
186  NotifyIfDone();
187  }
188 
190  ++close_count_;
191  }
192 
193  void NotifyIfDone() {
194  if (close_count_ == 0) {
195  for (auto callback : callbacks_) {
196  callback(server_);
197  }
198  InspectorSocketServer* server = server_;
199  delete server->closer_;
200  server->closer_ = nullptr;
201  }
202  }
203 
204  private:
205  InspectorSocketServer* server_;
206  std::set<InspectorSocketServer::ServerCallback> callbacks_;
207  int close_count_;
208 };
209 
211  public:
212  static int Accept(InspectorSocketServer* server, int server_port,
213  uv_stream_t* server_socket);
214  void Send(const std::string& message);
215  void Close();
216 
217  int id() const { return id_; }
218  bool IsForTarget(const std::string& target_id) const {
219  return target_id_ == target_id;
220  }
221  static int ServerPortForClient(InspectorSocket* client) {
222  return From(client)->server_port_;
223  }
224 
225  private:
226  SocketSession(InspectorSocketServer* server, int server_port);
227  static SocketSession* From(InspectorSocket* socket) {
228  return node::ContainerOf(&SocketSession::socket_, socket);
229  }
230 
231  enum class State { kHttp, kWebSocket, kClosing, kEOF, kDeclined };
232  static bool HandshakeCallback(InspectorSocket* socket,
233  enum inspector_handshake_event state,
234  const std::string& path);
235  static void ReadCallback(uv_stream_t* stream, ssize_t read,
236  const uv_buf_t* buf);
237  static void CloseCallback(InspectorSocket* socket, int code);
238 
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;
244  }
245 
246  const int id_;
247  InspectorSocket socket_;
248  InspectorSocketServer* server_;
249  std::string target_id_;
250  State state_;
251  const int server_port_;
252 };
253 
255  public:
256  static int Listen(InspectorSocketServer* inspector_server,
257  sockaddr* addr, uv_loop_t* loop);
258  void Close() {
259  uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_),
260  SocketClosedCallback);
261  }
262  int port() const { return port_; }
263 
264  private:
265  explicit ServerSocket(InspectorSocketServer* server)
266  : tcp_socket_(uv_tcp_t()), server_(server), port_(-1) {}
267  template<typename UvHandle>
268  static ServerSocket* FromTcpSocket(UvHandle* socket) {
269  return node::ContainerOf(&ServerSocket::tcp_socket_,
270  reinterpret_cast<uv_tcp_t*>(socket));
271  }
272 
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_);
277  }
278  int DetectPort();
279 
280  uv_tcp_t tcp_socket_;
281  InspectorSocketServer* server_;
282  int port_;
283 };
284 
286  uv_loop_t* loop,
287  const std::string& host,
288  int port,
289  FILE* out) : loop_(loop),
290  delegate_(delegate),
291  host_(host),
292  port_(port),
293  closer_(nullptr),
294  next_session_id_(0),
295  out_(out) {
296  state_ = ServerState::kNew;
297 }
298 
300  const std::string& id) {
301  if (TargetExists(id) && delegate_->StartSession(session->id(), id)) {
302  connected_sessions_[session->id()] = session;
303  return true;
304  } else {
305  return false;
306  }
307 }
308 
310  int id = session->id();
311  if (connected_sessions_.erase(id) != 0) {
312  delegate_->EndSession(id);
313  if (connected_sessions_.empty()) {
314  if (state_ == ServerState::kRunning && !server_sockets_.empty()) {
315  PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
316  delegate_->GetTargetIds(), out_);
317  }
318  if (state_ == ServerState::kStopped) {
319  delegate_->ServerDone();
320  }
321  }
322  }
323  delete session;
324 }
325 
327  const std::string& path) {
328  const char* command = MatchPathSegment(path.c_str(), "/json");
329  if (command == nullptr)
330  return false;
331 
332  if (MatchPathSegment(command, "list") || command[0] == '\0') {
333  SendListResponse(socket);
334  return true;
335  } else if (MatchPathSegment(command, "protocol")) {
336  SendProtocolJson(socket);
337  return true;
338  } else if (MatchPathSegment(command, "version")) {
339  SendVersionResponse(socket);
340  return true;
341  } else if (const char* target_id = MatchPathSegment(command, "activate")) {
342  if (TargetExists(target_id)) {
343  SendHttpResponse(socket, "Target activated");
344  return true;
345  }
346  return false;
347  }
348  return false;
349 }
350 
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;
359  target_map["title"] = delegate_->GetTargetTitle(id);
360  Escape(&target_map["title"]);
361  target_map["type"] = "node";
362  // This attribute value is a "best effort" URL that is passed as a JSON
363  // string. It is not guaranteed to resolve to a valid resource.
364  target_map["url"] = delegate_->GetTargetUrl(id);
365  Escape(&target_map["url"]);
366 
367  bool connected = false;
368  for (const auto& session : connected_sessions_) {
369  if (session.second->IsForTarget(id)) {
370  connected = true;
371  break;
372  }
373  }
374  if (!connected) {
375  std::string host;
376  int port = SocketSession::ServerPortForClient(socket);
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=";
381  frontend_url << FormatWsAddress(host, port, id, false);
382  target_map["devtoolsFrontendUrl"] += frontend_url.str();
383  target_map["webSocketDebuggerUrl"] =
384  FormatWsAddress(host, port, id, true);
385  }
386  }
387  SendHttpResponse(socket, MapsToString(response));
388 }
389 
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);
400  if (err < 0) {
401  if (out_ != NULL) {
402  fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
403  uv_strerror(err));
404  }
405  return false;
406  }
407  for (addrinfo* address = req.addrinfo; address != nullptr;
408  address = address->ai_next) {
409  err = ServerSocket::Listen(this, address->ai_addr, loop_);
410  }
411  uv_freeaddrinfo(req.addrinfo);
412 
413  if (!connected_sessions_.empty()) {
414  return true;
415  }
416  // We only show error if we failed to start server on all addresses. We only
417  // show one error, for the last address.
418  if (server_sockets_.empty()) {
419  if (out_ != NULL) {
420  fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
421  host_.c_str(), port_, uv_strerror(err));
422  fflush(out_);
423  }
424  return false;
425  }
426  state_ = ServerState::kRunning;
427  // getaddrinfo sorts the addresses, so the first port is most relevant.
428  PrintDebuggerReadyMessage(host_, server_sockets_[0]->port(),
429  delegate_->GetTargetIds(), out_);
430  return true;
431 }
432 
434  CHECK_EQ(state_, ServerState::kRunning);
435  if (closer_ == nullptr) {
436  closer_ = new Closer(this);
437  }
438  closer_->AddCallback(cb);
439  closer_->IncreaseExpectedCount();
440  state_ = ServerState::kStopping;
441  for (ServerSocket* server_socket : server_sockets_)
442  server_socket->Close();
443  closer_->NotifyIfDone();
444 }
445 
447  for (const auto& session : connected_sessions_) {
448  session.second->Close();
449  }
450 }
451 
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();
456 }
457 
458 void InspectorSocketServer::Send(int session_id, const std::string& message) {
459  auto session_iterator = connected_sessions_.find(session_id);
460  if (session_iterator != connected_sessions_.end()) {
461  session_iterator->second->Send(message);
462  }
463 }
464 
466  server_sockets_.push_back(server_socket);
467 }
468 
470  CHECK_EQ(state_, ServerState::kStopping);
471 
472  server_sockets_.erase(std::remove(server_sockets_.begin(),
473  server_sockets_.end(), server_socket),
474  server_sockets_.end());
475  if (!server_sockets_.empty())
476  return;
477 
478  if (closer_ != nullptr) {
479  closer_->DecreaseExpectedCount();
480  }
481  if (connected_sessions_.empty()) {
482  delegate_->ServerDone();
483  }
484  state_ = ServerState::kStopped;
485 }
486 
488  if (!server_sockets_.empty()) {
489  return server_sockets_[0]->port();
490  }
491  return port_;
492 }
493 
494 // InspectorSession tracking
495 SocketSession::SocketSession(InspectorSocketServer* server, int server_port)
496  : id_(server->GenerateSessionId()),
497  server_(server),
498  state_(State::kHttp),
499  server_port_(server_port) { }
500 
502  CHECK_NE(state_, State::kClosing);
503  state_ = State::kClosing;
504  inspector_close(&socket_, CloseCallback);
505 }
506 
507 // static
508 int SocketSession::Accept(InspectorSocketServer* server, int server_port,
509  uv_stream_t* server_socket) {
510  // Memory is freed when the socket closes.
511  SocketSession* session = new SocketSession(server, server_port);
512  int err = inspector_accept(server_socket, &session->socket_,
513  HandshakeCallback);
514  if (err != 0) {
515  delete session;
516  }
517  return err;
518 }
519 
520 // static
521 bool SocketSession::HandshakeCallback(InspectorSocket* socket,
523  const std::string& path) {
524  SocketSession* session = SocketSession::From(socket);
525  InspectorSocketServer* server = session->server_;
526  const std::string& id = path.empty() ? path : path.substr(1);
527  switch (event) {
529  return server->HandleGetRequest(socket, path);
531  if (server->SessionStarted(session, id)) {
532  session->SetTargetId(id);
533  return true;
534  } else {
535  session->SetDeclined();
536  return false;
537  }
539  session->FrontendConnected();
540  return true;
542  server->SessionTerminated(session);
543  return false;
544  default:
545  UNREACHABLE();
546  return false;
547  }
548 }
549 
550 // static
551 void SocketSession::CloseCallback(InspectorSocket* socket, int code) {
552  SocketSession* session = SocketSession::From(socket);
553  CHECK_EQ(State::kClosing, session->state_);
554  session->server_->SessionTerminated(session);
555 }
556 
557 void SocketSession::FrontendConnected() {
558  CHECK_EQ(State::kHttp, state_);
559  state_ = State::kWebSocket;
560  inspector_read_start(&socket_, OnBufferAlloc, ReadCallback);
561 }
562 
563 // static
564 void SocketSession::ReadCallback(uv_stream_t* stream, ssize_t read,
565  const uv_buf_t* buf) {
566  InspectorSocket* socket = inspector_from_stream(stream);
567  SocketSession* session = SocketSession::From(socket);
568  if (read > 0) {
569  session->server_->MessageReceived(session->id_,
570  std::string(buf->base, read));
571  } else {
572  session->Close();
573  }
574  if (buf != nullptr && buf->base != nullptr)
575  delete[] buf->base;
576 }
577 
578 void SocketSession::Send(const std::string& message) {
579  inspector_write(&socket_, message.data(), message.length());
580 }
581 
582 // ServerSocket implementation
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);
588  if (err != 0)
589  return err;
590  int port;
591  if (addr.ss_family == AF_INET6)
592  port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
593  else
594  port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
595  port_ = ntohs(port);
596  return err;
597 }
598 
599 // static
601  sockaddr* addr, uv_loop_t* loop) {
602  ServerSocket* server_socket = new ServerSocket(inspector_server);
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);
606  if (err == 0) {
607  err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 1,
608  ServerSocket::SocketConnectedCallback);
609  }
610  if (err == 0) {
611  err = server_socket->DetectPort();
612  }
613  if (err == 0) {
614  inspector_server->ServerSocketListening(server_socket);
615  } else {
616  uv_close(reinterpret_cast<uv_handle_t*>(server), FreeOnCloseCallback);
617  }
618  return err;
619 }
620 
621 // static
622 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
623  int status) {
624  if (status == 0) {
625  ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
626  // Memory is freed when the socket closes.
627  SocketSession::Accept(server_socket->server_, server_socket->port_,
628  tcp_socket);
629  }
630 }
631 
632 // static
633 void ServerSocket::SocketClosedCallback(uv_handle_t* tcp_socket) {
634  ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
635  server_socket->server_->ServerSocketClosed(server_socket);
636  delete server_socket;
637 }
638 
639 } // namespace inspector
640 } // namespace node
void inspector_close(InspectorSocket *inspector, inspector_cb callback)
static int ServerPortForClient(InspectorSocket *client)
unsigned char * buf
Definition: cares_wrap.cc:483
bool IsForTarget(const std::string &target_id) const
int len
Definition: cares_wrap.cc:485
void Send(const std::string &message)
void inspector_write(InspectorSocket *inspector, const char *data, size_t len)
int status
Definition: cares_wrap.cc:479
union node::cares_wrap::@8::CaresAsyncData::@0 data
virtual bool StartSession(int session_id, const std::string &target_id)=0
hostent * host
Definition: cares_wrap.cc:482
#define NODE_VERSION
Definition: node_version.h:62
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)
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
uv_fs_t req
Definition: node_file.cc:374
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)