Commit c9dc8212 authored by Lucía Cantú-Miller's avatar Lucía Cantú-Miller Committed by Commit Bot

[chromedriver] Pipe Handler

Feature added to ChromeDriver for a user to be able to  use pipes to
communicate with Chrome. This cl focuses and the handling, reading
from pipe and writing into pipe.

Bug: chromedriver:3480
Change-Id: If7affbb2dd1da96b7484352abbde0a3cf0a92f7d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2333818
Commit-Queue: Lucía Cantú-Miller <cantumiller@google.com>
Reviewed-by: default avatarJohn Chen <johnchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799811}
parent 3e3d2d5a
......@@ -146,6 +146,8 @@ source_set("automation_client_lib") {
"net/command_id.h",
"net/net_util.cc",
"net/net_util.h",
"net/pipe_handler.cc",
"net/pipe_handler.h",
"net/sync_websocket.h",
"net/sync_websocket_factory.cc",
"net/sync_websocket_factory.h",
......
......@@ -107,6 +107,13 @@ const base::FilePath::CharType kDevToolsActivePort[] =
enum ChromeType { Remote, Desktop, Android, Replay };
#if defined(OS_POSIX)
// The values for kReadFD and kWriteFD come from
// content/browser/devtools/devtools_pipe_handler.cc
const int kReadFD = 3;
const int kWriteFD = 4;
#endif
Status PrepareDesktopCommandLine(const Capabilities& capabilities,
bool enable_chrome_logs,
base::CommandLine* prepared_command,
......@@ -158,7 +165,8 @@ Status PrepareDesktopCommandLine(const Capabilities& capabilities,
switches.RemoveSwitch(excluded_switch);
}
switches.SetFromSwitches(capabilities.switches);
if (!switches.HasSwitch("remote-debugging-port")) {
if (!switches.HasSwitch("remote-debugging-port") &&
!switches.HasSwitch("remote-debugging-pipe")) {
switches.SetSwitch("remote-debugging-port", "0");
}
if (capabilities.exclude_switches.count("user-data-dir") > 0) {
......@@ -390,6 +398,28 @@ Status LaunchRemoteChromeSession(
return Status(kOk);
}
#if defined(OS_POSIX)
Status PipeSetUp(base::LaunchOptions* options, int* write_fd, int* read_fd) {
int chrome_to_driver_pipe_fds[2];
int driver_to_chrome_pipe_fds[2];
if (pipe(chrome_to_driver_pipe_fds) == -1 ||
pipe(driver_to_chrome_pipe_fds) == -1)
return Status(kUnknownError, "cannot set up pipe");
options->fds_to_remap.emplace_back(driver_to_chrome_pipe_fds[0], kReadFD);
options->fds_to_remap.emplace_back(chrome_to_driver_pipe_fds[1], kWriteFD);
close(driver_to_chrome_pipe_fds[0]);
close(chrome_to_driver_pipe_fds[1]);
*write_fd = driver_to_chrome_pipe_fds[1];
*read_fd = chrome_to_driver_pipe_fds[0];
return Status(kOk);
}
#endif
Status LaunchDesktopChrome(network::mojom::URLLoaderFactory* factory,
const SyncWebSocketFactory& socket_factory,
const Capabilities& capabilities,
......@@ -468,6 +498,16 @@ Status LaunchDesktopChrome(network::mojom::URLLoaderFactory* factory,
#endif
#if defined(OS_POSIX)
bool uses_pipe = false;
int write_fd;
int read_fd;
if (capabilities.switches.HasSwitch("remote-debugging-pipe")) {
uses_pipe = true;
Status status = PipeSetUp(&options, &write_fd, &read_fd);
}
base::ScopedFD devnull;
if (!cmd_line->HasSwitch("verbose") && !enable_chrome_logs &&
cmd_line->GetSwitchValueASCII("log-level") != "ALL") {
......@@ -775,33 +815,6 @@ Status LaunchReplayChrome(network::mojom::URLLoaderFactory* factory,
} // namespace
Status PipeSetUp(base::LaunchOptions* options, int* write_fd, int* read_fd) {
#if defined(OS_POSIX)
int chrome_to_driver_pipe_fds[2];
int driver_to_chrome_pipe_fds[2];
if (pipe(chrome_to_driver_pipe_fds) == -1 ||
pipe(driver_to_chrome_pipe_fds) == -1)
return Status(kUnknownError, "cannot set up pipe");
// Numbers 3 & 4 come from kReadDf and kWriteFD in
// content/browser/devtools/devtools_pipe_handler.cc
options->fds_to_remap.emplace_back(driver_to_chrome_pipe_fds[0], 3);
options->fds_to_remap.emplace_back(chrome_to_driver_pipe_fds[1], 4);
close(driver_to_chrome_pipe_fds[0]);
close(chrome_to_driver_pipe_fds[1]);
*write_fd = driver_to_chrome_pipe_fds[1];
*read_fd = chrome_to_driver_pipe_fds[0];
return Status(kOk);
#endif
return Status(kUnknownError, "feature not supported");
}
Status LaunchChrome(network::mojom::URLLoaderFactory* factory,
const SyncWebSocketFactory& socket_factory,
DeviceManager* device_manager,
......
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string.h>
#include <memory>
#include "base/base64.h"
#include "base/bind.h"
#include "build/build_config.h"
#include "chrome/test/chromedriver/net/pipe_handler.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_source.h"
#if defined(OS_WIN)
#include <io.h>
#else
#include <unistd.h>
#endif
namespace {}
const size_t read_buffer_size = 100 * 1024 * 1024;
PipeHandler::PipeHandler(WebSocketListener* listener, int write_fd, int read_fd)
: listener_(listener),
write_fd_(write_fd),
read_fd_(read_fd),
write_buffer_(base::MakeRefCounted<net::DrainableIOBuffer>(
base::MakeRefCounted<net::IOBuffer>(0),
0)),
read_buffer_(base::MakeRefCounted<net::DrainableIOBuffer>(
base::MakeRefCounted<net::IOBuffer>(read_buffer_size),
0)) {}
PipeHandler::~PipeHandler() = default;
bool PipeHandler::Send(const std::string& message) {
VLOG(4) << "PipeHandler::Send " << message;
pending_write_.append(message);
pending_write_.append("\0", 1);
if (!write_buffer_->BytesRemaining()) {
WriteIntoPipe();
}
return true;
}
void PipeHandler::WriteIntoPipe() {
if (!write_buffer_->BytesRemaining()) {
if (pending_write_.empty())
return;
write_buffer_ = base::MakeRefCounted<net::DrainableIOBuffer>(
base::MakeRefCounted<net::StringIOBuffer>(pending_write_),
pending_write_.length());
pending_write_.clear();
}
while (write_buffer_->BytesRemaining()) {
auto bytes_written = write(write_fd_, write_buffer_->data(),
write_buffer_->BytesRemaining());
if (bytes_written == -1) {
LOG(ERROR) << "Connection closed, not able to write into pipe";
Close();
return;
}
write_buffer_->DidConsume(bytes_written);
}
}
void PipeHandler::Read() {
while (true) {
auto bytes_read = read(read_fd_, &read_buffer_, read_buffer_size);
if (read_buffer_->BytesRemaining() == 0) {
LOG(ERROR) << "Connection closed, not enough capacity";
Close();
break;
}
if (!bytes_read)
break;
// Go over the last read chunk, look for \0, extract messages.
int offset = 0;
for (int i = read_buffer_->size() - bytes_read; i < read_buffer_->size();
++i) {
if (read_buffer_->data()[i] == '\0') {
listener_->OnMessageReceived(std::string(read_buffer_->data() + offset,
read_buffer_->data() + i));
offset = i + 1;
}
}
if (offset)
read_buffer_->DidConsume(offset);
}
}
void PipeHandler::Close() {
close(write_fd_);
close(read_fd_);
listener_->OnClose();
}
// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_TEST_CHROMEDRIVER_NET_PIPE_HANDLER_H_
#define CHROME_TEST_CHROMEDRIVER_NET_PIPE_HANDLER_H_
#include <string>
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "chrome/test/chromedriver/net/websocket.h"
namespace net {
class DrainableIOBuffer;
}
class WebSocketListener;
class PipeHandler {
public:
PipeHandler(WebSocketListener* listener, int write_fd, int read_fd);
virtual ~PipeHandler();
// Sends the given message and returns true on success.
bool Send(const std::string& message);
private:
void WriteIntoPipe();
void Read();
void Close();
WebSocketListener* listener_;
int write_fd_;
int read_fd_;
scoped_refptr<net::DrainableIOBuffer> write_buffer_;
scoped_refptr<net::DrainableIOBuffer> read_buffer_;
std::string pending_write_;
DISALLOW_COPY_AND_ASSIGN(PipeHandler);
};
#endif // CHROME_TEST_CHROMEDRIVER_NET_PIPE_HANDLER_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment