普通文本  |  319行  |  11.16 KB

// Copyright 2015 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "libwebserv/dbus_protocol_handler.h"

#include <tuple>

#include <base/logging.h>
#include <brillo/map_utils.h>
#include <brillo/streams/file_stream.h>
#include <brillo/streams/stream_utils.h>

#include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
#include "libwebserv/dbus_server.h"
#include "libwebserv/protocol_handler.h"
#include "libwebserv/request.h"
#include "libwebserv/request_handler_callback.h"
#include "libwebserv/response_impl.h"
#include "webservd/dbus-proxies.h"

namespace libwebserv {

namespace {

// Dummy callback for async D-Bus errors.
void IgnoreDBusError(brillo::Error* /* error */) {}

// Copies the data from |src_stream| to the destination stream represented
// by a file descriptor |fd|.
void WriteResponseData(brillo::StreamPtr src_stream,
                       const dbus::FileDescriptor& fd) {
  int dupfd = dup(fd.value());
  auto dest_stream =
      brillo::FileStream::FromFileDescriptor(dupfd, true, nullptr);
  CHECK(dest_stream);
  // Dummy callbacks for success/error of data-copy operation. We ignore both
  // notifications here.
  auto on_success = [](brillo::StreamPtr, brillo::StreamPtr, uint64_t) {};
  auto on_error = [](brillo::StreamPtr, brillo::StreamPtr,
                     const brillo::Error*) {};
  brillo::stream_utils::CopyData(
      std::move(src_stream), std::move(dest_stream), base::Bind(on_success),
      base::Bind(on_error));
}

}  // anonymous namespace

DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
                                         DBusServer* server)
    : name_{name}, server_{server} {
}

DBusProtocolHandler::~DBusProtocolHandler() {
  // Remove any existing handlers, so the web server knows that we don't
  // need them anymore.

  // We need to get a copy of the map keys since removing the handlers will
  // modify the map in the middle of the loop and that's not a good thing.
  auto handler_ids = brillo::GetMapKeys(request_handlers_);
  for (int handler_id : handler_ids) {
    RemoveHandler(handler_id);
  }
}
bool DBusProtocolHandler::IsConnected() const {
  return !proxies_.empty();
}

std::string DBusProtocolHandler::GetName() const {
  return name_;
}

std::set<uint16_t> DBusProtocolHandler::GetPorts() const {
  std::set<uint16_t> ports;
  for (const auto& pair : proxies_)
    ports.insert(pair.second->port());
  return ports;
}

std::set<std::string> DBusProtocolHandler::GetProtocols() const {
  std::set<std::string> protocols;
  for (const auto& pair : proxies_)
    protocols.insert(pair.second->protocol());
  return protocols;
}

brillo::Blob DBusProtocolHandler::GetCertificateFingerprint() const {
  brillo::Blob fingerprint;
  for (const auto& pair : proxies_) {
    fingerprint = pair.second->certificate_fingerprint();
    if (!fingerprint.empty())
      break;
  }
  return fingerprint;
}

int DBusProtocolHandler::AddHandler(
    const std::string& url,
    const std::string& method,
    std::unique_ptr<RequestHandlerInterface> handler) {
  request_handlers_.emplace(
      ++last_handler_id_,
      HandlerMapEntry{url, method,
                      std::map<ProtocolHandlerProxyInterface*, std::string>{},
                      std::move(handler)});
  // For each instance of remote protocol handler object sharing the same name,
  // add the request handler.
  for (const auto& pair : proxies_) {
    pair.second->AddRequestHandlerAsync(
        url,
        method,
        server_->service_name_,
        base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
                   weak_ptr_factory_.GetWeakPtr(),
                   last_handler_id_,
                   pair.second),
        base::Bind(&DBusProtocolHandler::AddHandlerError,
                   weak_ptr_factory_.GetWeakPtr(),
                   last_handler_id_));
  }
  return last_handler_id_;
}

int DBusProtocolHandler::AddHandlerCallback(
    const std::string& url,
    const std::string& method,
    const base::Callback<RequestHandlerInterface::HandlerSignature>&
        handler_callback) {
  std::unique_ptr<RequestHandlerInterface> handler{
      new RequestHandlerCallback{handler_callback}};
  return AddHandler(url, method, std::move(handler));
}

bool DBusProtocolHandler::RemoveHandler(int handler_id) {
  auto p = request_handlers_.find(handler_id);
  if (p == request_handlers_.end())
    return false;

  for (const auto& pair : p->second.remote_handler_ids) {
    pair.first->RemoveRequestHandlerAsync(
        pair.second,
        base::Bind(&base::DoNothing),
        base::Bind(&IgnoreDBusError));
  }

  request_handlers_.erase(p);
  return true;
}

void DBusProtocolHandler::Connect(ProtocolHandlerProxyInterface* proxy) {
  proxies_.emplace(proxy->GetObjectPath(), proxy);
  for (const auto& pair : request_handlers_) {
    proxy->AddRequestHandlerAsync(
        pair.second.url,
        pair.second.method,
        server_->service_name_,
        base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
                   weak_ptr_factory_.GetWeakPtr(),
                   pair.first,
                   proxy),
        base::Bind(&DBusProtocolHandler::AddHandlerError,
                   weak_ptr_factory_.GetWeakPtr(),
                   pair.first));
  }
}

void DBusProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
  proxies_.erase(object_path);
  if (proxies_.empty())
    remote_handler_id_map_.clear();
  for (auto& pair : request_handlers_)
    pair.second.remote_handler_ids.clear();
}

void DBusProtocolHandler::AddHandlerSuccess(
    int handler_id,
    ProtocolHandlerProxyInterface* proxy,
    const std::string& remote_handler_id) {
  auto p = request_handlers_.find(handler_id);
  CHECK(p != request_handlers_.end());
  p->second.remote_handler_ids.emplace(proxy, remote_handler_id);

  remote_handler_id_map_.emplace(remote_handler_id, handler_id);
}

void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
                                          brillo::Error* /* error */) {
  // Nothing to do at the moment.
}

bool DBusProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
                                     const std::string& remote_handler_id,
                                     const std::string& request_id,
                                     std::unique_ptr<Request> request,
                                     brillo::ErrorPtr* error) {
  request_id_map_.emplace(request_id, protocol_handler_id);
  auto id_iter = remote_handler_id_map_.find(remote_handler_id);
  if (id_iter == remote_handler_id_map_.end()) {
    brillo::Error::AddToPrintf(error, FROM_HERE,
                               brillo::errors::dbus::kDomain,
                               DBUS_ERROR_FAILED,
                               "Unknown request handler '%s'",
                               remote_handler_id.c_str());
    return false;
  }
  auto handler_iter = request_handlers_.find(id_iter->second);
  if (handler_iter == request_handlers_.end()) {
    brillo::Error::AddToPrintf(error, FROM_HERE,
                               brillo::errors::dbus::kDomain,
                               DBUS_ERROR_FAILED,
                               "Handler # %d is no longer available",
                               id_iter->second);
    return false;
  }
  handler_iter->second.handler->HandleRequest(
      std::move(request),
      std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
  return true;
}

void DBusProtocolHandler::CompleteRequest(
    const std::string& request_id,
    int status_code,
    const std::multimap<std::string, std::string>& headers,
    brillo::StreamPtr data_stream) {
  ProtocolHandlerProxyInterface* proxy =
      GetRequestProtocolHandlerProxy(request_id);
  if (!proxy)
    return;

  std::vector<std::tuple<std::string, std::string>> header_list;
  header_list.reserve(headers.size());
  for (const auto& pair : headers)
    header_list.emplace_back(pair.first, pair.second);

  int64_t data_size = -1;
  if (data_stream->CanGetSize())
    data_size = data_stream->GetRemainingSize();
  proxy->CompleteRequestAsync(
      request_id, status_code, header_list, data_size,
      base::Bind(&WriteResponseData, base::Passed(&data_stream)),
      base::Bind(&IgnoreDBusError));
}

void DBusProtocolHandler::GetFileData(
    const std::string& request_id,
    int file_id,
    const base::Callback<void(brillo::StreamPtr)>& success_callback,
    const base::Callback<void(brillo::Error*)>& error_callback) {
  ProtocolHandlerProxyInterface* proxy =
      GetRequestProtocolHandlerProxy(request_id);
  CHECK(proxy);

  // Store the success/error callback in a shared object so it can be referenced
  // by the two wrapper callbacks. Since the original callbacks MAY contain
  // move-only types, copying the base::Callback object is generally unsafe and
  // may destroy the source object of the copy (despite the fact that it is
  // constant). So, here we move both callbacks to |Callbacks| structure and
  // use a shared pointer to it in both success and error callback wrappers.
  struct Callbacks {
    base::Callback<void(brillo::StreamPtr)> on_success;
    base::Callback<void(brillo::Error*)> on_error;
  };
  auto callbacks = std::make_shared<Callbacks>();
  callbacks->on_success = success_callback;
  callbacks->on_error = error_callback;

  auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
    brillo::ErrorPtr error;
    // Unfortunately there is no way to take ownership of the file descriptor
    // since |fd| is a const reference, so duplicate the descriptor.
    int dupfd = dup(fd.value());
    auto stream = brillo::FileStream::FromFileDescriptor(dupfd, true, &error);
    if (!stream)
      return callbacks->on_error.Run(error.get());
    callbacks->on_success.Run(std::move(stream));
  };
  auto on_error = [callbacks](brillo::Error* error) {
    callbacks->on_error.Run(error);
  };

  proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
                                 base::Bind(on_error));
}

DBusProtocolHandler::ProtocolHandlerProxyInterface*
DBusProtocolHandler::GetRequestProtocolHandlerProxy(
    const std::string& request_id) const {
  auto iter = request_id_map_.find(request_id);
  if (iter == request_id_map_.end()) {
    LOG(ERROR) << "Can't find pending request with ID " << request_id;
    return nullptr;
  }
  std::string handler_id = iter->second;
  auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
    return pair.second->id() == handler_id;
  };
  auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
                                 find_proxy_by_id);
  if (proxy_iter == proxies_.end()) {
    LOG(WARNING) << "Completing a request after the handler proxy is removed";
    return nullptr;
  }
  return proxy_iter->second;
}


}  // namespace libwebserv