Hide ProtocolHandler implementation
Move ProtocolHandler implementation and fields into DBusProtocolHandler
and extract a ProtocolHandler interface.
Bug: 25932908
Test: `mmma -j16 system/webservd`
Change-Id: I9100387f331e899da9bf44202ce2becf5fad4895
diff --git a/libwebserv/Android.mk b/libwebserv/Android.mk
index 50235bc..bffcd7a 100644
--- a/libwebserv/Android.mk
+++ b/libwebserv/Android.mk
@@ -25,6 +25,7 @@
LOCAL_SHARED_LIBRARIES := libwebservd-client-internal
LOCAL_SRC_FILES := \
dbus_bindings/org.chromium.WebServer.RequestHandler.dbus-xml \
+ dbus_protocol_handler.cc \
dbus_server.cc \
protocol_handler.cc \
request.cc \
diff --git a/libwebserv/dbus_protocol_handler.cc b/libwebserv/dbus_protocol_handler.cc
new file mode 100644
index 0000000..ff7e8b9
--- /dev/null
+++ b/libwebserv/dbus_protocol_handler.cc
@@ -0,0 +1,318 @@
+// 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
diff --git a/libwebserv/dbus_protocol_handler.h b/libwebserv/dbus_protocol_handler.h
new file mode 100644
index 0000000..74e0b49
--- /dev/null
+++ b/libwebserv/dbus_protocol_handler.h
@@ -0,0 +1,164 @@
+// 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.
+
+#ifndef WEBSERVER_LIBWEBSERV_DBUS_PROTOCOL_HANDLER_H_
+#define WEBSERVER_LIBWEBSERV_DBUS_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <brillo/errors/error.h>
+#include <brillo/secure_blob.h>
+#include <brillo/streams/stream.h>
+#include <dbus/object_path.h>
+
+#include <libwebserv/export.h>
+#include <libwebserv/protocol_handler.h>
+#include <libwebserv/request_handler_interface.h>
+
+namespace org {
+namespace chromium {
+namespace WebServer {
+
+class ProtocolHandlerProxyInterface;
+
+} // namespace WebServer
+} // namespace chromium
+} // namespace org
+
+namespace libwebserv {
+
+class DBusServer;
+class Request;
+
+class LIBWEBSERV_PRIVATE DBusProtocolHandler : public ProtocolHandler {
+ public:
+ DBusProtocolHandler(const std::string& name, DBusServer* server);
+ ~DBusProtocolHandler() override;
+
+ bool IsConnected() const override;
+
+ std::string GetName() const override;
+
+ std::set<uint16_t> GetPorts() const override;
+
+ std::set<std::string> GetProtocols() const override;
+
+ brillo::Blob GetCertificateFingerprint() const override;
+
+ int AddHandler(const std::string& url,
+ const std::string& method,
+ std::unique_ptr<RequestHandlerInterface> handler) override;
+
+ int AddHandlerCallback(
+ const std::string& url,
+ const std::string& method,
+ const base::Callback<RequestHandlerInterface::HandlerSignature>&
+ handler_callback) override;
+
+ bool RemoveHandler(int handler_id) override;
+
+ private:
+ friend class FileInfo;
+ friend class DBusServer;
+ friend class ResponseImpl;
+
+ using ProtocolHandlerProxyInterface =
+ org::chromium::WebServer::ProtocolHandlerProxyInterface;
+
+ struct HandlerMapEntry {
+ std::string url;
+ std::string method;
+ std::map<ProtocolHandlerProxyInterface*, std::string> remote_handler_ids;
+ std::unique_ptr<RequestHandlerInterface> handler;
+ };
+
+ // Called by the DBusServer class when the D-Bus proxy object gets connected
+ // to the web server daemon.
+ void Connect(ProtocolHandlerProxyInterface* proxy);
+
+ // Called by the DBusServer class when the D-Bus proxy object gets
+ // disconnected from the web server daemon.
+ void Disconnect(const dbus::ObjectPath& object_path);
+
+ // Asynchronous callbacks to handle successful or failed request handler
+ // registration over D-Bus.
+ void AddHandlerSuccess(
+ int handler_id,
+ ProtocolHandlerProxyInterface* proxy,
+ const std::string& remote_handler_id);
+ void AddHandlerError(int handler_id, brillo::Error* error);
+
+ // Called by DBusServer when an incoming request is dispatched.
+ bool 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);
+
+ // Called by Response object to finish the request and send response data.
+ void CompleteRequest(
+ const std::string& request_id,
+ int status_code,
+ const std::multimap<std::string, std::string>& headers,
+ brillo::StreamPtr data_stream);
+
+ // Makes a call to the (remote) web server request handler over D-Bus to
+ // obtain the file content of uploaded file (identified by |file_id|) during
+ // request with |request_id|.
+ void 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);
+
+ // A helper method to obtain a corresponding protocol handler D-Bus proxy for
+ // outstanding request with ID |request_id|.
+ ProtocolHandlerProxyInterface* GetRequestProtocolHandlerProxy(
+ const std::string& request_id) const;
+
+ // Protocol Handler name.
+ std::string name_;
+ // Back reference to the server object.
+ DBusServer* server_{nullptr};
+ // Handler data map. The key is the client-facing request handler ID returned
+ // by AddHandler() when registering the handler.
+ std::map<int, HandlerMapEntry> request_handlers_;
+ // The counter to generate new handler IDs.
+ int last_handler_id_{0};
+ // Map of remote handler IDs (GUID strings) to client-facing request handler
+ // IDs (int) which are returned by AddHandler() and used as a key in
+ // |request_handlers_|.
+ std::map<std::string, int> remote_handler_id_map_;
+ // Remote D-Bus proxies for the server protocol handler objects.
+ // There could be multiple protocol handlers with the same name (to make
+ // it possible to server the same requests on different ports, for example).
+ std::map<dbus::ObjectPath, ProtocolHandlerProxyInterface*> proxies_;
+ // A map of request ID to protocol handler ID. Used to locate the appropriate
+ // protocol handler D-Bus proxy for given request.
+ std::map<std::string, std::string> request_id_map_;
+
+ base::WeakPtrFactory<DBusProtocolHandler> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(DBusProtocolHandler);
+};
+
+} // namespace libwebserv
+
+#endif // WEBSERVER_LIBWEBSERV_DBUS_PROTOCOL_HANDLER_H_
diff --git a/libwebserv/dbus_server.cc b/libwebserv/dbus_server.cc
index 091b02f..90ace2b 100644
--- a/libwebserv/dbus_server.cc
+++ b/libwebserv/dbus_server.cc
@@ -17,7 +17,7 @@
#include <tuple>
#include <vector>
-#include <libwebserv/protocol_handler.h>
+#include <libwebserv/dbus_protocol_handler.h>
#include <libwebserv/request_impl.h>
#include "dbus_bindings/org.chromium.WebServer.RequestHandler.h"
@@ -58,7 +58,7 @@
std::string request_id = std::get<2>(in_request_info);
std::string url = std::get<3>(in_request_info);
std::string method = std::get<4>(in_request_info);
- ProtocolHandler* protocol_handler =
+ DBusProtocolHandler* protocol_handler =
server_->GetProtocolHandlerByID(protocol_handler_id);
if (!protocol_handler) {
brillo::Error::AddToPrintf(error, FROM_HERE,
@@ -169,7 +169,8 @@
<< "' is on-line (" << handler->name() << ")";
protocol_handler_id_map_.emplace(handler->GetObjectPath(), handler->id());
- ProtocolHandler* registered_handler = GetProtocolHandler(handler->name());
+ DBusProtocolHandler* registered_handler =
+ GetProtocolHandlerImpl(handler->name());
if (registered_handler) {
protocol_handlers_ids_.emplace(handler->id(), registered_handler);
registered_handler->Connect(handler);
@@ -186,7 +187,7 @@
VLOG(1) << "Server-side protocol handler with ID '" << p->second
<< "' is off-line.";
- ProtocolHandler* registered_handler = GetProtocolHandlerByID(p->second);
+ DBusProtocolHandler* registered_handler = GetProtocolHandlerByID(p->second);
if (registered_handler) {
if (!on_protocol_handler_disconnected_.is_null())
on_protocol_handler_disconnected_.Run(registered_handler);
@@ -198,14 +199,19 @@
}
ProtocolHandler* DBusServer::GetProtocolHandler(const std::string& name) {
+ return GetProtocolHandlerImpl(name);
+}
+
+DBusProtocolHandler* DBusServer::GetProtocolHandlerImpl(
+ const std::string& name) {
auto p = protocol_handlers_names_.find(name);
if (p == protocol_handlers_names_.end()) {
VLOG(1) << "Creating a client-side instance of web server's protocol "
<< "handler with name '" << name << "'";
p = protocol_handlers_names_.emplace(
name,
- std::unique_ptr<ProtocolHandler>{new ProtocolHandler{name, this}})
- .first;
+ std::unique_ptr<DBusProtocolHandler>{
+ new DBusProtocolHandler{name, this}}).first;
}
return p->second.get();
}
@@ -218,7 +224,8 @@
return GetProtocolHandler(ProtocolHandler::kHttps);
}
-ProtocolHandler* DBusServer::GetProtocolHandlerByID(const std::string& id) const {
+DBusProtocolHandler* DBusServer::GetProtocolHandlerByID(
+ const std::string& id) const {
auto p = protocol_handlers_ids_.find(id);
if (p == protocol_handlers_ids_.end()) {
LOG(ERROR) << "Unable to locate protocol handler with ID '" << id << "'";
diff --git a/libwebserv/dbus_server.h b/libwebserv/dbus_server.h
index 36bcdcb..dbfdbe2 100644
--- a/libwebserv/dbus_server.h
+++ b/libwebserv/dbus_server.h
@@ -43,6 +43,8 @@
namespace libwebserv {
+class DBusProtocolHandler;
+
class LIBWEBSERV_PRIVATE DBusServer : public Server {
public:
DBusServer();
@@ -79,7 +81,7 @@
base::TimeDelta GetDefaultRequestTimeout() const override;
private:
- friend class ProtocolHandler;
+ friend class DBusProtocolHandler;
class RequestHandler;
void Disconnect();
@@ -100,9 +102,12 @@
const dbus::ObjectPath& object_path);
// Looks up a protocol handler by ID. If not found, returns nullptr.
- ProtocolHandler* GetProtocolHandlerByID(
+ DBusProtocolHandler* GetProtocolHandlerByID(
const std::string& id) const;
+ // Like the public version, but returns our specific handler type.
+ DBusProtocolHandler* GetProtocolHandlerImpl(const std::string& name);
+
// Private implementation of D-Bus RequestHandlerInterface called by the web
// server daemon whenever a new request is available to be processed.
std::unique_ptr<RequestHandler> request_handler_;
@@ -113,10 +118,10 @@
std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object_;
// A mapping of protocol handler name to the associated object.
- std::map<std::string, std::unique_ptr<ProtocolHandler>>
+ std::map<std::string, std::unique_ptr<DBusProtocolHandler>>
protocol_handlers_names_;
// A mapping of protocol handler IDs to the associated object.
- std::map<std::string, ProtocolHandler*> protocol_handlers_ids_;
+ std::map<std::string, DBusProtocolHandler*> protocol_handlers_ids_;
// A map between D-Bus object path of protocol handler and remote protocol
// handler ID.
std::map<dbus::ObjectPath, std::string> protocol_handler_id_map_;
diff --git a/libwebserv/protocol_handler.cc b/libwebserv/protocol_handler.cc
index 8c50d47..b4e54b4 100644
--- a/libwebserv/protocol_handler.cc
+++ b/libwebserv/protocol_handler.cc
@@ -12,303 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include "libwebserv/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/request.h"
-#include "libwebserv/request_handler_callback.h"
-#include "libwebserv/response_impl.h"
-#include "webservd/dbus-proxies.h"
+#include <libwebserv/protocol_handler.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
-
-const char ProtocolHandler::kHttp[] = "http";
+const char ProtocolHandler::kHttp[] = "http";
const char ProtocolHandler::kHttps[] = "https";
-ProtocolHandler::ProtocolHandler(const std::string& name, DBusServer* server)
- : name_{name}, server_{server} {
-}
-
-ProtocolHandler::~ProtocolHandler() {
- // 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);
- }
-}
-
-std::string ProtocolHandler::GetName() const {
- return name_;
-}
-
-std::set<uint16_t> ProtocolHandler::GetPorts() const {
- std::set<uint16_t> ports;
- for (const auto& pair : proxies_)
- ports.insert(pair.second->port());
- return ports;
-}
-
-std::set<std::string> ProtocolHandler::GetProtocols() const {
- std::set<std::string> protocols;
- for (const auto& pair : proxies_)
- protocols.insert(pair.second->protocol());
- return protocols;
-}
-
-brillo::Blob ProtocolHandler::GetCertificateFingerprint() const {
- brillo::Blob fingerprint;
- for (const auto& pair : proxies_) {
- fingerprint = pair.second->certificate_fingerprint();
- if (!fingerprint.empty())
- break;
- }
- return fingerprint;
-}
-
-int ProtocolHandler::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(&ProtocolHandler::AddHandlerSuccess,
- weak_ptr_factory_.GetWeakPtr(),
- last_handler_id_,
- pair.second),
- base::Bind(&ProtocolHandler::AddHandlerError,
- weak_ptr_factory_.GetWeakPtr(),
- last_handler_id_));
- }
- return last_handler_id_;
-}
-
-int ProtocolHandler::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 ProtocolHandler::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 ProtocolHandler::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(&ProtocolHandler::AddHandlerSuccess,
- weak_ptr_factory_.GetWeakPtr(),
- pair.first,
- proxy),
- base::Bind(&ProtocolHandler::AddHandlerError,
- weak_ptr_factory_.GetWeakPtr(),
- pair.first));
- }
-}
-
-void ProtocolHandler::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 ProtocolHandler::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 ProtocolHandler::AddHandlerError(int handler_id, brillo::Error* error) {
- // Nothing to do at the moment.
-}
-
-bool ProtocolHandler::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 ProtocolHandler::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 ProtocolHandler::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));
-}
-
-ProtocolHandler::ProtocolHandlerProxyInterface*
-ProtocolHandler::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
diff --git a/libwebserv/protocol_handler.h b/libwebserv/protocol_handler.h
index 2c2dee5..c4a9bef 100644
--- a/libwebserv/protocol_handler.h
+++ b/libwebserv/protocol_handler.h
@@ -15,71 +15,52 @@
#ifndef WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
#define WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
-#include <map>
#include <memory>
#include <set>
#include <string>
-#include <vector>
#include <base/callback_forward.h>
#include <base/macros.h>
-#include <base/memory/weak_ptr.h>
-#include <brillo/errors/error.h>
#include <brillo/secure_blob.h>
-#include <brillo/streams/stream.h>
-#include <dbus/object_path.h>
#include <libwebserv/export.h>
#include <libwebserv/request_handler_interface.h>
-namespace org {
-namespace chromium {
-namespace WebServer {
-
-class ProtocolHandlerProxyInterface;
-
-} // namespace WebServer
-} // namespace chromium
-} // namespace org
-
namespace libwebserv {
-class DBusServer;
-class Request;
-
// Wrapper around a protocol handler (e.g. HTTP or HTTPs).
// ProtocolHandler allows consumers to add request handlers on a given protocol.
// When the ProtocolHandler is connected, allows users to read port and protocol
// information.
-class LIBWEBSERV_EXPORT ProtocolHandler final {
+class LIBWEBSERV_EXPORT ProtocolHandler {
public:
- ProtocolHandler(const std::string& name, DBusServer* server);
- ~ProtocolHandler();
+ ProtocolHandler() = default;
+ virtual ~ProtocolHandler() = default;
- // Returns true if the protocol handler object is connected to the web server
- // daemon's proxy object and is capable of processing incoming requests.
- bool IsConnected() const { return !proxies_.empty(); }
+ // Returns true if the protocol handler object is backed by a ProtocolHandler
+ // on the remote web server and is capable of processing incoming requests.
+ virtual bool IsConnected() const = 0;
// Handler's name identifier (as provided in "name" setting of config file).
// Standard/default handler names are "http" and "https".
- std::string GetName() const;
+ virtual std::string GetName() const = 0;
// Returns the ports the handler is bound to. There could be multiple.
// If the handler is not connected to the server, this will return an empty
// set.
- std::set<uint16_t> GetPorts() const;
+ virtual std::set<uint16_t> GetPorts() const = 0;
// Returns the transport protocol that is served by this handler.
// Can be either "http" or "https".
// If the handler is not connected to the server, this will return an empty
// set.
- std::set<std::string> GetProtocols() const;
+ virtual std::set<std::string> GetProtocols() const = 0;
// Returns a SHA-256 fingerprint of HTTPS certificate used. Returns an empty
// byte buffer if this handler does not serve the HTTPS protocol.
// If the handler is not connected to the server, this will return an empty
// array.
- brillo::Blob GetCertificateFingerprint() const;
+ virtual brillo::Blob GetCertificateFingerprint() const = 0;
// Adds a request handler for given |url|. If the |url| ends with a '/', this
// makes the handler respond to any URL beneath this path.
@@ -106,106 +87,25 @@
// available. This also happens when the web server goes away and then comes
// back (e.g. restarted). So, there is no need to re-register the handlers
// once the web server process is restarted.
- int AddHandler(const std::string& url,
- const std::string& method,
- std::unique_ptr<RequestHandlerInterface> handler);
+ virtual int AddHandler(const std::string& url,
+ const std::string& method,
+ std::unique_ptr<RequestHandlerInterface> handler) = 0;
// Similar to AddHandler() above but the handler is just a callback function.
- int AddHandlerCallback(
+ virtual int AddHandlerCallback(
const std::string& url,
const std::string& method,
const base::Callback<RequestHandlerInterface::HandlerSignature>&
- handler_callback);
+ handler_callback) = 0;
// Removes the handler with the specified |handler_id|.
// Returns false if the handler with the given ID is not found.
- bool RemoveHandler(int handler_id);
+ virtual bool RemoveHandler(int handler_id) = 0;
static const char kHttp[];
static const char kHttps[];
private:
- friend class FileInfo;
- friend class DBusServer;
- friend class ResponseImpl;
-
- using ProtocolHandlerProxyInterface =
- org::chromium::WebServer::ProtocolHandlerProxyInterface;
-
- struct LIBWEBSERV_PRIVATE HandlerMapEntry {
- std::string url;
- std::string method;
- std::map<ProtocolHandlerProxyInterface*, std::string> remote_handler_ids;
- std::unique_ptr<RequestHandlerInterface> handler;
- };
-
- // Called by the DBusServer class when the D-Bus proxy object gets connected
- // to the web server daemon.
- LIBWEBSERV_PRIVATE void Connect(ProtocolHandlerProxyInterface* proxy);
-
- // Called by the DBusServer class when the D-Bus proxy object gets
- // disconnected from the web server daemon.
- LIBWEBSERV_PRIVATE void Disconnect(const dbus::ObjectPath& object_path);
-
- // Asynchronous callbacks to handle successful or failed request handler
- // registration over D-Bus.
- LIBWEBSERV_PRIVATE void AddHandlerSuccess(
- int handler_id,
- ProtocolHandlerProxyInterface* proxy,
- const std::string& remote_handler_id);
- LIBWEBSERV_PRIVATE void AddHandlerError(int handler_id,
- brillo::Error* error);
-
- // Called by DBusServer when an incoming request is dispatched.
- LIBWEBSERV_PRIVATE bool 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);
-
- // Called by Response object to finish the request and send response data.
- LIBWEBSERV_PRIVATE void CompleteRequest(
- const std::string& request_id,
- int status_code,
- const std::multimap<std::string, std::string>& headers,
- brillo::StreamPtr data_stream);
-
- // Makes a call to the (remote) web server request handler over D-Bus to
- // obtain the file content of uploaded file (identified by |file_id|) during
- // request with |request_id|.
- LIBWEBSERV_PRIVATE void 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);
-
- // A helper method to obtain a corresponding protocol handler D-Bus proxy for
- // outstanding request with ID |request_id|.
- LIBWEBSERV_PRIVATE ProtocolHandlerProxyInterface*
- GetRequestProtocolHandlerProxy(const std::string& request_id) const;
-
- // Protocol Handler name.
- std::string name_;
- // Back reference to the server object.
- DBusServer* server_{nullptr};
- // Handler data map. The key is the client-facing request handler ID returned
- // by AddHandler() when registering the handler.
- std::map<int, HandlerMapEntry> request_handlers_;
- // The counter to generate new handler IDs.
- int last_handler_id_{0};
- // Map of remote handler IDs (GUID strings) to client-facing request handler
- // IDs (int) which are returned by AddHandler() and used as a key in
- // |request_handlers_|.
- std::map<std::string, int> remote_handler_id_map_;
- // Remote D-Bus proxies for the server protocol handler objects.
- // There could be multiple protocol handlers with the same name (to make
- // it possible to server the same requests on different ports, for example).
- std::map<dbus::ObjectPath, ProtocolHandlerProxyInterface*> proxies_;
- // A map of request ID to protocol handler ID. Used to locate the appropriate
- // protocol handler D-Bus proxy for given request.
- std::map<std::string, std::string> request_id_map_;
-
- base::WeakPtrFactory<ProtocolHandler> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ProtocolHandler);
};
diff --git a/libwebserv/request.cc b/libwebserv/request.cc
index 959b294..5d7abf7 100644
--- a/libwebserv/request.cc
+++ b/libwebserv/request.cc
@@ -18,11 +18,11 @@
#include <brillo/http/http_utils.h>
#include <brillo/streams/file_stream.h>
-#include <libwebserv/protocol_handler.h>
+#include <libwebserv/dbus_protocol_handler.h>
namespace libwebserv {
-FileInfo::FileInfo(ProtocolHandler* handler,
+FileInfo::FileInfo(DBusProtocolHandler* handler,
int file_id,
const std::string& request_id,
const std::string& file_name,
@@ -45,7 +45,7 @@
error_callback);
}
-RequestImpl::RequestImpl(ProtocolHandler* handler,
+RequestImpl::RequestImpl(DBusProtocolHandler* handler,
const std::string& url,
const std::string& method)
: Request{url, method}, handler_{handler} {
diff --git a/libwebserv/request.h b/libwebserv/request.h
index ce8d82b..2950d1b 100644
--- a/libwebserv/request.h
+++ b/libwebserv/request.h
@@ -33,7 +33,7 @@
namespace libwebserv {
-class ProtocolHandler;
+class DBusProtocolHandler;
using PairOfStrings = std::pair<std::string, std::string>;
@@ -51,14 +51,14 @@
private:
friend class DBusServer;
- LIBWEBSERV_PRIVATE FileInfo(ProtocolHandler* handler,
+ LIBWEBSERV_PRIVATE FileInfo(DBusProtocolHandler* handler,
int file_id,
const std::string& request_id,
const std::string& file_name,
const std::string& content_type,
const std::string& transfer_encoding);
- ProtocolHandler* handler_{nullptr};
+ DBusProtocolHandler* handler_{nullptr};
int file_id_{0};
std::string request_id_;
std::string file_name_;
diff --git a/libwebserv/request_impl.h b/libwebserv/request_impl.h
index 2c172ec..c2dc2a4 100644
--- a/libwebserv/request_impl.h
+++ b/libwebserv/request_impl.h
@@ -24,6 +24,8 @@
namespace libwebserv {
+class DBusProtocolHandler;
+
// Implementation of the Request interface.
class RequestImpl final : public Request {
public:
@@ -33,10 +35,10 @@
private:
friend class DBusServer;
- LIBWEBSERV_PRIVATE RequestImpl(ProtocolHandler* handler,
+ LIBWEBSERV_PRIVATE RequestImpl(DBusProtocolHandler* handler,
const std::string& url,
const std::string& method);
- ProtocolHandler* handler_{nullptr};
+ DBusProtocolHandler* handler_{nullptr};
base::File raw_data_fd_;
bool last_posted_data_was_file_{false};
diff --git a/libwebserv/response.cc b/libwebserv/response.cc
index bedc10c..c87a6c0 100644
--- a/libwebserv/response.cc
+++ b/libwebserv/response.cc
@@ -23,11 +23,11 @@
#include <brillo/mime_utils.h>
#include <brillo/streams/memory_stream.h>
#include <brillo/strings/string_utils.h>
-#include <libwebserv/protocol_handler.h>
+#include <libwebserv/dbus_protocol_handler.h>
namespace libwebserv {
-ResponseImpl::ResponseImpl(ProtocolHandler* handler,
+ResponseImpl::ResponseImpl(DBusProtocolHandler* handler,
const std::string& request_id)
: handler_{handler}, request_id_{request_id} {
}
diff --git a/libwebserv/response_impl.h b/libwebserv/response_impl.h
index 81b4faa..c66ea57 100644
--- a/libwebserv/response_impl.h
+++ b/libwebserv/response_impl.h
@@ -25,6 +25,8 @@
namespace libwebserv {
+class DBusProtocolHandler;
+
// Implementation of the Response interface.
class ResponseImpl final : public Response {
public:
@@ -49,14 +51,14 @@
void ReplyWithErrorNotFound() override;
private:
- friend class ProtocolHandler;
+ friend class DBusProtocolHandler;
- LIBWEBSERV_PRIVATE ResponseImpl(ProtocolHandler* handler,
+ LIBWEBSERV_PRIVATE ResponseImpl(DBusProtocolHandler* handler,
const std::string& request_id);
LIBWEBSERV_PRIVATE void SendResponse();
- ProtocolHandler* handler_{nullptr};
+ DBusProtocolHandler* handler_{nullptr};
std::string request_id_;
int status_code_{0};
brillo::StreamPtr data_stream_;