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