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