blob: 622257c18c349349d18bef521a6704c99ab1da4d [file] [log] [blame]
// Copyright 2019 The Chromium OS 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 "cups_proxy/mojo_handler.h"
#include <map>
#include <utility>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
namespace cups_proxy {
namespace {
// Minimum proxy.mojom CupsProxier interface version required.
const int kMinVersionRequired = 1;
std::string ShowHeaders(const IppHeaders& headers) {
std::vector<std::string> ret;
for (const auto& header : headers) {
ret.push_back(base::JoinString({header->key, header->value}, " = "));
}
return base::JoinString(ret, ", ");
}
std::string ShowBody(const IppBody& body) {
std::string s(body.begin(), body.end());
std::replace_if(
s.begin(), s.end(), [](char c) -> bool { return c == '\0'; }, '|');
return s;
}
IppHeaders ConvertHeadersToMojom(
const std::map<std::string, std::string>& headers) {
IppHeaders ret;
for (const auto& header : headers) {
auto mojom_header = mojom::HttpHeader::New();
mojom_header->key = header.first;
mojom_header->value = header.second;
ret.push_back(std::move(mojom_header));
}
return ret;
}
} // namespace
MojoHandler::MojoHandler() : mojo_thread_("cups_proxy_mojo_thread") {}
MojoHandler::~MojoHandler() {
// The message pipe is bound on the mojo thread, and it has to be closed on
// the same thread which it is bound, so we close the message pipe by calling
// .reset() on the mojo thread.
mojo_task_runner_->PostTask(FROM_HERE,
base::Bind(&mojom::CupsProxierPtr::reset,
base::Unretained(&chrome_proxy_)));
mojo_thread_.Stop();
}
bool MojoHandler::StartThread() {
if (!mojo_thread_.Start()) {
return false;
}
mojo_task_runner_ = mojo_thread_.task_runner();
return true;
}
void MojoHandler::SetupMojoPipe(base::ScopedFD fd,
base::Closure error_handler) {
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannelEndpoint(mojo::PlatformHandle(std::move(fd))));
mojo_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MojoHandler::SetupMojoPipeOnThread,
base::Unretained(this), std::move(error_handler),
std::move(invitation)));
}
void MojoHandler::SetupMojoPipeOnThread(base::Closure error_handler,
mojo::IncomingInvitation invitation) {
DCHECK(mojo_task_runner_->BelongsToCurrentThread());
DCHECK(!chrome_proxy_);
// Bind primordial message pipe to a CupsProxyService implementation.
chrome_proxy_.Bind(mojom::CupsProxierPtrInfo(
invitation.ExtractMessagePipe(
printing::kBootstrapMojoConnectionChannelToken),
0u /* version */));
chrome_proxy_.set_connection_error_handler(std::move(error_handler));
chrome_proxy_.RequireVersion(kMinVersionRequired);
for (auto& callback : queued_requests_) {
mojo_task_runner_->PostTask(FROM_HERE, std::move(callback));
}
queued_requests_.clear();
LOG(INFO) << "Mojo connection bootstrapped.";
}
bool MojoHandler::IsInitialized() {
return chrome_proxy_.is_bound();
}
void MojoHandler::ProxyRequestOnThread(
const std::string& method,
const std::string& url,
const std::string& version,
IppHeaders headers,
const IppBody& body,
mojom::CupsProxier::ProxyRequestCallback callback) {
DCHECK(mojo_task_runner_->BelongsToCurrentThread());
if (chrome_proxy_) {
chrome_proxy_->ProxyRequest(method, url, version, std::move(headers), body,
std::move(callback));
} else {
LOG(INFO) << "Chrome Proxy is not up yet, queuing the request.";
queued_requests_.push_back(base::BindOnce(
&MojoHandler::ProxyRequestOnThread, base::Unretained(this), method, url,
version, std::move(headers), body, std::move(callback)));
}
}
IppResponse MojoHandler::ProxyRequestSync(const MHDHttpRequest& request) {
DCHECK(!mojo_task_runner_->BelongsToCurrentThread());
const std::string& url = request.url();
const std::string& method = request.method();
const std::string& version = request.version();
IppHeaders headers = ConvertHeadersToMojom(request.headers());
const IppBody& body = request.body();
IppResponse response;
base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
auto callback = base::BindOnce(
[](IppResponse* response, base::WaitableEvent* event, IppHeaders headers,
const IppBody& ipp_message, int http_status_code) {
response->headers = std::move(headers);
response->body = ipp_message;
response->http_status_code = http_status_code;
event->Signal();
},
&response, &event);
DVLOG(2) << "url = " << url << ", method = " << method
<< ", version = " << version;
DVLOG(2) << "headers = " << ShowHeaders(headers);
DVLOG(2) << "body = " << ShowBody(body);
mojo_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&MojoHandler::ProxyRequestOnThread,
base::Unretained(this), method, url, version,
std::move(headers), body, std::move(callback)));
event.Wait();
DVLOG(2) << "response code = " << response.http_status_code;
DVLOG(2) << "response headers = " << ShowHeaders(response.headers);
DVLOG(2) << "response body = " << ShowBody(response.body);
return response;
}
} // namespace cups_proxy