PageRenderTime 56ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/content/browser/devtools/devtools_pipe_handler.cc

https://github.com/chromium/chromium
C++ | 433 lines | 360 code | 57 blank | 16 comment | 39 complexity | 3e0d8d1aa127a5e07c4714a23312c7e5 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause
  1. // Copyright (c) 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "content/browser/devtools/devtools_pipe_handler.h"
  5. #include "base/task/thread_pool.h"
  6. #include "build/build_config.h"
  7. #if BUILDFLAG(IS_WIN)
  8. #include <io.h>
  9. #include <windows.h>
  10. #else
  11. #include <sys/socket.h>
  12. #endif
  13. #include <stdio.h>
  14. #include <cstdlib>
  15. #include <memory>
  16. #include <string>
  17. #include <utility>
  18. #include "base/bind.h"
  19. #include "base/command_line.h"
  20. #include "base/files/file_util.h"
  21. #include "base/logging.h"
  22. #include "base/memory/ref_counted_memory.h"
  23. #include "base/message_loop/message_pump_type.h"
  24. #include "base/strings/string_util.h"
  25. #include "base/synchronization/atomic_flag.h"
  26. #include "base/task/sequenced_task_runner.h"
  27. #include "base/task/single_thread_task_runner.h"
  28. #include "base/threading/thread.h"
  29. #include "build/build_config.h"
  30. #include "content/public/browser/browser_task_traits.h"
  31. #include "content/public/browser/browser_thread.h"
  32. #include "content/public/browser/devtools_agent_host.h"
  33. #include "content/public/common/content_switches.h"
  34. #include "net/server/http_connection.h"
  35. #include "third_party/inspector_protocol/crdtp/cbor.h"
  36. const size_t kReceiveBufferSizeForDevTools = 100 * 1024 * 1024; // 100Mb
  37. const size_t kWritePacketSize = 1 << 16;
  38. const int kReadFD = 3;
  39. const int kWriteFD = 4;
  40. // Our CBOR (RFC 7049) based format starts with a tag 24 indicating
  41. // an envelope, that is, a byte string which as payload carries the
  42. // entire remaining message. Thereby, the length of the byte string
  43. // also tells us the message size on the wire.
  44. // The details of the encoding are implemented in
  45. // third_party/inspector_protocol/crdtp/cbor.h.
  46. namespace content {
  47. namespace {
  48. class PipeIOBase {
  49. public:
  50. explicit PipeIOBase(const char* thread_name)
  51. : thread_(new base::Thread(thread_name)) {}
  52. virtual ~PipeIOBase() = default;
  53. bool Start() {
  54. base::Thread::Options options;
  55. options.message_pump_type = base::MessagePumpType::IO;
  56. if (!thread_->StartWithOptions(std::move(options)))
  57. return false;
  58. StartMainLoop();
  59. return true;
  60. }
  61. static void Shutdown(std::unique_ptr<PipeIOBase> pipe_io) {
  62. if (!pipe_io)
  63. return;
  64. auto thread = std::move(pipe_io->thread_);
  65. pipe_io->shutting_down_.Set();
  66. pipe_io->ClosePipe();
  67. // Post self destruction on the custom thread if it's running.
  68. if (thread->task_runner()) {
  69. thread->task_runner()->DeleteSoon(FROM_HERE, std::move(pipe_io));
  70. } else {
  71. pipe_io.reset();
  72. }
  73. // Post background task that would join and destroy the thread.
  74. base::ThreadPool::CreateSequencedTaskRunner(
  75. {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN,
  76. base::WithBaseSyncPrimitives(), base::TaskPriority::BEST_EFFORT})
  77. ->DeleteSoon(FROM_HERE, std::move(thread));
  78. }
  79. protected:
  80. virtual void StartMainLoop() {}
  81. virtual void ClosePipe() = 0;
  82. std::unique_ptr<base::Thread> thread_;
  83. base::AtomicFlag shutting_down_;
  84. };
  85. } // namespace
  86. class PipeReaderBase : public PipeIOBase {
  87. public:
  88. PipeReaderBase(base::WeakPtr<DevToolsPipeHandler> devtools_handler,
  89. int read_fd)
  90. : PipeIOBase("DevToolsPipeHandlerReadThread"),
  91. devtools_handler_(std::move(devtools_handler)) {
  92. #if BUILDFLAG(IS_WIN)
  93. read_handle_ = reinterpret_cast<HANDLE>(_get_osfhandle(read_fd));
  94. #else
  95. read_fd_ = read_fd;
  96. #endif
  97. }
  98. protected:
  99. void StartMainLoop() override {
  100. thread_->task_runner()->PostTask(
  101. FROM_HERE,
  102. base::BindOnce(&PipeReaderBase::ReadLoop, base::Unretained(this)));
  103. }
  104. void ClosePipe() override {
  105. // Concurrently discard the pipe handles to successfully join threads.
  106. #if BUILDFLAG(IS_WIN)
  107. // Cancel pending synchronous read.
  108. CancelIoEx(read_handle_, nullptr);
  109. CloseHandle(read_handle_);
  110. #else
  111. shutdown(read_fd_, SHUT_RDWR);
  112. #endif
  113. }
  114. virtual void ReadLoopInternal() = 0;
  115. size_t ReadBytes(void* buffer, size_t size, bool exact_size) {
  116. size_t bytes_read = 0;
  117. while (bytes_read < size) {
  118. #if BUILDFLAG(IS_WIN)
  119. DWORD size_read = 0;
  120. bool had_error =
  121. !ReadFile(read_handle_, static_cast<char*>(buffer) + bytes_read,
  122. size - bytes_read, &size_read, nullptr);
  123. #else
  124. int size_read = read(read_fd_, static_cast<char*>(buffer) + bytes_read,
  125. size - bytes_read);
  126. if (size_read < 0 && errno == EINTR)
  127. continue;
  128. bool had_error = size_read <= 0;
  129. #endif
  130. if (had_error) {
  131. if (!shutting_down_.IsSet()) {
  132. LOG(ERROR) << "Connection terminated while reading from pipe";
  133. GetUIThreadTaskRunner({})->PostTask(
  134. FROM_HERE, base::BindOnce(&DevToolsPipeHandler::OnDisconnect,
  135. devtools_handler_));
  136. }
  137. return 0;
  138. }
  139. bytes_read += size_read;
  140. if (!exact_size)
  141. break;
  142. }
  143. return bytes_read;
  144. }
  145. void HandleMessage(std::vector<uint8_t> message) {
  146. GetUIThreadTaskRunner({})->PostTask(
  147. FROM_HERE, base::BindOnce(&DevToolsPipeHandler::HandleMessage,
  148. devtools_handler_, std::move(message)));
  149. }
  150. private:
  151. void ReadLoop() {
  152. ReadLoopInternal();
  153. GetUIThreadTaskRunner({})->PostTask(
  154. FROM_HERE,
  155. base::BindOnce(&DevToolsPipeHandler::Shutdown, devtools_handler_));
  156. }
  157. base::WeakPtr<DevToolsPipeHandler> devtools_handler_;
  158. #if BUILDFLAG(IS_WIN)
  159. HANDLE read_handle_;
  160. #else
  161. int read_fd_;
  162. #endif
  163. };
  164. class PipeWriterBase : public PipeIOBase {
  165. public:
  166. explicit PipeWriterBase(int write_fd)
  167. : PipeIOBase("DevToolsPipeHandlerWriteThread") {
  168. #if BUILDFLAG(IS_WIN)
  169. write_handle_ = reinterpret_cast<HANDLE>(_get_osfhandle(write_fd));
  170. #else
  171. write_fd_ = write_fd;
  172. #endif
  173. }
  174. void Write(base::span<const uint8_t> message) {
  175. base::TaskRunner* task_runner = thread_->task_runner().get();
  176. task_runner->PostTask(
  177. FROM_HERE,
  178. base::BindOnce(&PipeWriterBase::WriteIntoPipe, base::Unretained(this),
  179. std::string(message.begin(), message.end())));
  180. }
  181. protected:
  182. void ClosePipe() override {
  183. #if BUILDFLAG(IS_WIN)
  184. CloseHandle(write_handle_);
  185. #else
  186. shutdown(write_fd_, SHUT_RDWR);
  187. #endif
  188. }
  189. virtual void WriteIntoPipe(std::string message) = 0;
  190. void WriteBytes(const char* bytes, size_t size) {
  191. size_t total_written = 0;
  192. while (total_written < size) {
  193. size_t length = size - total_written;
  194. if (length > kWritePacketSize)
  195. length = kWritePacketSize;
  196. #if BUILDFLAG(IS_WIN)
  197. DWORD bytes_written = 0;
  198. bool had_error =
  199. !WriteFile(write_handle_, bytes + total_written,
  200. static_cast<DWORD>(length), &bytes_written, nullptr);
  201. #else
  202. int bytes_written = write(write_fd_, bytes + total_written, length);
  203. if (bytes_written < 0 && errno == EINTR)
  204. continue;
  205. bool had_error = bytes_written <= 0;
  206. #endif
  207. if (had_error) {
  208. if (!shutting_down_.IsSet())
  209. LOG(ERROR) << "Could not write into pipe";
  210. return;
  211. }
  212. total_written += bytes_written;
  213. }
  214. }
  215. private:
  216. #if BUILDFLAG(IS_WIN)
  217. HANDLE write_handle_;
  218. #else
  219. int write_fd_;
  220. #endif
  221. };
  222. namespace {
  223. class PipeWriterASCIIZ : public PipeWriterBase {
  224. public:
  225. explicit PipeWriterASCIIZ(int write_fd) : PipeWriterBase(write_fd) {}
  226. void WriteIntoPipe(std::string message) override {
  227. WriteBytes(message.data(), message.size());
  228. WriteBytes("\0", 1);
  229. }
  230. };
  231. class PipeWriterCBOR : public PipeWriterBase {
  232. public:
  233. explicit PipeWriterCBOR(int write_fd) : PipeWriterBase(write_fd) {}
  234. void WriteIntoPipe(std::string message) override {
  235. DCHECK(crdtp::cbor::IsCBORMessage(crdtp::SpanFrom(message)));
  236. WriteBytes(message.data(), message.size());
  237. }
  238. };
  239. class PipeReaderASCIIZ : public PipeReaderBase {
  240. public:
  241. PipeReaderASCIIZ(base::WeakPtr<DevToolsPipeHandler> devtools_handler,
  242. int read_fd)
  243. : PipeReaderBase(std::move(devtools_handler), read_fd) {
  244. read_buffer_ = new net::HttpConnection::ReadIOBuffer();
  245. read_buffer_->set_max_buffer_size(kReceiveBufferSizeForDevTools);
  246. }
  247. private:
  248. void ReadLoopInternal() override {
  249. while (true) {
  250. if (read_buffer_->RemainingCapacity() == 0 &&
  251. !read_buffer_->IncreaseCapacity()) {
  252. LOG(ERROR) << "Connection closed, not enough capacity";
  253. break;
  254. }
  255. size_t bytes_read = ReadBytes(read_buffer_->data(),
  256. read_buffer_->RemainingCapacity(), false);
  257. if (!bytes_read)
  258. break;
  259. read_buffer_->DidRead(bytes_read);
  260. // Go over the last read chunk, look for \0, extract messages.
  261. int offset = 0;
  262. for (int i = read_buffer_->GetSize() - bytes_read;
  263. i < read_buffer_->GetSize(); ++i) {
  264. if (read_buffer_->StartOfBuffer()[i] == '\0') {
  265. HandleMessage(
  266. std::vector<uint8_t>(read_buffer_->StartOfBuffer() + offset,
  267. read_buffer_->StartOfBuffer() + i));
  268. offset = i + 1;
  269. }
  270. }
  271. if (offset)
  272. read_buffer_->DidConsume(offset);
  273. }
  274. }
  275. scoped_refptr<net::HttpConnection::ReadIOBuffer> read_buffer_;
  276. };
  277. class PipeReaderCBOR : public PipeReaderBase {
  278. public:
  279. PipeReaderCBOR(base::WeakPtr<DevToolsPipeHandler> devtools_handler,
  280. int read_fd)
  281. : PipeReaderBase(std::move(devtools_handler), read_fd) {}
  282. private:
  283. static uint32_t UInt32FromCBOR(const uint8_t* buf) {
  284. return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
  285. }
  286. void ReadLoopInternal() override {
  287. while (true) {
  288. const size_t kPeekSize =
  289. 8; // tag tag_type? byte_string length*4 map_start
  290. std::vector<uint8_t> buffer(kPeekSize);
  291. if (!ReadBytes(&buffer.front(), kPeekSize, true))
  292. break;
  293. auto status_or_header = crdtp::cbor::EnvelopeHeader::ParseFromFragment(
  294. crdtp::SpanFrom(buffer));
  295. if (!status_or_header.ok()) {
  296. LOG(ERROR) << "Error parsing CBOR envelope: "
  297. << status_or_header.status().ToASCIIString();
  298. return;
  299. }
  300. const size_t msg_size = (*status_or_header).outer_size();
  301. CHECK_GT(msg_size, kPeekSize);
  302. buffer.resize(msg_size);
  303. if (!ReadBytes(&buffer.front() + kPeekSize, msg_size - kPeekSize, true))
  304. return;
  305. HandleMessage(std::move(buffer));
  306. }
  307. }
  308. };
  309. } // namespace
  310. // DevToolsPipeHandler ---------------------------------------------------
  311. DevToolsPipeHandler::DevToolsPipeHandler(base::OnceClosure on_disconnect)
  312. : on_disconnect_(std::move(on_disconnect)),
  313. read_fd_(kReadFD),
  314. write_fd_(kWriteFD) {
  315. browser_target_ = DevToolsAgentHost::CreateForBrowser(
  316. nullptr, DevToolsAgentHost::CreateServerSocketCallback());
  317. browser_target_->AttachClient(this);
  318. std::string str_mode = base::ToLowerASCII(
  319. base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
  320. switches::kRemoteDebuggingPipe));
  321. mode_ = str_mode == "cbor" ? DevToolsPipeHandler::ProtocolMode::kCBOR
  322. : DevToolsPipeHandler::ProtocolMode::kASCIIZ;
  323. switch (mode_) {
  324. case ProtocolMode::kASCIIZ:
  325. pipe_reader_ = std::make_unique<PipeReaderASCIIZ>(
  326. weak_factory_.GetWeakPtr(), read_fd_);
  327. pipe_writer_ = std::make_unique<PipeWriterASCIIZ>(write_fd_);
  328. break;
  329. case ProtocolMode::kCBOR:
  330. pipe_reader_ = std::make_unique<PipeReaderCBOR>(
  331. weak_factory_.GetWeakPtr(), read_fd_);
  332. pipe_writer_ = std::make_unique<PipeWriterCBOR>(write_fd_);
  333. break;
  334. }
  335. if (!pipe_reader_->Start() || !pipe_writer_->Start())
  336. Shutdown();
  337. }
  338. void DevToolsPipeHandler::Shutdown() {
  339. if (shutting_down_)
  340. return;
  341. shutting_down_ = true;
  342. // Disconnect from the target.
  343. DCHECK(browser_target_);
  344. browser_target_->DetachClient(this);
  345. browser_target_ = nullptr;
  346. PipeIOBase::Shutdown(std::move(pipe_reader_));
  347. PipeIOBase::Shutdown(std::move(pipe_writer_));
  348. }
  349. DevToolsPipeHandler::~DevToolsPipeHandler() {
  350. Shutdown();
  351. }
  352. void DevToolsPipeHandler::HandleMessage(std::vector<uint8_t> message) {
  353. if (browser_target_)
  354. browser_target_->DispatchProtocolMessage(this, message);
  355. }
  356. void DevToolsPipeHandler::OnDisconnect() {
  357. if (on_disconnect_)
  358. std::move(on_disconnect_).Run();
  359. }
  360. void DevToolsPipeHandler::DispatchProtocolMessage(
  361. DevToolsAgentHost* agent_host,
  362. base::span<const uint8_t> message) {
  363. if (pipe_writer_)
  364. pipe_writer_->Write(message);
  365. }
  366. void DevToolsPipeHandler::AgentHostClosed(DevToolsAgentHost* agent_host) {}
  367. bool DevToolsPipeHandler::UsesBinaryProtocol() {
  368. return mode_ == ProtocolMode::kCBOR;
  369. }
  370. bool DevToolsPipeHandler::AllowUnsafeOperations() {
  371. return true;
  372. }
  373. std::string DevToolsPipeHandler::GetTypeForMetrics() {
  374. return "RemoteDebugger";
  375. }
  376. } // namespace content