Home | History | Annotate | Download | only in libwebserv
      1 // Copyright 2015 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "libwebserv/dbus_protocol_handler.h"
     16 
     17 #include <tuple>
     18 
     19 #include <base/logging.h>
     20 #include <brillo/map_utils.h>
     21 #include <brillo/streams/file_stream.h>
     22 #include <brillo/streams/stream_utils.h>
     23 
     24 #include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
     25 #include "libwebserv/dbus_server.h"
     26 #include "libwebserv/protocol_handler.h"
     27 #include "libwebserv/request.h"
     28 #include "libwebserv/request_handler_callback.h"
     29 #include "libwebserv/response_impl.h"
     30 #include "webservd/dbus-proxies.h"
     31 
     32 namespace libwebserv {
     33 
     34 namespace {
     35 
     36 // Dummy callback for async D-Bus errors.
     37 void IgnoreDBusError(brillo::Error* /* error */) {}
     38 
     39 // Copies the data from |src_stream| to the destination stream represented
     40 // by a file descriptor |fd|.
     41 void WriteResponseData(brillo::StreamPtr src_stream,
     42                        const dbus::FileDescriptor& fd) {
     43   int dupfd = dup(fd.value());
     44   auto dest_stream =
     45       brillo::FileStream::FromFileDescriptor(dupfd, true, nullptr);
     46   CHECK(dest_stream);
     47   // Dummy callbacks for success/error of data-copy operation. We ignore both
     48   // notifications here.
     49   auto on_success = [](brillo::StreamPtr, brillo::StreamPtr, uint64_t) {};
     50   auto on_error = [](brillo::StreamPtr, brillo::StreamPtr,
     51                      const brillo::Error*) {};
     52   brillo::stream_utils::CopyData(
     53       std::move(src_stream), std::move(dest_stream), base::Bind(on_success),
     54       base::Bind(on_error));
     55 }
     56 
     57 }  // anonymous namespace
     58 
     59 DBusProtocolHandler::DBusProtocolHandler(const std::string& name,
     60                                          DBusServer* server)
     61     : name_{name}, server_{server} {
     62 }
     63 
     64 DBusProtocolHandler::~DBusProtocolHandler() {
     65   // Remove any existing handlers, so the web server knows that we don't
     66   // need them anymore.
     67 
     68   // We need to get a copy of the map keys since removing the handlers will
     69   // modify the map in the middle of the loop and that's not a good thing.
     70   auto handler_ids = brillo::GetMapKeys(request_handlers_);
     71   for (int handler_id : handler_ids) {
     72     RemoveHandler(handler_id);
     73   }
     74 }
     75 bool DBusProtocolHandler::IsConnected() const {
     76   return !proxies_.empty();
     77 }
     78 
     79 std::string DBusProtocolHandler::GetName() const {
     80   return name_;
     81 }
     82 
     83 std::set<uint16_t> DBusProtocolHandler::GetPorts() const {
     84   std::set<uint16_t> ports;
     85   for (const auto& pair : proxies_)
     86     ports.insert(pair.second->port());
     87   return ports;
     88 }
     89 
     90 std::set<std::string> DBusProtocolHandler::GetProtocols() const {
     91   std::set<std::string> protocols;
     92   for (const auto& pair : proxies_)
     93     protocols.insert(pair.second->protocol());
     94   return protocols;
     95 }
     96 
     97 brillo::Blob DBusProtocolHandler::GetCertificateFingerprint() const {
     98   brillo::Blob fingerprint;
     99   for (const auto& pair : proxies_) {
    100     fingerprint = pair.second->certificate_fingerprint();
    101     if (!fingerprint.empty())
    102       break;
    103   }
    104   return fingerprint;
    105 }
    106 
    107 int DBusProtocolHandler::AddHandler(
    108     const std::string& url,
    109     const std::string& method,
    110     std::unique_ptr<RequestHandlerInterface> handler) {
    111   request_handlers_.emplace(
    112       ++last_handler_id_,
    113       HandlerMapEntry{url, method,
    114                       std::map<ProtocolHandlerProxyInterface*, std::string>{},
    115                       std::move(handler)});
    116   // For each instance of remote protocol handler object sharing the same name,
    117   // add the request handler.
    118   for (const auto& pair : proxies_) {
    119     pair.second->AddRequestHandlerAsync(
    120         url,
    121         method,
    122         server_->service_name_,
    123         base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
    124                    weak_ptr_factory_.GetWeakPtr(),
    125                    last_handler_id_,
    126                    pair.second),
    127         base::Bind(&DBusProtocolHandler::AddHandlerError,
    128                    weak_ptr_factory_.GetWeakPtr(),
    129                    last_handler_id_));
    130   }
    131   return last_handler_id_;
    132 }
    133 
    134 int DBusProtocolHandler::AddHandlerCallback(
    135     const std::string& url,
    136     const std::string& method,
    137     const base::Callback<RequestHandlerInterface::HandlerSignature>&
    138         handler_callback) {
    139   std::unique_ptr<RequestHandlerInterface> handler{
    140       new RequestHandlerCallback{handler_callback}};
    141   return AddHandler(url, method, std::move(handler));
    142 }
    143 
    144 bool DBusProtocolHandler::RemoveHandler(int handler_id) {
    145   auto p = request_handlers_.find(handler_id);
    146   if (p == request_handlers_.end())
    147     return false;
    148 
    149   for (const auto& pair : p->second.remote_handler_ids) {
    150     pair.first->RemoveRequestHandlerAsync(
    151         pair.second,
    152         base::Bind(&base::DoNothing),
    153         base::Bind(&IgnoreDBusError));
    154   }
    155 
    156   request_handlers_.erase(p);
    157   return true;
    158 }
    159 
    160 void DBusProtocolHandler::Connect(ProtocolHandlerProxyInterface* proxy) {
    161   proxies_.emplace(proxy->GetObjectPath(), proxy);
    162   for (const auto& pair : request_handlers_) {
    163     proxy->AddRequestHandlerAsync(
    164         pair.second.url,
    165         pair.second.method,
    166         server_->service_name_,
    167         base::Bind(&DBusProtocolHandler::AddHandlerSuccess,
    168                    weak_ptr_factory_.GetWeakPtr(),
    169                    pair.first,
    170                    proxy),
    171         base::Bind(&DBusProtocolHandler::AddHandlerError,
    172                    weak_ptr_factory_.GetWeakPtr(),
    173                    pair.first));
    174   }
    175 }
    176 
    177 void DBusProtocolHandler::Disconnect(const dbus::ObjectPath& object_path) {
    178   proxies_.erase(object_path);
    179   if (proxies_.empty())
    180     remote_handler_id_map_.clear();
    181   for (auto& pair : request_handlers_)
    182     pair.second.remote_handler_ids.clear();
    183 }
    184 
    185 void DBusProtocolHandler::AddHandlerSuccess(
    186     int handler_id,
    187     ProtocolHandlerProxyInterface* proxy,
    188     const std::string& remote_handler_id) {
    189   auto p = request_handlers_.find(handler_id);
    190   CHECK(p != request_handlers_.end());
    191   p->second.remote_handler_ids.emplace(proxy, remote_handler_id);
    192 
    193   remote_handler_id_map_.emplace(remote_handler_id, handler_id);
    194 }
    195 
    196 void DBusProtocolHandler::AddHandlerError(int /* handler_id */,
    197                                           brillo::Error* /* error */) {
    198   // Nothing to do at the moment.
    199 }
    200 
    201 bool DBusProtocolHandler::ProcessRequest(const std::string& protocol_handler_id,
    202                                      const std::string& remote_handler_id,
    203                                      const std::string& request_id,
    204                                      std::unique_ptr<Request> request,
    205                                      brillo::ErrorPtr* error) {
    206   request_id_map_.emplace(request_id, protocol_handler_id);
    207   auto id_iter = remote_handler_id_map_.find(remote_handler_id);
    208   if (id_iter == remote_handler_id_map_.end()) {
    209     brillo::Error::AddToPrintf(error, FROM_HERE,
    210                                brillo::errors::dbus::kDomain,
    211                                DBUS_ERROR_FAILED,
    212                                "Unknown request handler '%s'",
    213                                remote_handler_id.c_str());
    214     return false;
    215   }
    216   auto handler_iter = request_handlers_.find(id_iter->second);
    217   if (handler_iter == request_handlers_.end()) {
    218     brillo::Error::AddToPrintf(error, FROM_HERE,
    219                                brillo::errors::dbus::kDomain,
    220                                DBUS_ERROR_FAILED,
    221                                "Handler # %d is no longer available",
    222                                id_iter->second);
    223     return false;
    224   }
    225   handler_iter->second.handler->HandleRequest(
    226       std::move(request),
    227       std::unique_ptr<Response>{new ResponseImpl{this, request_id}});
    228   return true;
    229 }
    230 
    231 void DBusProtocolHandler::CompleteRequest(
    232     const std::string& request_id,
    233     int status_code,
    234     const std::multimap<std::string, std::string>& headers,
    235     brillo::StreamPtr data_stream) {
    236   ProtocolHandlerProxyInterface* proxy =
    237       GetRequestProtocolHandlerProxy(request_id);
    238   if (!proxy)
    239     return;
    240 
    241   std::vector<std::tuple<std::string, std::string>> header_list;
    242   header_list.reserve(headers.size());
    243   for (const auto& pair : headers)
    244     header_list.emplace_back(pair.first, pair.second);
    245 
    246   int64_t data_size = -1;
    247   if (data_stream->CanGetSize())
    248     data_size = data_stream->GetRemainingSize();
    249   proxy->CompleteRequestAsync(
    250       request_id, status_code, header_list, data_size,
    251       base::Bind(&WriteResponseData, base::Passed(&data_stream)),
    252       base::Bind(&IgnoreDBusError));
    253 }
    254 
    255 void DBusProtocolHandler::GetFileData(
    256     const std::string& request_id,
    257     int file_id,
    258     const base::Callback<void(brillo::StreamPtr)>& success_callback,
    259     const base::Callback<void(brillo::Error*)>& error_callback) {
    260   ProtocolHandlerProxyInterface* proxy =
    261       GetRequestProtocolHandlerProxy(request_id);
    262   CHECK(proxy);
    263 
    264   // Store the success/error callback in a shared object so it can be referenced
    265   // by the two wrapper callbacks. Since the original callbacks MAY contain
    266   // move-only types, copying the base::Callback object is generally unsafe and
    267   // may destroy the source object of the copy (despite the fact that it is
    268   // constant). So, here we move both callbacks to |Callbacks| structure and
    269   // use a shared pointer to it in both success and error callback wrappers.
    270   struct Callbacks {
    271     base::Callback<void(brillo::StreamPtr)> on_success;
    272     base::Callback<void(brillo::Error*)> on_error;
    273   };
    274   auto callbacks = std::make_shared<Callbacks>();
    275   callbacks->on_success = success_callback;
    276   callbacks->on_error = error_callback;
    277 
    278   auto on_success = [callbacks](const dbus::FileDescriptor& fd) {
    279     brillo::ErrorPtr error;
    280     // Unfortunately there is no way to take ownership of the file descriptor
    281     // since |fd| is a const reference, so duplicate the descriptor.
    282     int dupfd = dup(fd.value());
    283     auto stream = brillo::FileStream::FromFileDescriptor(dupfd, true, &error);
    284     if (!stream)
    285       return callbacks->on_error.Run(error.get());
    286     callbacks->on_success.Run(std::move(stream));
    287   };
    288   auto on_error = [callbacks](brillo::Error* error) {
    289     callbacks->on_error.Run(error);
    290   };
    291 
    292   proxy->GetRequestFileDataAsync(request_id, file_id, base::Bind(on_success),
    293                                  base::Bind(on_error));
    294 }
    295 
    296 DBusProtocolHandler::ProtocolHandlerProxyInterface*
    297 DBusProtocolHandler::GetRequestProtocolHandlerProxy(
    298     const std::string& request_id) const {
    299   auto iter = request_id_map_.find(request_id);
    300   if (iter == request_id_map_.end()) {
    301     LOG(ERROR) << "Can't find pending request with ID " << request_id;
    302     return nullptr;
    303   }
    304   std::string handler_id = iter->second;
    305   auto find_proxy_by_id = [handler_id](decltype(*proxies_.begin()) pair) {
    306     return pair.second->id() == handler_id;
    307   };
    308   auto proxy_iter = std::find_if(proxies_.begin(), proxies_.end(),
    309                                  find_proxy_by_id);
    310   if (proxy_iter == proxies_.end()) {
    311     LOG(WARNING) << "Completing a request after the handler proxy is removed";
    312     return nullptr;
    313   }
    314   return proxy_iter->second;
    315 }
    316 
    317 
    318 }  // namespace libwebserv
    319