• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "inspector_io.h"
2 
3 #include "inspector_socket_server.h"
4 #include "inspector/main_thread_interface.h"
5 #include "inspector/node_string.h"
6 #include "crypto/crypto_util.h"
7 #include "base_object-inl.h"
8 #include "debug_utils-inl.h"
9 #include "node.h"
10 #include "node_internals.h"
11 #include "node_mutex.h"
12 #include "v8-inspector.h"
13 #include "util-inl.h"
14 #include "zlib.h"
15 
16 #include <deque>
17 #include <cstring>
18 #include <vector>
19 
20 namespace node {
21 namespace inspector {
22 namespace {
23 using v8_inspector::StringBuffer;
24 using v8_inspector::StringView;
25 
26 // kKill closes connections and stops the server, kStop only stops the server
27 enum class TransportAction { kKill, kSendMessage, kStop };
28 
ScriptPath(uv_loop_t * loop,const std::string & script_name)29 std::string ScriptPath(uv_loop_t* loop, const std::string& script_name) {
30   std::string script_path;
31 
32   if (!script_name.empty()) {
33     uv_fs_t req;
34     req.ptr = nullptr;
35     if (0 == uv_fs_realpath(loop, &req, script_name.c_str(), nullptr)) {
36       CHECK_NOT_NULL(req.ptr);
37       script_path = std::string(static_cast<char*>(req.ptr));
38     }
39     uv_fs_req_cleanup(&req);
40   }
41 
42   return script_path;
43 }
44 
45 // UUID RFC: https://www.ietf.org/rfc/rfc4122.txt
46 // Used ver 4 - with numbers
GenerateID()47 std::string GenerateID() {
48   uint16_t buffer[8];
49   CHECK(crypto::CSPRNG(buffer, sizeof(buffer)).is_ok());
50 
51   char uuid[256];
52   snprintf(uuid, sizeof(uuid), "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
53            buffer[0],  // time_low
54            buffer[1],  // time_mid
55            buffer[2],  // time_low
56            (buffer[3] & 0x0fff) | 0x4000,  // time_hi_and_version
57            (buffer[4] & 0x3fff) | 0x8000,  // clk_seq_hi clk_seq_low
58            buffer[5],  // node
59            buffer[6],
60            buffer[7]);
61   return uuid;
62 }
63 
64 class RequestToServer {
65  public:
RequestToServer(TransportAction action,int session_id,std::unique_ptr<v8_inspector::StringBuffer> message)66   RequestToServer(TransportAction action,
67                   int session_id,
68                   std::unique_ptr<v8_inspector::StringBuffer> message)
69                   : action_(action),
70                     session_id_(session_id),
71                     message_(std::move(message)) {}
72 
Dispatch(InspectorSocketServer * server) const73   void Dispatch(InspectorSocketServer* server) const {
74     switch (action_) {
75       case TransportAction::kKill:
76         server->TerminateConnections();
77         [[fallthrough]];
78       case TransportAction::kStop:
79         server->Stop();
80         break;
81       case TransportAction::kSendMessage:
82         server->Send(
83             session_id_,
84             protocol::StringUtil::StringViewToUtf8(message_->string()));
85         break;
86     }
87   }
88 
89  private:
90   TransportAction action_;
91   int session_id_;
92   std::unique_ptr<v8_inspector::StringBuffer> message_;
93 };
94 
95 class RequestQueueData {
96  public:
97   using MessageQueue = std::deque<RequestToServer>;
98 
RequestQueueData(uv_loop_t * loop)99   explicit RequestQueueData(uv_loop_t* loop)
100                             : handle_(std::make_shared<RequestQueue>(this)) {
101     int err = uv_async_init(loop, &async_, [](uv_async_t* async) {
102       RequestQueueData* wrapper =
103           node::ContainerOf(&RequestQueueData::async_, async);
104       wrapper->DoDispatch();
105     });
106     CHECK_EQ(0, err);
107   }
108 
109   static void CloseAndFree(RequestQueueData* queue);
110 
Post(int session_id,TransportAction action,std::unique_ptr<StringBuffer> message)111   void Post(int session_id,
112             TransportAction action,
113             std::unique_ptr<StringBuffer> message) {
114     Mutex::ScopedLock scoped_lock(state_lock_);
115     bool notify = messages_.empty();
116     messages_.emplace_back(action, session_id, std::move(message));
117     if (notify) {
118       CHECK_EQ(0, uv_async_send(&async_));
119       incoming_message_cond_.Broadcast(scoped_lock);
120     }
121   }
122 
Wait()123   void Wait() {
124     Mutex::ScopedLock scoped_lock(state_lock_);
125     if (messages_.empty()) {
126       incoming_message_cond_.Wait(scoped_lock);
127     }
128   }
129 
SetServer(InspectorSocketServer * server)130   void SetServer(InspectorSocketServer* server) {
131     server_ = server;
132   }
133 
handle()134   std::shared_ptr<RequestQueue> handle() {
135     return handle_;
136   }
137 
138  private:
139   ~RequestQueueData() = default;
140 
GetMessages()141   MessageQueue GetMessages() {
142     Mutex::ScopedLock scoped_lock(state_lock_);
143     MessageQueue messages;
144     messages_.swap(messages);
145     return messages;
146   }
147 
DoDispatch()148   void DoDispatch() {
149     if (server_ == nullptr)
150       return;
151     for (const auto& request : GetMessages()) {
152       request.Dispatch(server_);
153     }
154   }
155 
156   std::shared_ptr<RequestQueue> handle_;
157   uv_async_t async_;
158   InspectorSocketServer* server_ = nullptr;
159   MessageQueue messages_;
160   Mutex state_lock_;  // Locked before mutating the queue.
161   ConditionVariable incoming_message_cond_;
162 };
163 }  // namespace
164 
165 class RequestQueue {
166  public:
RequestQueue(RequestQueueData * data)167   explicit RequestQueue(RequestQueueData* data) : data_(data) {}
168 
Reset()169   void Reset() {
170     Mutex::ScopedLock scoped_lock(lock_);
171     data_ = nullptr;
172   }
173 
Post(int session_id,TransportAction action,std::unique_ptr<StringBuffer> message)174   void Post(int session_id,
175             TransportAction action,
176             std::unique_ptr<StringBuffer> message) {
177     Mutex::ScopedLock scoped_lock(lock_);
178     if (data_ != nullptr)
179       data_->Post(session_id, action, std::move(message));
180   }
181 
Expired()182   bool Expired() {
183     Mutex::ScopedLock scoped_lock(lock_);
184     return data_ == nullptr;
185   }
186 
187  private:
188   RequestQueueData* data_;
189   Mutex lock_;
190 };
191 
192 class IoSessionDelegate : public InspectorSessionDelegate {
193  public:
IoSessionDelegate(std::shared_ptr<RequestQueue> queue,int id)194   explicit IoSessionDelegate(std::shared_ptr<RequestQueue> queue, int id)
195                              : request_queue_(queue), id_(id) { }
SendMessageToFrontend(const v8_inspector::StringView & message)196   void SendMessageToFrontend(const v8_inspector::StringView& message) override {
197     request_queue_->Post(id_, TransportAction::kSendMessage,
198                          StringBuffer::create(message));
199   }
200 
201  private:
202   std::shared_ptr<RequestQueue> request_queue_;
203   int id_;
204 };
205 
206 // Passed to InspectorSocketServer to handle WS inspector protocol events,
207 // mostly session start, message received, and session end.
208 class InspectorIoDelegate: public node::inspector::SocketServerDelegate {
209  public:
210   InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,
211                       std::shared_ptr<MainThreadHandle> main_thread,
212                       const std::string& target_id,
213                       const std::string& script_path,
214                       const std::string& script_name);
215   ~InspectorIoDelegate() override = default;
216 
217   void StartSession(int session_id, const std::string& target_id) override;
218   void MessageReceived(int session_id, const std::string& message) override;
219   void EndSession(int session_id) override;
220 
221   std::vector<std::string> GetTargetIds() override;
222   std::string GetTargetTitle(const std::string& id) override;
223   std::string GetTargetUrl(const std::string& id) override;
AssignServer(InspectorSocketServer * server)224   void AssignServer(InspectorSocketServer* server) override {
225     request_queue_->SetServer(server);
226   }
227 
228  private:
229   std::shared_ptr<RequestQueueData> request_queue_;
230   std::shared_ptr<MainThreadHandle> main_thread_;
231   std::unordered_map<int, std::unique_ptr<InspectorSession>> sessions_;
232   const std::string script_name_;
233   const std::string script_path_;
234   const std::string target_id_;
235 };
236 
237 // static
Start(std::shared_ptr<MainThreadHandle> main_thread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,const InspectPublishUid & inspect_publish_uid)238 std::unique_ptr<InspectorIo> InspectorIo::Start(
239     std::shared_ptr<MainThreadHandle> main_thread,
240     const std::string& path,
241     std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
242     const InspectPublishUid& inspect_publish_uid) {
243   auto io = std::unique_ptr<InspectorIo>(
244       new InspectorIo(main_thread,
245                       path,
246                       host_port,
247                       inspect_publish_uid));
248   if (io->request_queue_->Expired()) {  // Thread is not running
249     return nullptr;
250   }
251   return io;
252 }
253 
InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,const std::string & path,std::shared_ptr<ExclusiveAccess<HostPort>> host_port,const InspectPublishUid & inspect_publish_uid)254 InspectorIo::InspectorIo(std::shared_ptr<MainThreadHandle> main_thread,
255                          const std::string& path,
256                          std::shared_ptr<ExclusiveAccess<HostPort>> host_port,
257                          const InspectPublishUid& inspect_publish_uid)
258     : main_thread_(main_thread),
259       host_port_(host_port),
260       inspect_publish_uid_(inspect_publish_uid),
261       thread_(),
262       script_name_(path),
263       id_(GenerateID()) {
264   Mutex::ScopedLock scoped_lock(thread_start_lock_);
265   CHECK_EQ(uv_thread_create(&thread_, InspectorIo::ThreadMain, this), 0);
266   thread_start_condition_.Wait(scoped_lock);
267 }
268 
~InspectorIo()269 InspectorIo::~InspectorIo() {
270   request_queue_->Post(0, TransportAction::kKill, nullptr);
271   int err = uv_thread_join(&thread_);
272   CHECK_EQ(err, 0);
273 }
274 
StopAcceptingNewConnections()275 void InspectorIo::StopAcceptingNewConnections() {
276   request_queue_->Post(0, TransportAction::kStop, nullptr);
277 }
278 
279 // static
ThreadMain(void * io)280 void InspectorIo::ThreadMain(void* io) {
281   static_cast<InspectorIo*>(io)->ThreadMain();
282 }
283 
ThreadMain()284 void InspectorIo::ThreadMain() {
285   uv_loop_t loop;
286   loop.data = nullptr;
287   int err = uv_loop_init(&loop);
288   CHECK_EQ(err, 0);
289   std::shared_ptr<RequestQueueData> queue(new RequestQueueData(&loop),
290                                           RequestQueueData::CloseAndFree);
291   std::string script_path = ScriptPath(&loop, script_name_);
292   std::unique_ptr<InspectorIoDelegate> delegate(
293       new InspectorIoDelegate(queue, main_thread_, id_,
294                               script_path, script_name_));
295   std::string host;
296   int port;
297   {
298     ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
299     host = host_port->host();
300     port = host_port->port();
301   }
302   InspectorSocketServer server(std::move(delegate),
303                                &loop,
304                                std::move(host),
305                                port,
306                                inspect_publish_uid_);
307   request_queue_ = queue->handle();
308   // Its lifetime is now that of the server delegate
309   queue.reset();
310   {
311     Mutex::ScopedLock scoped_lock(thread_start_lock_);
312     if (server.Start()) {
313       ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
314       host_port->set_port(server.Port());
315     }
316     thread_start_condition_.Broadcast(scoped_lock);
317   }
318   uv_run(&loop, UV_RUN_DEFAULT);
319   CheckedUvLoopClose(&loop);
320 }
321 
GetWsUrl() const322 std::string InspectorIo::GetWsUrl() const {
323   ExclusiveAccess<HostPort>::Scoped host_port(host_port_);
324   return FormatWsAddress(host_port->host(), host_port->port(), id_, true);
325 }
326 
InspectorIoDelegate(std::shared_ptr<RequestQueueData> queue,std::shared_ptr<MainThreadHandle> main_thread,const std::string & target_id,const std::string & script_path,const std::string & script_name)327 InspectorIoDelegate::InspectorIoDelegate(
328     std::shared_ptr<RequestQueueData> queue,
329     std::shared_ptr<MainThreadHandle> main_thread,
330     const std::string& target_id,
331     const std::string& script_path,
332     const std::string& script_name)
333     : request_queue_(queue), main_thread_(main_thread),
334       script_name_(script_name), script_path_(script_path),
335       target_id_(target_id) {}
336 
StartSession(int session_id,const std::string & target_id)337 void InspectorIoDelegate::StartSession(int session_id,
338                                        const std::string& target_id) {
339   auto session = main_thread_->Connect(
340       std::unique_ptr<InspectorSessionDelegate>(
341           new IoSessionDelegate(request_queue_->handle(), session_id)), true);
342   if (session) {
343     sessions_[session_id] = std::move(session);
344     fprintf(stderr, "Debugger attached.\n");
345   }
346 }
347 
MessageReceived(int session_id,const std::string & message)348 void InspectorIoDelegate::MessageReceived(int session_id,
349                                           const std::string& message) {
350   auto session = sessions_.find(session_id);
351   if (session != sessions_.end())
352     session->second->Dispatch(Utf8ToStringView(message)->string());
353 }
354 
EndSession(int session_id)355 void InspectorIoDelegate::EndSession(int session_id) {
356   sessions_.erase(session_id);
357 }
358 
GetTargetIds()359 std::vector<std::string> InspectorIoDelegate::GetTargetIds() {
360   return { target_id_ };
361 }
362 
GetTargetTitle(const std::string & id)363 std::string InspectorIoDelegate::GetTargetTitle(const std::string& id) {
364   return script_name_.empty() ? GetHumanReadableProcessName() : script_name_;
365 }
366 
GetTargetUrl(const std::string & id)367 std::string InspectorIoDelegate::GetTargetUrl(const std::string& id) {
368   return "file://" + script_path_;
369 }
370 
371 // static
CloseAndFree(RequestQueueData * queue)372 void RequestQueueData::CloseAndFree(RequestQueueData* queue) {
373   queue->handle_->Reset();
374   queue->handle_.reset();
375   uv_close(reinterpret_cast<uv_handle_t*>(&queue->async_),
376            [](uv_handle_t* handle) {
377     uv_async_t* async = reinterpret_cast<uv_async_t*>(handle);
378     RequestQueueData* wrapper =
379         node::ContainerOf(&RequestQueueData::async_, async);
380     delete wrapper;
381   });
382 }
383 }  // namespace inspector
384 }  // namespace node
385