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