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