• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "inspector_socket_server.h"
2 #include <unistd.h>
3 
4 #include "node.h"
5 #include "util-inl.h"
6 #include "uv.h"
7 #include "zlib.h"
8 
9 #include <algorithm>
10 #include <cstdio>
11 #include <iostream>
12 #include <map>
13 #include <set>
14 #include <sstream>
15 #include <string>
16 
17 namespace node {
18 namespace inspector {
19 
20 // Function is declared in inspector_io.h so the rest of the node does not
21 // depend on inspector_socket_server.h
22 std::string FormatWsAddress(const std::string& host, int port,
23                             const std::string& target_id,
24                             bool include_protocol);
25 namespace {
26 
27 static const uint8_t PROTOCOL_JSON[] = {
28   #include "v8_inspector_protocol_json.h"  // NOLINT(build/include_order)
29 };
30 
Escape(std::string * string)31 void Escape(std::string* string) {
32   for (char& c : *string) {
33     c = (c == '\"' || c == '\\') ? '_' : c;
34   }
35 }
36 
FormatHostPort(const std::string & host,int port)37 std::string FormatHostPort(const std::string& host, int port) {
38   // Host is valid (socket was bound) so colon means it's a v6 IP address
39   bool v6 = host.find(':') != std::string::npos;
40   std::ostringstream url;
41   if (v6) {
42     url << '[';
43   }
44   url << host;
45   if (v6) {
46     url << ']';
47   }
48   url << ':' << port;
49   return url.str();
50 }
51 
FormatAddress(const std::string & host,const std::string & target_id,bool include_protocol)52 std::string FormatAddress(const std::string& host,
53                           const std::string& target_id,
54                           bool include_protocol) {
55   std::ostringstream url;
56   if (include_protocol)
57     url << "ws://";
58   url << host << '/' << target_id;
59   return url.str();
60 }
61 
MapToString(const std::map<std::string,std::string> & object)62 std::string MapToString(const std::map<std::string, std::string>& object) {
63   bool first = true;
64   std::ostringstream json;
65   json << "{\n";
66   for (const auto& name_value : object) {
67     if (!first)
68       json << ",\n";
69     first = false;
70     json << "  \"" << name_value.first << "\": \"";
71     json << name_value.second << "\"";
72   }
73   json << "\n} ";
74   return json.str();
75 }
76 
MapsToString(const std::vector<std::map<std::string,std::string>> & array)77 std::string MapsToString(
78     const std::vector<std::map<std::string, std::string>>& array) {
79   bool first = true;
80   std::ostringstream json;
81   json << "[ ";
82   for (const auto& object : array) {
83     if (!first)
84       json << ", ";
85     first = false;
86     json << MapToString(object);
87   }
88   json << "]\n\n";
89   return json.str();
90 }
91 
MatchPathSegment(const char * path,const char * expected)92 const char* MatchPathSegment(const char* path, const char* expected) {
93   size_t len = strlen(expected);
94   if (StringEqualNoCaseN(path, expected, len)) {
95     if (path[len] == '/') return path + len + 1;
96     if (path[len] == '\0') return path + len;
97   }
98   return nullptr;
99 }
100 
SendHttpResponse(InspectorSocket * socket,const std::string & response,int code)101 void SendHttpResponse(InspectorSocket* socket,
102                       const std::string& response,
103                       int code) {
104   const char HEADERS[] = "HTTP/1.0 %d OK\r\n"
105                          "Content-Type: application/json; charset=UTF-8\r\n"
106                          "Cache-Control: no-cache\r\n"
107                          "Content-Length: %zu\r\n"
108                          "\r\n";
109   char header[sizeof(HEADERS) + 20];
110   int header_len = snprintf(header,
111                             sizeof(header),
112                             HEADERS,
113                             code,
114                             response.size());
115   socket->Write(header, header_len);
116   socket->Write(response.data(), response.size());
117 }
118 
SendVersionResponse(InspectorSocket * socket)119 void SendVersionResponse(InspectorSocket* socket) {
120   std::map<std::string, std::string> response;
121   response["Browser"] = "node.js/" NODE_VERSION;
122   response["Protocol-Version"] = "1.1";
123   SendHttpResponse(socket, MapToString(response), 200);
124 }
125 
SendHttpNotFound(InspectorSocket * socket)126 void SendHttpNotFound(InspectorSocket* socket) {
127   SendHttpResponse(socket, "", 404);
128 }
129 
SendProtocolJson(InspectorSocket * socket)130 void SendProtocolJson(InspectorSocket* socket) {
131   z_stream strm;
132   strm.zalloc = Z_NULL;
133   strm.zfree = Z_NULL;
134   strm.opaque = Z_NULL;
135   CHECK_EQ(Z_OK, inflateInit(&strm));
136   static const size_t kDecompressedSize =
137       PROTOCOL_JSON[0] * 0x10000u +
138       PROTOCOL_JSON[1] * 0x100u +
139       PROTOCOL_JSON[2];
140   strm.next_in = const_cast<uint8_t*>(PROTOCOL_JSON + 3);
141   strm.avail_in = sizeof(PROTOCOL_JSON) - 3;
142   std::string data(kDecompressedSize, '\0');
143   strm.next_out = reinterpret_cast<Byte*>(data.data());
144   strm.avail_out = data.size();
145   CHECK_EQ(Z_STREAM_END, inflate(&strm, Z_FINISH));
146   CHECK_EQ(0, strm.avail_out);
147   CHECK_EQ(Z_OK, inflateEnd(&strm));
148   SendHttpResponse(socket, data, 200);
149 }
150 }  // namespace
151 
FormatWsAddress(const std::string & host,int port,const std::string & target_id,bool include_protocol)152 std::string FormatWsAddress(const std::string& host, int port,
153                             const std::string& target_id,
154                             bool include_protocol) {
155   return FormatAddress(FormatHostPort(host, port), target_id, include_protocol);
156 }
157 
158 class SocketSession {
159  public:
160   SocketSession(InspectorSocketServer* server, int id, int server_port);
Close()161   void Close() {
162     ws_socket_.reset();
163   }
164   void Send(const std::string& message);
Own(InspectorSocket::Pointer ws_socket)165   void Own(InspectorSocket::Pointer ws_socket) {
166     ws_socket_ = std::move(ws_socket);
167   }
id() const168   int id() const { return id_; }
server_port()169   int server_port() {
170     return server_port_;
171   }
ws_socket()172   InspectorSocket* ws_socket() {
173     return ws_socket_.get();
174   }
Accept(const std::string & ws_key)175   void Accept(const std::string& ws_key) {
176     ws_socket_->AcceptUpgrade(ws_key);
177   }
Decline()178   void Decline() {
179     ws_socket_->CancelHandshake();
180   }
181 
182   class Delegate : public InspectorSocket::Delegate {
183    public:
Delegate(InspectorSocketServer * server,int session_id)184     Delegate(InspectorSocketServer* server, int session_id)
185              : server_(server), session_id_(session_id) { }
~Delegate()186     ~Delegate() override {
187       server_->SessionTerminated(session_id_);
188     }
189     void OnHttpGet(const std::string& host, const std::string& path) override;
190     void OnSocketUpgrade(const std::string& host, const std::string& path,
191                          const std::string& ws_key) override;
192     void OnWsFrame(const std::vector<char>& data) override;
193 
194    private:
Session()195     SocketSession* Session() {
196       return server_->Session(session_id_);
197     }
198 
199     InspectorSocketServer* server_;
200     int session_id_;
201   };
202 
203  private:
204   const int id_;
205   InspectorSocket::Pointer ws_socket_;
206   const int server_port_;
207 };
208 
209 class ServerSocket {
210  public:
ServerSocket(InspectorSocketServer * server)211   explicit ServerSocket(InspectorSocketServer* server)
212                         : tcp_socket_(uv_tcp_t()), server_(server), unix_socket_(uv_pipe_t()) {}
213   int Listen(sockaddr* addr, uv_loop_t* loop, int pid = -1);
Close()214   void Close() {
215     uv_close(reinterpret_cast<uv_handle_t*>(&tcp_socket_), FreeOnCloseCallback);
216   }
CloseUnix()217   void CloseUnix() {
218     if (unix_socket_on) {
219       uv_close(reinterpret_cast<uv_handle_t*>(&unix_socket_), nullptr);
220       unix_socket_on = false;
221     }
222   }
port() const223   int port() const { return port_; }
224 
225  private:
226   template <typename UvHandle>
FromTcpSocket(UvHandle * socket)227   static ServerSocket* FromTcpSocket(UvHandle* socket) {
228     return node::ContainerOf(&ServerSocket::tcp_socket_,
229                              reinterpret_cast<uv_tcp_t*>(socket));
230   }
231   static void SocketConnectedCallback(uv_stream_t* tcp_socket, int status);
232   static void UnixSocketConnectedCallback(uv_stream_t* unix_socket, int status);
FreeOnCloseCallback(uv_handle_t * tcp_socket_)233   static void FreeOnCloseCallback(uv_handle_t* tcp_socket_) {
234     delete FromTcpSocket(tcp_socket_);
235   }
236   int DetectPort(uv_loop_t *loop, int pid);
237   ~ServerSocket() = default;
238 
239   uv_tcp_t tcp_socket_;
240   InspectorSocketServer* server_;
241   uv_pipe_t unix_socket_;
242   int port_ = -1;
243   bool unix_socket_on = false;
244 };
245 
PrintDebuggerReadyMessage(const std::string & host,const std::vector<InspectorSocketServer::ServerSocketPtr> & server_sockets,const std::vector<std::string> & ids,const char * verb,bool publish_uid_stderr,FILE * out)246 void PrintDebuggerReadyMessage(
247     const std::string& host,
248     const std::vector<InspectorSocketServer::ServerSocketPtr>& server_sockets,
249     const std::vector<std::string>& ids,
250     const char* verb,
251     bool publish_uid_stderr,
252     FILE* out) {
253   if (!publish_uid_stderr || out == nullptr) {
254     return;
255   }
256   for (const auto& server_socket : server_sockets) {
257     for (const std::string& id : ids) {
258       fprintf(out, "Debugger %s on %s\n",
259               verb,
260               FormatWsAddress(host, server_socket->port(), id, true).c_str());
261     }
262   }
263   fprintf(out, "For help, see: %s\n",
264           "https://nodejs.org/en/docs/inspector");
265   fflush(out);
266 }
267 
InspectorSocketServer(std::unique_ptr<SocketServerDelegate> delegate,uv_loop_t * loop,const std::string & host,int port,const InspectPublishUid & inspect_publish_uid,FILE * out,int pid)268 InspectorSocketServer::InspectorSocketServer(
269     std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop,
270     const std::string& host, int port,
271     const InspectPublishUid& inspect_publish_uid, FILE* out, int pid)
272     : loop_(loop),
273       delegate_(std::move(delegate)),
274       host_(host),
275       port_(port),
276       inspect_publish_uid_(inspect_publish_uid),
277       next_session_id_(0),
278       out_(out),
279       pid_(pid) {
280   delegate_->AssignServer(this);
281   state_ = ServerState::kNew;
282 }
283 
284 InspectorSocketServer::~InspectorSocketServer() = default;
285 
Session(int session_id)286 SocketSession* InspectorSocketServer::Session(int session_id) {
287   auto it = connected_sessions_.find(session_id);
288   return it == connected_sessions_.end() ? nullptr : it->second.second.get();
289 }
290 
SessionStarted(int session_id,const std::string & id,const std::string & ws_key)291 void InspectorSocketServer::SessionStarted(int session_id,
292                                            const std::string& id,
293                                            const std::string& ws_key) {
294   SocketSession* session = Session(session_id);
295   if (!TargetExists(id)) {
296     session->Decline();
297     return;
298   }
299   connected_sessions_[session_id].first = id;
300   session->Accept(ws_key);
301   delegate_->StartSession(session_id, id);
302 }
303 
SessionTerminated(int session_id)304 void InspectorSocketServer::SessionTerminated(int session_id) {
305   if (Session(session_id) == nullptr) {
306     return;
307   }
308   bool was_attached = connected_sessions_[session_id].first != "";
309   if (was_attached) {
310     delegate_->EndSession(session_id);
311   }
312   connected_sessions_.erase(session_id);
313   if (connected_sessions_.empty()) {
314     if (was_attached && state_ == ServerState::kRunning
315         && !server_sockets_.empty()) {
316       PrintDebuggerReadyMessage(host_,
317                                 server_sockets_,
318                                 delegate_->GetTargetIds(),
319                                 "ending",
320                                 inspect_publish_uid_.console,
321                                 out_);
322     }
323     if (state_ == ServerState::kStopped) {
324       delegate_.reset();
325     }
326   }
327 }
328 
HandleGetRequest(int session_id,const std::string & host,const std::string & path)329 bool InspectorSocketServer::HandleGetRequest(int session_id,
330                                              const std::string& host,
331                                              const std::string& path) {
332   SocketSession* session = Session(session_id);
333   InspectorSocket* socket = session->ws_socket();
334   if (!inspect_publish_uid_.http) {
335     SendHttpNotFound(socket);
336     return true;
337   }
338   const char* command = MatchPathSegment(path.c_str(), "/json");
339   if (command == nullptr)
340     return false;
341 
342   if (MatchPathSegment(command, "list") || command[0] == '\0') {
343     SendListResponse(socket, host, session);
344     return true;
345   } else if (MatchPathSegment(command, "protocol")) {
346     SendProtocolJson(socket);
347     return true;
348   } else if (MatchPathSegment(command, "version")) {
349     SendVersionResponse(socket);
350     return true;
351   }
352   return false;
353 }
354 
SendListResponse(InspectorSocket * socket,const std::string & host,SocketSession * session)355 void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
356                                              const std::string& host,
357                                              SocketSession* session) {
358   std::vector<std::map<std::string, std::string>> response;
359   for (const std::string& id : delegate_->GetTargetIds()) {
360     response.push_back(std::map<std::string, std::string>());
361     std::map<std::string, std::string>& target_map = response.back();
362     target_map["description"] = "node.js instance";
363     target_map["faviconUrl"] =
364                         "https://nodejs.org/static/images/favicons/favicon.ico";
365     target_map["id"] = id;
366     target_map["title"] = delegate_->GetTargetTitle(id);
367     Escape(&target_map["title"]);
368     target_map["type"] = "node";
369     // This attribute value is a "best effort" URL that is passed as a JSON
370     // string. It is not guaranteed to resolve to a valid resource.
371     target_map["url"] = delegate_->GetTargetUrl(id);
372     Escape(&target_map["url"]);
373 
374     std::string detected_host = host;
375     if (detected_host.empty()) {
376       detected_host = FormatHostPort(socket->GetHost(),
377                                      session->server_port());
378     }
379     std::string formatted_address = FormatAddress(detected_host, id, false);
380     target_map["devtoolsFrontendUrl"] = GetFrontendURL(false,
381                                                        formatted_address);
382     // The compat URL is for Chrome browsers older than 66.0.3345.0
383     target_map["devtoolsFrontendUrlCompat"] = GetFrontendURL(true,
384                                                              formatted_address);
385     target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true);
386   }
387   SendHttpResponse(socket, MapsToString(response), 200);
388 }
389 
GetFrontendURL(bool is_compat,const std::string & formatted_address)390 std::string InspectorSocketServer::GetFrontendURL(bool is_compat,
391     const std::string &formatted_address) {
392   std::ostringstream frontend_url;
393   frontend_url << "devtools://devtools/bundled/";
394   frontend_url << (is_compat ? "inspector" : "js_app");
395   // We disabled experimental features in DevTools by removing "experiments=true&"
396   // to avoid failure in loading pretty JS code.
397   frontend_url << ".html?v8only=true&ws=";
398   frontend_url << formatted_address;
399   return frontend_url.str();
400 }
401 
Start()402 bool InspectorSocketServer::Start() {
403   CHECK_NOT_NULL(delegate_);
404   CHECK_EQ(state_, ServerState::kNew);
405   std::unique_ptr<SocketServerDelegate> delegate_holder;
406   // We will return it if startup is successful
407   delegate_.swap(delegate_holder);
408   struct addrinfo hints;
409   memset(&hints, 0, sizeof(hints));
410   hints.ai_flags = AI_NUMERICSERV;
411   hints.ai_socktype = SOCK_STREAM;
412   uv_getaddrinfo_t req;
413   const std::string port_string = std::to_string(port_);
414   int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
415                            port_string.c_str(), &hints);
416   if (err < 0) {
417     if (out_ != nullptr) {
418       fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
419               uv_strerror(err));
420     }
421     return false;
422   }
423   for (addrinfo* address = req.addrinfo; address != nullptr;
424        address = address->ai_next) {
425     auto server_socket = ServerSocketPtr(new ServerSocket(this));
426     err = server_socket->Listen(address->ai_addr, loop_, pid_);
427     if (err == 0)
428       server_sockets_.push_back(std::move(server_socket));
429   }
430   uv_freeaddrinfo(req.addrinfo);
431 
432   // We only show error if we failed to start server on all addresses. We only
433   // show one error, for the last address.
434   if (server_sockets_.empty()) {
435     if (out_ != nullptr) {
436       fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
437               host_.c_str(), port_, uv_strerror(err));
438       fflush(out_);
439     }
440     return false;
441   }
442   delegate_.swap(delegate_holder);
443   state_ = ServerState::kRunning;
444   PrintDebuggerReadyMessage(host_,
445                             server_sockets_,
446                             delegate_->GetTargetIds(),
447                             "listening",
448                             inspect_publish_uid_.console,
449                             out_);
450   return true;
451 }
452 
Stop()453 void InspectorSocketServer::Stop() {
454   if (state_ == ServerState::kStopped)
455     return;
456   CHECK_EQ(state_, ServerState::kRunning);
457   state_ = ServerState::kStopped;
458   server_sockets_.clear();
459   if (done())
460     delegate_.reset();
461 }
462 
TerminateConnections()463 void InspectorSocketServer::TerminateConnections() {
464   for (const auto& key_value : connected_sessions_)
465     key_value.second.second->Close();
466 }
467 
TargetExists(const std::string & id)468 bool InspectorSocketServer::TargetExists(const std::string& id) {
469   const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
470   const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
471   return found != target_ids.end();
472 }
473 
Port() const474 int InspectorSocketServer::Port() const {
475   if (!server_sockets_.empty()) {
476     return server_sockets_[0]->port();
477   }
478   return port_;
479 }
480 
Accept(int server_port,uv_stream_t * server_socket)481 void InspectorSocketServer::Accept(int server_port,
482                                    uv_stream_t* server_socket) {
483   std::unique_ptr<SocketSession> session(
484       new SocketSession(this, next_session_id_++, server_port));
485 
486   InspectorSocket::DelegatePointer delegate =
487       InspectorSocket::DelegatePointer(
488           new SocketSession::Delegate(this, session->id()));
489 
490   InspectorSocket::Pointer inspector =
491       InspectorSocket::Accept(server_socket, std::move(delegate));
492   if (inspector) {
493     session->Own(std::move(inspector));
494     connected_sessions_[session->id()].second = std::move(session);
495   }
496 }
497 
Send(int session_id,const std::string & message)498 void InspectorSocketServer::Send(int session_id, const std::string& message) {
499   SocketSession* session = Session(session_id);
500   if (session != nullptr) {
501     session->Send(message);
502   }
503 }
504 
CloseServerSocket(ServerSocket * server)505 void InspectorSocketServer::CloseServerSocket(ServerSocket* server) {
506   server->Close();
507   server->CloseUnix();
508 }
509 
510 // InspectorSession tracking
SocketSession(InspectorSocketServer * server,int id,int server_port)511 SocketSession::SocketSession(InspectorSocketServer* server, int id,
512                              int server_port)
513     : id_(id), server_port_(server_port) {}
514 
Send(const std::string & message)515 void SocketSession::Send(const std::string& message) {
516   ws_socket_->Write(message.data(), message.length());
517 }
518 
OnHttpGet(const std::string & host,const std::string & path)519 void SocketSession::Delegate::OnHttpGet(const std::string& host,
520                                         const std::string& path) {
521   if (!server_->HandleGetRequest(session_id_, host, path))
522     Session()->ws_socket()->CancelHandshake();
523 }
524 
OnSocketUpgrade(const std::string & host,const std::string & path,const std::string & ws_key)525 void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
526                                               const std::string& path,
527                                               const std::string& ws_key) {
528   std::string id = path.empty() ? path : path.substr(1);
529   server_->SessionStarted(session_id_, id, ws_key);
530 }
531 
OnWsFrame(const std::vector<char> & data)532 void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data) {
533   server_->MessageReceived(session_id_,
534                            std::string(data.data(), data.size()));
535 }
536 
537 // ServerSocket implementation
DetectPort(uv_loop_t * loop,int pid)538 int ServerSocket::DetectPort(uv_loop_t *loop, int pid) {
539   sockaddr_storage addr;
540   int len = sizeof(addr);
541   int err = uv_tcp_getsockname(&tcp_socket_,
542                                reinterpret_cast<struct sockaddr*>(&addr), &len);
543   if (err != 0)
544     return err;
545   int port;
546   if (addr.ss_family == AF_INET6)
547     port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
548   else
549     port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
550   port_ = ntohs(port);
551   if (!unix_socket_on && pid != -1) {
552     auto unixDomainSocketPath = "jsvm_devtools_remote_" +
553       std::to_string(port_) + "_" + std::to_string(pid);
554     auto *abstract = new char[unixDomainSocketPath.length() + 2];
555     abstract[0] = '\0';
556     strcpy(abstract + 1, unixDomainSocketPath.c_str());
557     auto status = uv_pipe_init(loop, &unix_socket_, 0);
558     if (status == 0) {
559       status = uv_pipe_bind(&unix_socket_, abstract);
560     }
561     if (status == 0) {
562       constexpr int unixBacklog = 128;
563       status = uv_listen(reinterpret_cast<uv_stream_t*>(&unix_socket_), unixBacklog,
564         ServerSocket::UnixSocketConnectedCallback);
565     }
566     unix_socket_on = status == 0;
567     delete abstract;
568   }
569   return err;
570 }
571 
Listen(sockaddr * addr,uv_loop_t * loop,int pid)572 int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop, int pid) {
573   uv_tcp_t* server = &tcp_socket_;
574   CHECK_EQ(0, uv_tcp_init(loop, server));
575   int err = uv_tcp_bind(server, addr, 0);
576   if (err == 0) {
577     // 511 is the value used by a 'net' module by default
578     err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511,
579                     ServerSocket::SocketConnectedCallback);
580   }
581   if (err == 0) {
582     err = DetectPort(loop, pid);
583   }
584   return err;
585 }
586 
587 // static
SocketConnectedCallback(uv_stream_t * tcp_socket,int status)588 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
589                                            int status) {
590   if (status == 0) {
591     ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
592     // Memory is freed when the socket closes.
593     server_socket->server_->Accept(server_socket->port_, tcp_socket);
594   }
595 }
596 
UnixSocketConnectedCallback(uv_stream_t * unix_socket,int status)597 void ServerSocket::UnixSocketConnectedCallback(uv_stream_t* unix_socket,
598                                                int status) {
599   if (status == 0) {
600     (void)unix_socket;
601   }
602 }
603 }  // namespace inspector
604 }  // namespace node
605