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,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 bool publish_uid_stderr,
238 FILE* out) {
239 if (!publish_uid_stderr || out == nullptr) {
240 return;
241 }
242 for (const auto& server_socket : server_sockets) {
243 for (const std::string& id : ids) {
244 fprintf(out, "Debugger listening on %s\n",
245 FormatWsAddress(host, server_socket->port(), id, true).c_str());
246 }
247 }
248 fprintf(out, "For help, see: %s\n",
249 "https://nodejs.org/en/docs/inspector");
250 fflush(out);
251 }
252
InspectorSocketServer(std::unique_ptr<SocketServerDelegate> delegate,uv_loop_t * loop,const std::string & host,int port,const InspectPublishUid & inspect_publish_uid,FILE * out)253 InspectorSocketServer::InspectorSocketServer(
254 std::unique_ptr<SocketServerDelegate> delegate, uv_loop_t* loop,
255 const std::string& host, int port,
256 const InspectPublishUid& inspect_publish_uid, FILE* out)
257 : loop_(loop),
258 delegate_(std::move(delegate)),
259 host_(host),
260 port_(port),
261 inspect_publish_uid_(inspect_publish_uid),
262 next_session_id_(0),
263 out_(out) {
264 delegate_->AssignServer(this);
265 state_ = ServerState::kNew;
266 }
267
268 InspectorSocketServer::~InspectorSocketServer() = default;
269
Session(int session_id)270 SocketSession* InspectorSocketServer::Session(int session_id) {
271 auto it = connected_sessions_.find(session_id);
272 return it == connected_sessions_.end() ? nullptr : it->second.second.get();
273 }
274
SessionStarted(int session_id,const std::string & id,const std::string & ws_key)275 void InspectorSocketServer::SessionStarted(int session_id,
276 const std::string& id,
277 const std::string& ws_key) {
278 SocketSession* session = Session(session_id);
279 if (!TargetExists(id)) {
280 session->Decline();
281 return;
282 }
283 connected_sessions_[session_id].first = id;
284 session->Accept(ws_key);
285 delegate_->StartSession(session_id, id);
286 }
287
SessionTerminated(int session_id)288 void InspectorSocketServer::SessionTerminated(int session_id) {
289 if (Session(session_id) == nullptr) {
290 return;
291 }
292 bool was_attached = connected_sessions_[session_id].first != "";
293 if (was_attached) {
294 delegate_->EndSession(session_id);
295 }
296 connected_sessions_.erase(session_id);
297 if (connected_sessions_.empty()) {
298 if (was_attached && state_ == ServerState::kRunning
299 && !server_sockets_.empty()) {
300 PrintDebuggerReadyMessage(host_,
301 server_sockets_,
302 delegate_->GetTargetIds(),
303 inspect_publish_uid_.console,
304 out_);
305 }
306 if (state_ == ServerState::kStopped) {
307 delegate_.reset();
308 }
309 }
310 }
311
HandleGetRequest(int session_id,const std::string & host,const std::string & path)312 bool InspectorSocketServer::HandleGetRequest(int session_id,
313 const std::string& host,
314 const std::string& path) {
315 SocketSession* session = Session(session_id);
316 InspectorSocket* socket = session->ws_socket();
317 if (!inspect_publish_uid_.http) {
318 SendHttpNotFound(socket);
319 return true;
320 }
321 const char* command = MatchPathSegment(path.c_str(), "/json");
322 if (command == nullptr)
323 return false;
324
325 if (MatchPathSegment(command, "list") || command[0] == '\0') {
326 SendListResponse(socket, host, session);
327 return true;
328 } else if (MatchPathSegment(command, "protocol")) {
329 SendProtocolJson(socket);
330 return true;
331 } else if (MatchPathSegment(command, "version")) {
332 SendVersionResponse(socket);
333 return true;
334 }
335 return false;
336 }
337
SendListResponse(InspectorSocket * socket,const std::string & host,SocketSession * session)338 void InspectorSocketServer::SendListResponse(InspectorSocket* socket,
339 const std::string& host,
340 SocketSession* session) {
341 std::vector<std::map<std::string, std::string>> response;
342 for (const std::string& id : delegate_->GetTargetIds()) {
343 response.push_back(std::map<std::string, std::string>());
344 std::map<std::string, std::string>& target_map = response.back();
345 target_map["description"] = "node.js instance";
346 target_map["faviconUrl"] =
347 "https://nodejs.org/static/images/favicons/favicon.ico";
348 target_map["id"] = id;
349 target_map["title"] = delegate_->GetTargetTitle(id);
350 Escape(&target_map["title"]);
351 target_map["type"] = "node";
352 // This attribute value is a "best effort" URL that is passed as a JSON
353 // string. It is not guaranteed to resolve to a valid resource.
354 target_map["url"] = delegate_->GetTargetUrl(id);
355 Escape(&target_map["url"]);
356
357 std::string detected_host = host;
358 if (detected_host.empty()) {
359 detected_host = FormatHostPort(socket->GetHost(),
360 session->server_port());
361 }
362 std::string formatted_address = FormatAddress(detected_host, id, false);
363 target_map["devtoolsFrontendUrl"] = GetFrontendURL(false,
364 formatted_address);
365 // The compat URL is for Chrome browsers older than 66.0.3345.0
366 target_map["devtoolsFrontendUrlCompat"] = GetFrontendURL(true,
367 formatted_address);
368 target_map["webSocketDebuggerUrl"] = FormatAddress(detected_host, id, true);
369 }
370 SendHttpResponse(socket, MapsToString(response), 200);
371 }
372
GetFrontendURL(bool is_compat,const std::string & formatted_address)373 std::string InspectorSocketServer::GetFrontendURL(bool is_compat,
374 const std::string &formatted_address) {
375 std::ostringstream frontend_url;
376 frontend_url << "devtools://devtools/bundled/";
377 frontend_url << (is_compat ? "inspector" : "js_app");
378 frontend_url << ".html?experiments=true&v8only=true&ws=";
379 frontend_url << formatted_address;
380 return frontend_url.str();
381 }
382
Start()383 bool InspectorSocketServer::Start() {
384 CHECK_NOT_NULL(delegate_);
385 CHECK_EQ(state_, ServerState::kNew);
386 std::unique_ptr<SocketServerDelegate> delegate_holder;
387 // We will return it if startup is successful
388 delegate_.swap(delegate_holder);
389 struct addrinfo hints;
390 memset(&hints, 0, sizeof(hints));
391 hints.ai_flags = AI_NUMERICSERV;
392 hints.ai_socktype = SOCK_STREAM;
393 uv_getaddrinfo_t req;
394 const std::string port_string = std::to_string(port_);
395 int err = uv_getaddrinfo(loop_, &req, nullptr, host_.c_str(),
396 port_string.c_str(), &hints);
397 if (err < 0) {
398 if (out_ != nullptr) {
399 fprintf(out_, "Unable to resolve \"%s\": %s\n", host_.c_str(),
400 uv_strerror(err));
401 }
402 return false;
403 }
404 for (addrinfo* address = req.addrinfo; address != nullptr;
405 address = address->ai_next) {
406 auto server_socket = ServerSocketPtr(new ServerSocket(this));
407 err = server_socket->Listen(address->ai_addr, loop_);
408 if (err == 0)
409 server_sockets_.push_back(std::move(server_socket));
410 }
411 uv_freeaddrinfo(req.addrinfo);
412
413 // We only show error if we failed to start server on all addresses. We only
414 // show one error, for the last address.
415 if (server_sockets_.empty()) {
416 if (out_ != nullptr) {
417 fprintf(out_, "Starting inspector on %s:%d failed: %s\n",
418 host_.c_str(), port_, uv_strerror(err));
419 fflush(out_);
420 }
421 return false;
422 }
423 delegate_.swap(delegate_holder);
424 state_ = ServerState::kRunning;
425 PrintDebuggerReadyMessage(host_,
426 server_sockets_,
427 delegate_->GetTargetIds(),
428 inspect_publish_uid_.console,
429 out_);
430 return true;
431 }
432
Stop()433 void InspectorSocketServer::Stop() {
434 if (state_ == ServerState::kStopped)
435 return;
436 CHECK_EQ(state_, ServerState::kRunning);
437 state_ = ServerState::kStopped;
438 server_sockets_.clear();
439 if (done())
440 delegate_.reset();
441 }
442
TerminateConnections()443 void InspectorSocketServer::TerminateConnections() {
444 for (const auto& key_value : connected_sessions_)
445 key_value.second.second->Close();
446 }
447
TargetExists(const std::string & id)448 bool InspectorSocketServer::TargetExists(const std::string& id) {
449 const std::vector<std::string>& target_ids = delegate_->GetTargetIds();
450 const auto& found = std::find(target_ids.begin(), target_ids.end(), id);
451 return found != target_ids.end();
452 }
453
Port() const454 int InspectorSocketServer::Port() const {
455 if (!server_sockets_.empty()) {
456 return server_sockets_[0]->port();
457 }
458 return port_;
459 }
460
Accept(int server_port,uv_stream_t * server_socket)461 void InspectorSocketServer::Accept(int server_port,
462 uv_stream_t* server_socket) {
463 std::unique_ptr<SocketSession> session(
464 new SocketSession(this, next_session_id_++, server_port));
465
466 InspectorSocket::DelegatePointer delegate =
467 InspectorSocket::DelegatePointer(
468 new SocketSession::Delegate(this, session->id()));
469
470 InspectorSocket::Pointer inspector =
471 InspectorSocket::Accept(server_socket, std::move(delegate));
472 if (inspector) {
473 session->Own(std::move(inspector));
474 connected_sessions_[session->id()].second = std::move(session);
475 }
476 }
477
Send(int session_id,const std::string & message)478 void InspectorSocketServer::Send(int session_id, const std::string& message) {
479 SocketSession* session = Session(session_id);
480 if (session != nullptr) {
481 session->Send(message);
482 }
483 }
484
CloseServerSocket(ServerSocket * server)485 void InspectorSocketServer::CloseServerSocket(ServerSocket* server) {
486 server->Close();
487 }
488
489 // InspectorSession tracking
SocketSession(InspectorSocketServer * server,int id,int server_port)490 SocketSession::SocketSession(InspectorSocketServer* server, int id,
491 int server_port)
492 : id_(id), server_port_(server_port) {}
493
Send(const std::string & message)494 void SocketSession::Send(const std::string& message) {
495 ws_socket_->Write(message.data(), message.length());
496 }
497
OnHttpGet(const std::string & host,const std::string & path)498 void SocketSession::Delegate::OnHttpGet(const std::string& host,
499 const std::string& path) {
500 if (!server_->HandleGetRequest(session_id_, host, path))
501 Session()->ws_socket()->CancelHandshake();
502 }
503
OnSocketUpgrade(const std::string & host,const std::string & path,const std::string & ws_key)504 void SocketSession::Delegate::OnSocketUpgrade(const std::string& host,
505 const std::string& path,
506 const std::string& ws_key) {
507 std::string id = path.empty() ? path : path.substr(1);
508 server_->SessionStarted(session_id_, id, ws_key);
509 }
510
OnWsFrame(const std::vector<char> & data)511 void SocketSession::Delegate::OnWsFrame(const std::vector<char>& data) {
512 server_->MessageReceived(session_id_,
513 std::string(data.data(), data.size()));
514 }
515
516 // ServerSocket implementation
DetectPort()517 int ServerSocket::DetectPort() {
518 sockaddr_storage addr;
519 int len = sizeof(addr);
520 int err = uv_tcp_getsockname(&tcp_socket_,
521 reinterpret_cast<struct sockaddr*>(&addr), &len);
522 if (err != 0)
523 return err;
524 int port;
525 if (addr.ss_family == AF_INET6)
526 port = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_port;
527 else
528 port = reinterpret_cast<const sockaddr_in*>(&addr)->sin_port;
529 port_ = ntohs(port);
530 return err;
531 }
532
Listen(sockaddr * addr,uv_loop_t * loop)533 int ServerSocket::Listen(sockaddr* addr, uv_loop_t* loop) {
534 uv_tcp_t* server = &tcp_socket_;
535 CHECK_EQ(0, uv_tcp_init(loop, server));
536 int err = uv_tcp_bind(server, addr, 0);
537 if (err == 0) {
538 // 511 is the value used by a 'net' module by default
539 err = uv_listen(reinterpret_cast<uv_stream_t*>(server), 511,
540 ServerSocket::SocketConnectedCallback);
541 }
542 if (err == 0) {
543 err = DetectPort();
544 }
545 return err;
546 }
547
548 // static
SocketConnectedCallback(uv_stream_t * tcp_socket,int status)549 void ServerSocket::SocketConnectedCallback(uv_stream_t* tcp_socket,
550 int status) {
551 if (status == 0) {
552 ServerSocket* server_socket = ServerSocket::FromTcpSocket(tcp_socket);
553 // Memory is freed when the socket closes.
554 server_socket->server_->Accept(server_socket->port_, tcp_socket);
555 }
556 }
557 } // namespace inspector
558 } // namespace node
559