webserver: Move web server functionality into webservd
Moved libmicrohttpd from libwebserv to webservd daemon. Added the
D-Bus interface between the web server daemon and client library.
Updated privetd to use the new interface.
BUG=brillo:10
TEST=`FEATURES=test emerge-link libwebserv privetd`
CQ-DEPEND=CL:245780,CL:245118,CL:*195757
Change-Id: I26bfab64c6a0fd9460a47fd3fa9205c89abb943a
Reviewed-on: https://chromium-review.googlesource.com/245980
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/libwebserv/connection.cc b/libwebserv/connection.cc
deleted file mode 100644
index 4e1ae8f..0000000
--- a/libwebserv/connection.cc
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2014 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 <libwebserv/connection.h>
-
-#include <algorithm>
-#include <vector>
-
-#include <base/bind.h>
-#include <base/location.h>
-#include <base/logging.h>
-#include <base/task_runner.h>
-#include <chromeos/http/http_request.h>
-#include <libwebserv/request.h>
-#include <libwebserv/request_handler_interface.h>
-#include <libwebserv/response.h>
-#include <libwebserv/server.h>
-#include <microhttpd.h>
-
-namespace libwebserv {
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Connection class.
-class ConnectionHelper {
- public:
- static int PostDataIterator(void* cls,
- MHD_ValueKind kind,
- const char* key,
- const char* filename,
- const char* content_type,
- const char* transfer_encoding,
- const char* data,
- uint64_t off,
- size_t size) {
- Connection* server_connection = reinterpret_cast<Connection*>(cls);
- if (!server_connection->ProcessPostData(
- key, filename, content_type, transfer_encoding, data, off, size)) {
- return MHD_NO;
- }
- return MHD_YES;
- }
-};
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Request class.
-class RequestHelper {
- public:
- static int ValueCallback(void* cls,
- MHD_ValueKind kind,
- const char* key,
- const char* value) {
- auto self = reinterpret_cast<Request*>(cls);
- std::string data;
- if (value)
- data = value;
- if (kind == MHD_HEADER_KIND) {
- self->headers_.emplace(Request::GetCanonicalHeaderName(key), data);
- } else if (kind == MHD_COOKIE_KIND) {
- // TODO(avakulenko): add support for cookies...
- } else if (kind == MHD_POSTDATA_KIND) {
- self->post_data_.emplace(key, data);
- } else if (kind == MHD_GET_ARGUMENT_KIND) {
- self->get_data_.emplace(key, data);
- }
- return MHD_YES;
- }
-};
-
-Connection::Connection(const scoped_refptr<base::TaskRunner>& task_runner,
- MHD_Connection* connection,
- RequestHandlerInterface* handler)
- : task_runner_(task_runner),
- raw_connection_(connection),
- handler_(handler) {
-}
-
-Connection::~Connection() {
- if (post_processor_)
- MHD_destroy_post_processor(post_processor_);
-}
-
-scoped_refptr<Connection> Connection::Create(Server* server,
- const std::string& url,
- const std::string& method,
- MHD_Connection* connection,
- RequestHandlerInterface* handler) {
- scoped_refptr<Connection> result(
- new Connection(server->task_runner_, connection, handler));
- VLOG(1) << "Incoming HTTP connection (" << result.get() << ")."
- << " Method='" << method << "', URL='" << url << "'";
- result->post_processor_ = MHD_create_post_processor(
- connection, 1024, &ConnectionHelper::PostDataIterator, result.get());
- result->request_ = Request::Create(url, method);
- result->response_ = Response::Create(result);
- return result;
-}
-
-bool Connection::BeginRequestData() {
- MHD_get_connection_values(raw_connection_, MHD_HEADER_KIND,
- &RequestHelper::ValueCallback, request_.get());
- MHD_get_connection_values(raw_connection_, MHD_COOKIE_KIND,
- &RequestHelper::ValueCallback, request_.get());
- MHD_get_connection_values(raw_connection_, MHD_POSTDATA_KIND,
- &RequestHelper::ValueCallback, request_.get());
- MHD_get_connection_values(raw_connection_, MHD_GET_ARGUMENT_KIND,
- &RequestHelper::ValueCallback, request_.get());
- return true;
-}
-
-bool Connection::AddRequestData(const void* data, size_t size) {
- if (!post_processor_)
- return request_->AddRawRequestData(data, size);
- return MHD_post_process(post_processor_,
- static_cast<const char*>(data), size) == MHD_YES;
-}
-
-void Connection::EndRequestData() {
- if (state_ == State::kIdle) {
- state_ = State::kRequestSent;
- // libmicrohttpd calls handlers on its own thread.
- // Redirect this to the main IO thread of the server.
- task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RequestHandlerInterface::HandleRequest,
- base::Unretained(handler_), base::Passed(&request_),
- base::Passed(&response_)));
- } else if (state_ == State::kResponseReceived) {
- VLOG(1) << "Sending HTTP response for connection (" << this
- << "): " << response_status_code_
- << ", data size = " << response_data_.size();
- MHD_Response* resp = MHD_create_response_from_buffer(
- response_data_.size(), response_data_.data(), MHD_RESPMEM_PERSISTENT);
- for (const auto& pair : response_headers_) {
- MHD_add_response_header(resp, pair.first.c_str(), pair.second.c_str());
- }
- CHECK_EQ(MHD_YES,
- MHD_queue_response(raw_connection_, response_status_code_, resp))
- << "Failed to queue response";
- MHD_destroy_response(resp); // |resp| is ref-counted.
- state_ = State::kDone;
- }
-}
-
-bool Connection::ProcessPostData(const char* key,
- const char* filename,
- const char* content_type,
- const char* transfer_encoding,
- const char* data,
- uint64_t off,
- size_t size) {
- if (off == 0)
- return request_->AddPostFieldData(key, filename, content_type,
- transfer_encoding, data, size);
- return request_->AppendPostFieldData(key, data, size);
-}
-
-} // namespace libwebserv
diff --git a/libwebserv/connection.h b/libwebserv/connection.h
deleted file mode 100644
index 73bb816..0000000
--- a/libwebserv/connection.h
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2014 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.
-
-#ifndef WEBSERVER_LIBWEBSERV_CONNECTION_H_
-#define WEBSERVER_LIBWEBSERV_CONNECTION_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/memory/scoped_ptr.h>
-#include <chromeos/errors/error.h>
-#include <libwebserv/export.h>
-
-struct MHD_Connection;
-struct MHD_PostProcessor;
-
-namespace base {
-class TaskRunner;
-} // namespace base
-
-namespace libwebserv {
-
-class Request;
-class RequestHandlerInterface;
-class Response;
-class Server;
-
-// A wrapper class around low-level HTTP connection.
-class LIBWEBSERV_EXPORT Connection final : public base::RefCounted<Connection> {
- public:
- ~Connection();
-
- // Factory creator method. Creates an instance of the connection and
- // initializes some complex data members. This is safer and easier to
- // report possible failures than reply on just the constructor.
- static scoped_refptr<Connection> Create(Server* server,
- const std::string& url,
- const std::string& method,
- MHD_Connection* connection,
- RequestHandlerInterface* handler);
-
- private:
- LIBWEBSERV_PRIVATE Connection(
- const scoped_refptr<base::TaskRunner>& task_runner,
- MHD_Connection* connection,
- RequestHandlerInterface* handler);
-
- // Helper callback methods used by Server's ConnectionHandler to transfer
- // request headers and data to the Connection's Request object.
- LIBWEBSERV_PRIVATE bool BeginRequestData();
- LIBWEBSERV_PRIVATE bool AddRequestData(const void* data, size_t size);
- LIBWEBSERV_PRIVATE void EndRequestData();
-
- // Callback for libmicrohttpd's PostProcessor.
- LIBWEBSERV_PRIVATE bool ProcessPostData(const char* key,
- const char* filename,
- const char* content_type,
- const char* transfer_encoding,
- const char* data,
- uint64_t off,
- size_t size);
-
- scoped_refptr<base::TaskRunner> task_runner_;
- MHD_Connection* raw_connection_{nullptr};
- RequestHandlerInterface* handler_{nullptr};
- MHD_PostProcessor* post_processor_{nullptr};
- scoped_ptr<Request> request_;
- scoped_ptr<Response> response_;
-
- enum class State { kIdle, kRequestSent, kResponseReceived, kDone };
- State state_{State::kIdle};
- int response_status_code_{0};
- std::vector<uint8_t> response_data_;
- std::multimap<std::string, std::string> response_headers_;
-
- friend class ConnectionHelper;
- friend class Request;
- friend class Response;
- friend class ServerHelper;
- DISALLOW_COPY_AND_ASSIGN(Connection);
-};
-
-} // namespace libwebserv
-
-#endif // WEBSERVER_LIBWEBSERV_CONNECTION_H_
diff --git a/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml
new file mode 100644
index 0000000..5eab71d
--- /dev/null
+++ b/libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/WebServer/RequestHandler"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.chromium.WebServer.RequestHandler">
+ <!-- Methods -->
+ <method name="ProcessRequest">
+ <tp:docstring>
+ Sends a new HTTP request to the handler.
+ Parameters:
+ - request_info - request metadata. Due to limitation of base::Callback
+ on the number of parameters, we have to collapse a couple
+ of distinct parameters into a larger struct, containing:
+ - (s) protocol_handler_id - ID of the protocol handler.
+ - (s) request_handler_id - ID of the registered request
+ handler.
+ - (s) request_id - unique ID of this request within the
+ protocol handler.
+ - (s) url - The request URL (e.g. "/path/object").
+ - (s) method - Request method (e.g. "GET", "POST", ...).
+ - headers - Request headers (key-value pairs)
+ - params - an array of request parameters which could be either
+ URL params (specified after "?" in the request URL), or
+ form fields in a POST request. Elements have the following
+ structure:
+ - (b) true = form field, false = URL param
+ - (s) field_name
+ - (s) field_value
+ - files - Information about uploaded files.
+ The data is an array of FileInfo structures containing the
+ following fields:
+ - (i) file_id
+ - (s) field_name
+ - (s) file_name
+ - (s) content_type
+ - (s) transfer_encoding
+ The actual contents of the file is obtained by calling
+ GetFileData() on the request object
+ - body - Raw unparsed request data. Could be empty for POST requests
+ that have form data/uploaded files already parsed into
+ form_fields/files parameters.
+ </tp:docstring>
+ <arg name="request_info" type="(sssss)" direction="in"/>
+ <arg name="headers" type="a(ss)" direction="in"/>
+ <arg name="params" type="a(bss)" direction="in"/>
+ <arg name="files" type="a(issss)" direction="in"/>
+ <arg name="body" type="ay" direction="in"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+ </method>
+ </interface>
+</node>
diff --git a/libwebserv/protocol_handler.cc b/libwebserv/protocol_handler.cc
new file mode 100644
index 0000000..9a10acf
--- /dev/null
+++ b/libwebserv/protocol_handler.cc
@@ -0,0 +1,207 @@
+// Copyright 2014 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 "webserver/libwebserv/protocol_handler.h"
+
+#include <tuple>
+
+#include <base/logging.h>
+#include <chromeos/map_utils.h>
+
+#include "libwebserv/org.chromium.WebServer.RequestHandler.h"
+#include "webservd/dbus-proxies.h"
+#include "webserver/libwebserv/request.h"
+#include "webserver/libwebserv/request_handler_callback.h"
+#include "webserver/libwebserv/response.h"
+#include "webserver/libwebserv/server.h"
+
+namespace libwebserv {
+
+namespace {
+
+// A dummy callback for async D-Bus errors.
+void IgnoreError(chromeos::Error* error) {}
+
+} // anonymous namespace
+
+const char ProtocolHandler::kHttp[] = "http";
+const char ProtocolHandler::kHttps[] = "https";
+
+ProtocolHandler::ProtocolHandler(const std::string& id, Server* server)
+ : id_{id}, 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 = chromeos::GetMapKeys(request_handlers_);
+ for (int handler_id : handler_ids) {
+ RemoveHandler(handler_id);
+ }
+}
+
+std::string ProtocolHandler::GetID() const {
+ return id_;
+}
+
+uint16_t ProtocolHandler::GetPort() const {
+ CHECK(IsConnected());
+ return proxy_->port();
+}
+
+std::string ProtocolHandler::GetProtocol() const {
+ CHECK(IsConnected());
+ return proxy_->protocol();
+}
+
+chromeos::Blob ProtocolHandler::GetCertificateFingerprint() const {
+ CHECK(IsConnected());
+ return proxy_->certificate_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::string{},
+ std::move(handler)});
+ if (proxy_) {
+ proxy_->AddRequestHandlerAsync(
+ url,
+ method,
+ server_->service_name_,
+ base::Bind(&ProtocolHandler::AddHandlerSuccess,
+ weak_ptr_factory_.GetWeakPtr(),
+ last_handler_id_),
+ 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;
+
+ if (proxy_) {
+ proxy_->RemoveRequestHandlerAsync(
+ p->second.remote_handler_id,
+ base::Bind(&base::DoNothing),
+ base::Bind(&IgnoreError));
+ }
+
+ request_handlers_.erase(p);
+ return true;
+}
+
+void ProtocolHandler::Connect(
+ org::chromium::WebServer::ProtocolHandlerProxy* proxy) {
+ proxy_ = 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),
+ base::Bind(&ProtocolHandler::AddHandlerError,
+ weak_ptr_factory_.GetWeakPtr(),
+ pair.first));
+ }
+}
+
+void ProtocolHandler::Disconnect() {
+ proxy_ = nullptr;
+ remote_handler_id_map_.clear();
+}
+
+void ProtocolHandler::AddHandlerSuccess(int handler_id,
+ const std::string& remote_handler_id) {
+ auto p = request_handlers_.find(handler_id);
+ CHECK(p != request_handlers_.end());
+ p->second.remote_handler_id = remote_handler_id;
+
+ remote_handler_id_map_.emplace(remote_handler_id, handler_id);
+}
+
+void ProtocolHandler::AddHandlerError(int handler_id, chromeos::Error* error) {
+ // Nothing to do at the moment.
+}
+
+bool ProtocolHandler::ProcessRequest(const std::string& remote_handler_id,
+ const std::string& request_id,
+ std::unique_ptr<Request> request,
+ chromeos::ErrorPtr* error) {
+ auto id_iter = remote_handler_id_map_.find(remote_handler_id);
+ if (id_iter == remote_handler_id_map_.end()) {
+ chromeos::Error::AddToPrintf(error, FROM_HERE,
+ chromeos::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()) {
+ chromeos::Error::AddToPrintf(error, FROM_HERE,
+ chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED,
+ "Handler # %d is no longer available",
+ id_iter->second);
+ return false;
+ }
+ handler_iter->second.handler->HandleRequest(
+ scoped_ptr<Request>(request.release()),
+ scoped_ptr<Response>(new Response{this, request_id}));
+ return true;
+}
+
+void ProtocolHandler::CompleteRequest(
+ const std::string& request_id,
+ int status_code,
+ const std::multimap<std::string, std::string>& headers,
+ const std::vector<uint8_t>& data) {
+ if (!proxy_) {
+ LOG(WARNING) << "Completing a request after the handler proxy is removed";
+ 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);
+
+ proxy_->CompleteRequestAsync(request_id, status_code, header_list, data,
+ base::Bind(&base::DoNothing),
+ base::Bind([](chromeos::Error*) {}));
+}
+
+void ProtocolHandler::GetFileData(
+ const std::string& request_id,
+ int file_id,
+ const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback) {
+ CHECK(proxy_);
+ proxy_->GetRequestFileDataAsync(
+ request_id, file_id, success_callback, error_callback);
+}
+
+
+} // namespace libwebserv
diff --git a/libwebserv/protocol_handler.h b/libwebserv/protocol_handler.h
new file mode 100644
index 0000000..9ba7d11
--- /dev/null
+++ b/libwebserv/protocol_handler.h
@@ -0,0 +1,187 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
+#define WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/callback_forward.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <chromeos/errors/error.h>
+#include <chromeos/secure_blob.h>
+
+#include <libwebserv/export.h>
+#include <libwebserv/request_handler_interface.h>
+
+namespace org {
+namespace chromium {
+namespace WebServer {
+
+class ProtocolHandlerProxy;
+
+} // namespace WebServer
+} // namespace chromium
+} // namespace org
+
+namespace libwebserv {
+
+class Server;
+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 {
+ public:
+ explicit ProtocolHandler(const std::string& id, Server* server);
+ ~ProtocolHandler();
+
+ // 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 proxy_ != nullptr; }
+
+ // Handler's unique ID. This is generally a GUID string, except for the
+ // default HTTP and HTTPS handlers which have IDs of "http" and "https"
+ // respectively.
+ std::string GetID() const;
+
+ // Returns the port the handler is bound to.
+ // ATTENTION: The handler must be connected to the web server before this
+ // method can be called.
+ uint16_t GetPort() const;
+
+ // Returns the transport protocol that is served by this handler.
+ // Can be either "http" or "https".
+ // ATTENTION: The handler must be connected to the web server before this
+ // method can be called.
+ std::string GetProtocol() const;
+
+ // Returns a SHA-256 fingerprint of HTTPS certificate used. Returns an empty
+ // byte buffer if this handler does not serve the HTTPS protocol.
+ // ATTENTION: The handler must be connected to the web server before this
+ // method can be called.
+ chromeos::Blob GetCertificateFingerprint() const;
+
+ // Adds a request handler for given |url|. If the |url| ends with a '/', this
+ // makes the handler respond to any URL beneath this path.
+ // Note that it is not possible to add a specific handler just for the root
+ // path "/". Doing so means "respond to any URL".
+ // |method| is optional request method verb, such as "GET" or "POST".
+ // If |method| is empty, the handler responds to any request verb.
+ // If there are more than one handler for a given request, the most specific
+ // match is chosen. For example, if there are the following handlers provided:
+ // - A["/foo/", ""]
+ // - B["/foo/bar", "GET"]
+ // - C["/foo/bar", ""]
+ // Here is what handlers are called when making certain requests:
+ // - GET("/foo/bar") => B[]
+ // - POST("/foo/bar") => C[]
+ // - PUT("/foo/bar") => C[]
+ // - GET("/foo/baz") => A[]
+ // - GET("/foo") => 404 Not Found
+ // This functions returns a handler ID which can be used later to remove
+ // the handler.
+ //
+ // The handler registration information is stored inside ProtocolHandler and
+ // is used to register the handlers with the web server daemon when it becomes
+ // 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);
+
+ // Similar to AddHandler() above but the handler is just a callback function.
+ int AddHandlerCallback(
+ const std::string& url,
+ const std::string& method,
+ const base::Callback<RequestHandlerInterface::HandlerSignature>&
+ handler_callback);
+
+ // 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);
+
+ static const char kHttp[];
+ static const char kHttps[];
+
+ private:
+ friend class FileInfo;
+ friend class Server;
+ friend class Response;
+
+ struct LIBWEBSERV_PRIVATE HandlerMapEntry {
+ std::string url;
+ std::string method;
+ std::string remote_handler_id;
+ std::unique_ptr<RequestHandlerInterface> handler;
+ };
+
+ // Called by the Server class when the D-Bus proxy object gets connected
+ // to the web server daemon.
+
+ LIBWEBSERV_PRIVATE void Connect(
+ org::chromium::WebServer::ProtocolHandlerProxy* proxy);
+ // Called by the Server class when the D-Bus proxy object gets disconnected
+ // from the web server daemon.
+ LIBWEBSERV_PRIVATE void Disconnect();
+
+ // Asynchronous callbacks to handle successful or failed request handler
+ // registration over D-Bus.
+ LIBWEBSERV_PRIVATE void AddHandlerSuccess(
+ int handler_id, const std::string& remote_handler_id);
+ LIBWEBSERV_PRIVATE void AddHandlerError(int handler_id,
+ chromeos::Error* error);
+
+ // Called by Server when an incoming request is dispatched.
+ LIBWEBSERV_PRIVATE bool ProcessRequest(const std::string& remote_handler_id,
+ const std::string& request_id,
+ std::unique_ptr<Request> request,
+ chromeos::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,
+ const std::vector<uint8_t>& data);
+
+ // 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(const std::vector<uint8_t>&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback);
+
+ // Protocol Handler unique ID.
+ std::string id_;
+ // Back reference to the server object.
+ Server* 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_;
+ // 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_;
+ // The counter to generate new handler IDs.
+ int last_handler_id_{0};
+ // Remove D-Bus proxy for the server protocol handler object.
+ org::chromium::WebServer::ProtocolHandlerProxy* proxy_{nullptr};
+
+ base::WeakPtrFactory<ProtocolHandler> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(ProtocolHandler);
+};
+
+} // namespace libwebserv
+
+#endif // WEBSERVER_LIBWEBSERV_PROTOCOL_HANDLER_H_
diff --git a/libwebserv/request.cc b/libwebserv/request.cc
index 37ea1c1..b51aa94 100644
--- a/libwebserv/request.cc
+++ b/libwebserv/request.cc
@@ -4,86 +4,49 @@
#include <libwebserv/request.h>
-#include <libwebserv/connection.h>
+#include <base/callback.h>
+#include <chromeos/http/http_utils.h>
+
+#include <libwebserv/protocol_handler.h>
namespace libwebserv {
-FileInfo::FileInfo(const std::string& file_name,
+FileInfo::FileInfo(ProtocolHandler* handler,
+ int file_id,
+ const std::string& request_id,
+ const std::string& file_name,
const std::string& content_type,
const std::string& transfer_encoding)
- : file_name_(file_name),
+ : handler_{handler},
+ file_id_{file_id},
+ request_id_{request_id},
+ file_name_(file_name),
content_type_(content_type),
transfer_encoding_(transfer_encoding) {
}
-const std::vector<uint8_t>& FileInfo::GetData() const {
- return data_;
+void FileInfo::GetData(
+ const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback) {
+ handler_->GetFileData(request_id_,
+ file_id_,
+ success_callback,
+ error_callback);
}
-Request::Request(const std::string& url, const std::string& method)
- : url_{url}, method_{method} {
+Request::Request(ProtocolHandler* handler,
+ const std::string& url,
+ const std::string& method)
+ : handler_{handler}, url_{url}, method_{method} {
}
Request::~Request() {
}
-scoped_ptr<Request> Request::Create(const std::string& url,
- const std::string& method) {
- // Can't use make_shared here since Request::Request is private.
- return scoped_ptr<Request>(new Request(url, method));
-}
-
const std::vector<uint8_t>& Request::GetData() const {
return raw_data_;
}
-bool Request::AddRawRequestData(const void* data, size_t size) {
- const uint8_t* byte_data_ = static_cast<const uint8_t*>(data);
- raw_data_.insert(raw_data_.end(), byte_data_, byte_data_ + size);
- return true;
-}
-
-bool Request::AddPostFieldData(const char* key,
- const char* filename,
- const char* content_type,
- const char* transfer_encoding,
- const char* data,
- size_t size) {
- if (filename) {
- std::unique_ptr<FileInfo> file_info{
- new FileInfo{filename, content_type ? content_type : "",
- transfer_encoding ? transfer_encoding : ""}};
- file_info->data_.assign(data, data + size);
- file_info_.emplace(key, std::move(file_info));
- last_posted_data_was_file_ = true;
- return true;
- }
- std::string value{data, size};
- post_data_.emplace(key, value);
- last_posted_data_was_file_ = false;
- return true;
-}
-
-bool Request::AppendPostFieldData(const char* key,
- const char* data,
- size_t size) {
- if (last_posted_data_was_file_) {
- auto file_pair = file_info_.equal_range(key);
- if (file_pair.first == file_info_.end())
- return false;
- FileInfo* file_info = file_pair.second->second.get();
- file_info->data_.insert(file_info->data_.end(), data, data + size);
- return true;
- }
-
- auto pair = post_data_.equal_range(key);
- if (pair.first == post_data_.end())
- return false;
- --pair.second; // Get the last form field with this name/key.
- pair.second->second.append(data, size);
- return true;
-}
-
std::vector<PairOfStrings> Request::GetFormData() const {
auto data = GetFormDataGet();
auto post_data = GetFormDataPost();
@@ -162,30 +125,19 @@
std::vector<std::string> Request::GetHeader(const std::string& name) const {
std::vector<std::string> data;
- auto pair = headers_.equal_range(GetCanonicalHeaderName(name));
- while (pair.first != pair.second) {
- data.push_back(pair.first->second);
- ++pair.first;
+ auto range =
+ headers_.equal_range(chromeos::http::GetCanonicalHeaderName(name));
+ while (range.first != range.second) {
+ data.push_back(range.first->second);
+ ++range.first;
}
return data;
}
-std::string Request::GetCanonicalHeaderName(const std::string& name) {
- std::string canonical_name = name;
- bool word_begin = true;
- for (char& c : canonical_name) {
- if (c == '-') {
- word_begin = true;
- } else {
- if (word_begin) {
- c = toupper(c);
- } else {
- c = tolower(c);
- }
- word_begin = false;
- }
- }
- return canonical_name;
+std::string Request::GetFirstHeader(const std::string& name) const {
+ auto p = headers_.find(chromeos::http::GetCanonicalHeaderName(name));
+ return (p != headers_.end()) ? p->second : std::string{};
}
+
} // namespace libwebserv
diff --git a/libwebserv/request.h b/libwebserv/request.h
index 6899b3d..f75622b 100644
--- a/libwebserv/request.h
+++ b/libwebserv/request.h
@@ -11,36 +11,50 @@
#include <utility>
#include <vector>
+#include <base/callback_forward.h>
#include <base/macros.h>
#include <base/memory/ref_counted.h>
#include <base/memory/scoped_ptr.h>
+#include <chromeos/errors/error.h>
#include <libwebserv/export.h>
struct MHD_Connection;
namespace libwebserv {
-class Connection;
+class ProtocolHandler;
+
using PairOfStrings = std::pair<std::string, std::string>;
+// This class represents the file information about a file uploaded via
+// POST request using multipart/form-data request.
class LIBWEBSERV_EXPORT FileInfo final {
public:
- FileInfo(const std::string& file_name,
- const std::string& content_type,
- const std::string& transfer_encoding);
-
- const std::vector<uint8_t>& GetData() const;
const std::string& GetFileName() const { return file_name_; }
const std::string& GetContentType() const { return content_type_; }
const std::string& GetTransferEncoding() const { return transfer_encoding_; }
+ void GetData(
+ const base::Callback<void(const std::vector<uint8_t>&)>& success_callback,
+ const base::Callback<void(chromeos::Error*)>& error_callback);
private:
+ friend class Server;
+
+ LIBWEBSERV_PRIVATE FileInfo(ProtocolHandler* 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};
+ int file_id_{0};
+ std::string request_id_;
std::string file_name_;
std::string content_type_;
std::string transfer_encoding_;
std::vector<uint8_t> data_;
- friend class Request;
DISALLOW_COPY_AND_ASSIGN(FileInfo);
};
@@ -49,10 +63,6 @@
public:
~Request();
- // Factory constructor method.
- static scoped_ptr<Request> Create(const std::string& url,
- const std::string& method);
-
// Gets the request body data stream. Note that the stream is available
// only for requests that provided data and if this data is not already
// pre-parsed by the server (e.g. "application/x-www-form-urlencoded" and
@@ -100,32 +110,22 @@
// Returns a list of key-value pairs for all the request headers.
std::vector<PairOfStrings> GetHeaders() const;
- // Returns the value(s) of a request head of given |name|.
+ // Returns the value(s) of a request header of given |name|.
std::vector<std::string> GetHeader(const std::string& name) const;
+ // Returns the value of a request header of given |name|. If there are more
+ // than one header with this name, the value of the first header is returned.
+ // An empty string is returned if the header does not exist in the request.
+ std::string GetFirstHeader(const std::string& name) const;
+
private:
- LIBWEBSERV_PRIVATE Request(const std::string& url, const std::string& method);
+ friend class Server;
- // Helper methods for processing request data coming from the raw HTTP
- // connection.
- // These methods parse the request headers and data so they can be accessed
- // by request handlers later.
- LIBWEBSERV_PRIVATE bool AddRawRequestData(const void* data, size_t size);
- LIBWEBSERV_PRIVATE bool AddPostFieldData(const char* key,
- const char* filename,
- const char* content_type,
- const char* transfer_encoding,
- const char* data,
- size_t size);
- LIBWEBSERV_PRIVATE bool AppendPostFieldData(const char* key,
- const char* data,
- size_t size);
- // Converts a request header name to canonical form (lowercase with uppercase
- // first letter and each letter after a hyphen ('-')).
- // "content-TYPE" will be converted to "Content-Type".
- LIBWEBSERV_PRIVATE static std::string GetCanonicalHeaderName(
- const std::string& name);
+ LIBWEBSERV_PRIVATE Request(ProtocolHandler* handler,
+ const std::string& url,
+ const std::string& method);
+ ProtocolHandler* handler_{nullptr};
std::string url_;
std::string method_;
std::vector<uint8_t> raw_data_;
@@ -136,8 +136,6 @@
std::multimap<std::string, std::unique_ptr<FileInfo>> file_info_;
std::multimap<std::string, std::string> headers_;
- friend class Connection;
- friend class RequestHelper;
DISALLOW_COPY_AND_ASSIGN(Request);
};
diff --git a/libwebserv/response.cc b/libwebserv/response.cc
index c54747f..fc46e87 100644
--- a/libwebserv/response.cc
+++ b/libwebserv/response.cc
@@ -12,13 +12,12 @@
#include <chromeos/http/http_request.h>
#include <chromeos/mime_utils.h>
#include <chromeos/strings/string_utils.h>
-#include <libwebserv/connection.h>
-#include <microhttpd.h>
+#include <libwebserv/protocol_handler.h>
namespace libwebserv {
-Response::Response(const scoped_refptr<Connection>& connection)
- : connection_(connection) {
+Response::Response(ProtocolHandler* handler, const std::string& request_id)
+ : handler_{handler}, request_id_{request_id} {
}
Response::~Response() {
@@ -28,11 +27,6 @@
}
}
-scoped_ptr<Response> Response::Create(
- const scoped_refptr<Connection>& connection) {
- return scoped_ptr<Response>(new Response(connection));
-}
-
void Response::AddHeader(const std::string& header_name,
const std::string& value) {
headers_.emplace(header_name, value);
@@ -99,12 +93,7 @@
void Response::SendResponse() {
CHECK(!reply_sent_) << "Response already sent";
reply_sent_ = true;
- CHECK(connection_->state_ == Connection::State::kRequestSent)
- << "Unexpected connection state";
- connection_->response_status_code_ = status_code_;
- connection_->response_data_ = std::move(data_);
- connection_->response_headers_ = std::move(headers_);
- connection_->state_ = Connection::State::kResponseReceived;
+ handler_->CompleteRequest(request_id_, status_code_, headers_, data_);
}
} // namespace libwebserv
diff --git a/libwebserv/response.h b/libwebserv/response.h
index 05465e3..616264d 100644
--- a/libwebserv/response.h
+++ b/libwebserv/response.h
@@ -12,31 +12,22 @@
#include <vector>
#include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/memory/scoped_ptr.h>
#include <libwebserv/export.h>
namespace base {
class Value;
} // namespace base
-struct MHD_Connection;
-
namespace libwebserv {
-class Connection;
+class ProtocolHandler;
// Response class is a proxy for HTTP response used by the request handler
// to provide response HTTP headers and data.
-class LIBWEBSERV_EXPORT Response
- : public std::enable_shared_from_this<Response> {
+class LIBWEBSERV_EXPORT Response final {
public:
~Response();
- // Factory constructor method.
- static scoped_ptr<Response> Create(
- const scoped_refptr<Connection>& connection);
-
// Adds a single HTTP response header to the response.
void AddHeader(const std::string& header_name, const std::string& value);
@@ -76,19 +67,20 @@
void ReplyWithErrorNotFound();
private:
- LIBWEBSERV_PRIVATE explicit Response(
- const scoped_refptr<Connection>& connection);
+ friend class ProtocolHandler;
+
+ LIBWEBSERV_PRIVATE Response(ProtocolHandler* handler,
+ const std::string& request_id);
LIBWEBSERV_PRIVATE void SendResponse();
- scoped_refptr<Connection> connection_;
+ ProtocolHandler* handler_{nullptr};
+ std::string request_id_;
int status_code_{0};
std::vector<uint8_t> data_;
std::multimap<std::string, std::string> headers_;
bool reply_sent_{false};
- friend class Connection;
- friend class ResponseHelper;
DISALLOW_COPY_AND_ASSIGN(Response);
};
diff --git a/libwebserv/server.cc b/libwebserv/server.cc
index 196f178..2bf7c7d 100644
--- a/libwebserv/server.cc
+++ b/libwebserv/server.cc
@@ -4,230 +4,201 @@
#include <libwebserv/server.h>
-#include <limits>
+#include <tuple>
#include <vector>
-#include <base/logging.h>
-#include <base/message_loop/message_loop_proxy.h>
-#include <libwebserv/connection.h>
+#include <libwebserv/protocol_handler.h>
#include <libwebserv/request.h>
-#include <libwebserv/request_handler_callback.h>
-#include <libwebserv/request_handler_interface.h>
-#include <libwebserv/response.h>
-#include <microhttpd.h>
+
+#include "libwebserv/org.chromium.WebServer.RequestHandler.h"
+#include "webservd/dbus-proxies.h"
namespace libwebserv {
-namespace {
-// Simple static request handler that just returns "404 Not Found" error.
-class PageNotFoundHandler : public RequestHandlerInterface {
+class Server::RequestHandler final
+ : public org::chromium::WebServer::RequestHandlerInterface {
public:
- void HandleRequest(scoped_ptr<Request> request,
- scoped_ptr<Response> response) override {
- response->ReplyWithErrorNotFound();
- }
+ explicit RequestHandler(Server* server) : server_{server} {}
+ bool ProcessRequest(
+ chromeos::ErrorPtr* error,
+ const std::tuple<std::string, std::string, std::string, std::string,
+ std::string>& in_request_info,
+ const std::vector<std::tuple<std::string, std::string>>& in_headers,
+ const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
+ const std::vector<std::tuple<int32_t, std::string, std::string,
+ std::string, std::string>>& in_files,
+ const std::vector<uint8_t>& in_body) override;
+
+ private:
+ Server* server_{nullptr};
+ DISALLOW_COPY_AND_ASSIGN(RequestHandler);
};
-} // anonymous namespace
-
-// Helper class to provide static callback methods to microhttpd library,
-// with the ability to access private methods of Server class.
-class ServerHelper {
- public:
- static int ConnectionHandler(void *cls,
- MHD_Connection* connection,
- const char* url,
- const char* method,
- const char* version,
- const char* upload_data,
- size_t* upload_data_size,
- void** con_cls) {
- Server* server = reinterpret_cast<Server*>(cls);
- if (nullptr == *con_cls) {
- RequestHandlerInterface* handler = server->FindHandler(url, method);
- if (!handler)
- return MHD_NO;
-
- auto server_connection =
- Connection::Create(server, url, method, connection, handler);
- if (!server_connection || !server_connection->BeginRequestData())
- return MHD_NO;
-
- *con_cls = server_connection.get();
- server_connection->AddRef();
- } else {
- Connection* connection = reinterpret_cast<Connection*>(*con_cls);
- if (*upload_data_size) {
- if (!connection->AddRequestData(upload_data, *upload_data_size))
- return MHD_NO;
- *upload_data_size = 0;
- } else {
- connection->EndRequestData();
- }
- }
- return MHD_YES;
+bool Server::RequestHandler::ProcessRequest(
+ chromeos::ErrorPtr* error,
+ const std::tuple<std::string, std::string, std::string, std::string,
+ std::string>& in_request_info,
+ const std::vector<std::tuple<std::string, std::string>>& in_headers,
+ const std::vector<std::tuple<bool, std::string, std::string>>& in_params,
+ const std::vector<std::tuple<int32_t, std::string, std::string,
+ std::string, std::string>>& in_files,
+ const std::vector<uint8_t>& in_body) {
+ std::string protocol_handler_id = std::get<0>(in_request_info);
+ std::string request_handler_id = std::get<1>(in_request_info);
+ 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 =
+ server_->GetProtocolHandler(protocol_handler_id);
+ if (!protocol_handler) {
+ chromeos::Error::AddToPrintf(error, FROM_HERE,
+ chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED,
+ "Unknown protocol handler '%s'",
+ protocol_handler_id.c_str());
+ return false;
+ }
+ std::unique_ptr<Request> request{new Request{protocol_handler, url, method}};
+ // Convert request data into format required by the Request object.
+ for (const auto& tuple : in_params) {
+ if (std::get<0>(tuple))
+ request->post_data_.emplace(std::get<1>(tuple), std::get<2>(tuple));
+ else
+ request->get_data_.emplace(std::get<1>(tuple), std::get<2>(tuple));
}
- static void RequestCompleted(void* cls,
- MHD_Connection* connection,
- void** con_cls,
- MHD_RequestTerminationCode toe) {
- reinterpret_cast<Connection*>(*con_cls)->Release();
- *con_cls = nullptr;
- }
-};
+ for (const auto& tuple : in_headers)
+ request->headers_.emplace(std::get<0>(tuple), std::get<1>(tuple));
-Server::Server() {}
+ for (const auto& tuple : in_files) {
+ request->file_info_.emplace(
+ std::get<1>(tuple), // field_name
+ std::unique_ptr<FileInfo>{new FileInfo{
+ protocol_handler,
+ std::get<0>(tuple), // file_id
+ request_id,
+ std::get<2>(tuple), // file_name
+ std::get<3>(tuple), // content_type
+ std::get<4>(tuple)}}); // transfer_encoding
+ }
+
+ request->raw_data_ = in_body;
+
+ return protocol_handler->ProcessRequest(request_handler_id,
+ request_id,
+ std::move(request),
+ error);
+}
+
+Server::Server()
+ : request_handler_{new RequestHandler{this}},
+ dbus_adaptor_{new org::chromium::WebServer::RequestHandlerAdaptor{
+ request_handler_.get()}} {}
Server::~Server() {
- Stop();
}
-bool Server::Start(uint16_t port) {
- return StartWithTLS(port, chromeos::SecureBlob{}, chromeos::Blob{});
+void Server::Connect(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb,
+ const base::Closure& on_server_online,
+ const base::Closure& on_server_offline) {
+ service_name_ = service_name;
+ dbus_object_.reset(new chromeos::dbus_utils::DBusObject{
+ nullptr, bus, dbus_adaptor_->GetObjectPath()});
+ dbus_adaptor_->RegisterWithDBusObject(dbus_object_.get());
+ dbus_object_->RegisterAsync(cb);
+ on_server_online_ = on_server_online;
+ on_server_offline_ = on_server_offline;
+ AddProtocolHandler(std::unique_ptr<ProtocolHandler>{
+ new ProtocolHandler{ProtocolHandler::kHttp, this}});
+ AddProtocolHandler(std::unique_ptr<ProtocolHandler>{
+ new ProtocolHandler{ProtocolHandler::kHttps, this}});
+ object_manager_.reset(new org::chromium::WebServer::ObjectManagerProxy{bus});
+ object_manager_->SetServerAddedCallback(
+ base::Bind(&Server::Online, base::Unretained(this)));
+ object_manager_->SetServerRemovedCallback(
+ base::Bind(&Server::Offline, base::Unretained(this)));
+ object_manager_->SetProtocolHandlerAddedCallback(
+ base::Bind(&Server::ProtocolHandlerAdded, base::Unretained(this)));
+ object_manager_->SetProtocolHandlerRemovedCallback(
+ base::Bind(&Server::ProtocolHandlerRemoved, base::Unretained(this)));
}
-bool Server::StartWithTLS(uint16_t port,
- const chromeos::SecureBlob& private_key,
- const chromeos::Blob& certificate) {
- if (server_) {
- LOG(ERROR) << "Web server is already running.";
- return false;
+void Server::Disconnect() {
+ object_manager_.reset();
+ on_server_offline_.Reset();
+ on_server_online_.Reset();
+ dbus_object_.reset();
+ protocol_handlers_.clear();
+}
+
+void Server::Online(org::chromium::WebServer::ServerProxy* server) {
+ proxy_ = server;
+ if (!on_server_online_.is_null())
+ on_server_online_.Run();
+}
+
+void Server::Offline(const dbus::ObjectPath& object_path) {
+ if (!on_server_offline_.is_null())
+ on_server_offline_.Run();
+ proxy_ = nullptr;
+}
+
+void Server::ProtocolHandlerAdded(
+ org::chromium::WebServer::ProtocolHandlerProxy* handler) {
+ protocol_handler_id_map_.emplace(handler->GetObjectPath(), handler->id());
+ ProtocolHandler* registered_handler = GetProtocolHandler(handler->id());
+ if (registered_handler) {
+ registered_handler->Connect(handler);
+ if (!on_protocol_handler_connected_.is_null())
+ on_protocol_handler_connected_.Run(registered_handler);
+ }
+}
+
+void Server::ProtocolHandlerRemoved(const dbus::ObjectPath& object_path) {
+ auto p = protocol_handler_id_map_.find(object_path);
+ if (p == protocol_handler_id_map_.end())
+ return;
+
+ ProtocolHandler* registered_handler = GetProtocolHandler(p->second);
+ if (registered_handler) {
+ if (!on_protocol_handler_disconnected_.is_null())
+ on_protocol_handler_disconnected_.Run(registered_handler);
+ registered_handler->Disconnect();
}
- // Either both keys and certificate must be specified or both muse be omitted.
- CHECK_EQ(private_key.empty(), certificate.empty());
-
- const bool use_tls = !private_key.empty();
-
- task_runner_ = base::MessageLoopProxy::current();
-
- LOG(INFO) << "Starting " << (use_tls ? "HTTPS" : "HTTP")
- << " Server on port: " << port;
- auto callback_addr =
- reinterpret_cast<intptr_t>(&ServerHelper::RequestCompleted);
- uint32_t flags = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG;
-
- std::vector<MHD_OptionItem> options{
- {MHD_OPTION_CONNECTION_LIMIT, 10, nullptr},
- {MHD_OPTION_CONNECTION_TIMEOUT, 60, nullptr},
- {MHD_OPTION_NOTIFY_COMPLETED, callback_addr, nullptr},
- };
-
- // libmicrohttpd expects both the key and certificate to be a zero-terminated
- // strings. Make sure they are terminated properly.
- chromeos::SecureBlob private_key_copy = private_key;
- chromeos::Blob certificate_copy = certificate;
- if (use_tls) {
- flags |= MHD_USE_SSL;
- private_key_copy.push_back(0);
- certificate_copy.push_back(0);
- options.push_back(
- MHD_OptionItem{MHD_OPTION_HTTPS_MEM_KEY, 0, private_key_copy.data()});
- options.push_back(
- MHD_OptionItem{MHD_OPTION_HTTPS_MEM_CERT, 0, certificate_copy.data()});
- }
-
- options.push_back(MHD_OptionItem{MHD_OPTION_END, 0, nullptr});
-
- server_ = MHD_start_daemon(flags, port, nullptr, nullptr,
- &ServerHelper::ConnectionHandler, this,
- MHD_OPTION_ARRAY, options.data(), MHD_OPTION_END);
- if (!server_) {
- LOG(ERROR) << "Failed to start the web server on port " << port;
- return false;
- }
- LOG(INFO) << "Server started";
- return true;
+ protocol_handler_id_map_.erase(p);
}
-bool Server::Stop() {
- if (server_) {
- LOG(INFO) << "Shutting down the web server...";
- MHD_stop_daemon(server_);
- server_ = nullptr;
- LOG(INFO) << "Server shutdown complete";
- }
- return true;
+ProtocolHandler* Server::GetProtocolHandler(const std::string& id) const {
+ auto p = protocol_handlers_.find(id);
+ return (p != protocol_handlers_.end()) ? p->second.get() : nullptr;
}
-int Server::AddHandler(const base::StringPiece& url,
- const base::StringPiece& method,
- std::unique_ptr<RequestHandlerInterface> handler) {
- request_handlers_.emplace(++last_handler_id_,
- HandlerMapEntry{url.as_string(),
- method.as_string(),
- std::move(handler)});
- return last_handler_id_;
+ProtocolHandler* Server::GetDefaultHttpHandler() const {
+ return GetProtocolHandler(ProtocolHandler::kHttp);
}
-int Server::AddHandlerCallback(
- const base::StringPiece& url,
- const base::StringPiece& method,
- const base::Callback<RequestHandlerInterface::HandlerSignature>&
- handler_callback) {
- std::unique_ptr<RequestHandlerInterface> handler{
- new RequestHandlerCallback{handler_callback}};
- return AddHandler(url, method, std::move(handler));
+ProtocolHandler* Server::GetDefaultHttpsHandler() const {
+ return GetProtocolHandler(ProtocolHandler::kHttps);
}
-bool Server::RemoveHandler(int handler_id) {
- return (request_handlers_.erase(handler_id) > 0);
+void Server::OnProtocolHandlerConnected(
+ const base::Callback<void(ProtocolHandler*)>& callback) {
+ on_protocol_handler_connected_ = callback;
}
-int Server::GetHandlerId(const base::StringPiece& url,
- const base::StringPiece& method) const {
- for (const auto& pair : request_handlers_) {
- if (pair.second.url == url && pair.second.method == method)
- return pair.first;
- }
- return 0;
+void Server::OnProtocolHandlerDisconnected(
+ const base::Callback<void(ProtocolHandler*)>& callback) {
+ on_protocol_handler_disconnected_ = callback;
}
-RequestHandlerInterface* Server::FindHandler(
- const base::StringPiece& url,
- const base::StringPiece& method) const {
- static PageNotFoundHandler page_not_found_handler;
-
- size_t score = std::numeric_limits<size_t>::max();
- RequestHandlerInterface* handler = nullptr;
- for (const auto& pair : request_handlers_) {
- std::string handler_url = pair.second.url;
- bool url_match = (handler_url == url);
- bool method_match = (pair.second.method == method);
-
- // Try exact match first. If everything matches, we have our handler.
- if (url_match && method_match)
- return pair.second.handler.get();
-
- // Calculate the current handler's similarity score. The lower the score
- // the better the match is...
- size_t current_score = 0;
- if (!url_match && !handler_url.empty() && handler_url.back() == '/') {
- if (url.starts_with(handler_url)) {
- url_match = true;
- // Use the difference in URL length as URL match quality proxy.
- // The longer URL, the more specific (better) match is.
- // Multiply by 2 to allow for extra score point for matching the method.
- current_score = (url.size() - handler_url.size()) * 2;
- }
- }
-
- if (!method_match && pair.second.method.empty()) {
- // If the handler didn't specify the method it handles, this means
- // it doesn't care. However this isn't the exact match, so bump
- // the score up one point.
- method_match = true;
- ++current_score;
- }
-
- if (url_match && method_match && current_score < score) {
- score = current_score;
- handler = pair.second.handler.get();
- }
- }
-
- return handler ? handler : &page_not_found_handler;
+void Server::AddProtocolHandler(std::unique_ptr<ProtocolHandler> handler) {
+ // Make sure a handler with this ID isn't already registered.
+ CHECK(protocol_handlers_.find(handler->GetID()) == protocol_handlers_.end());
+ protocol_handlers_.emplace(handler->GetID(), std::move(handler));
}
} // namespace libwebserv
diff --git a/libwebserv/server.h b/libwebserv/server.h
index 6221115..ba133df 100644
--- a/libwebserv/server.h
+++ b/libwebserv/server.h
@@ -9,110 +9,135 @@
#include <memory>
#include <string>
-#include <base/callback_forward.h>
#include <base/macros.h>
-#include <base/memory/ref_counted.h>
-#include <base/strings/string_piece.h>
-#include <chromeos/secure_blob.h>
+#include <dbus/bus.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/dbus_object.h>
#include <libwebserv/export.h>
#include <libwebserv/request_handler_interface.h>
-struct MHD_Daemon;
+namespace org {
+namespace chromium {
+namespace WebServer {
-namespace base {
-class TaskRunner;
-} // namespace base
+class ObjectManagerProxy;
+class ProtocolHandlerProxy;
+class RequestHandlerAdaptor;
+class ServerProxy;
+
+} // namespace WebServer
+} // namespace chromium
+} // namespace org
namespace libwebserv {
// Top-level wrapper class around HTTP server and provides an interface to
-// the web server. It allows the users to start the server and
-// register request handlers.
+// the web server.
class LIBWEBSERV_EXPORT Server final {
public:
Server();
~Server();
- // Starts the server and makes it listen to HTTP requests on the given port.
- //
- // Note that a valid message loop must exist before calling Start.
- bool Start(uint16_t port);
+ // Establish a connection to the system webserver.
+ // |service_name| is the well known D-Bus name of the client's process, used
+ // to expose a callback D-Bus object the web server calls back with incoming
+ // requests.
+ // |on_server_online| and |on_server_offline| will notify the caller when the
+ // server comes up and down.
+ // Note that we can Connect() even before the webserver attaches to D-Bus,
+ // and appropriate state will be built up when the webserver appears on D-Bus.
+ void Connect(
+ const scoped_refptr<dbus::Bus>& bus,
+ const std::string& service_name,
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb,
+ const base::Closure& on_server_online,
+ const base::Closure& on_server_offline);
- // Starts the server and makes it listen to HTTPS requests on the given port.
- //
- // Note that a valid message loop must exist before calling StartWithTLS.
- bool StartWithTLS(uint16_t port,
- const chromeos::SecureBlob& private_key,
- const chromeos::Blob& certificate);
+ // Disconnects from the web server and removes the library interface from
+ // D-Bus.
+ void Disconnect();
- // Stops the server.
- bool Stop();
+ // A helper method that returns the default handler for "http".
+ ProtocolHandler* GetDefaultHttpHandler() const;
- // Adds a request handler for given |url|. If the |url| ends with a '/', this
- // makes the handler respond to any URL beneath this path.
- // Note that it is not possible to add a specific handler just for the root
- // path "/". Doing so means "respond to any URL".
- // |method| is optional request method verb, such as "GET" or "POST".
- // If |method| is empty, the handler responds to any request verb.
- // If there are more than one handler for a given request, the most specific
- // match is chosen. For example, if there are the following handlers provided:
- // - A["/foo/", ""]
- // - B["/foo/bar", "GET"]
- // - C["/foo/bar", ""]
- // Here is what handlers are called when making certain requests:
- // - GET("/foo/bar") => B[]
- // - POST("/foo/bar") => C[]
- // - PUT("/foo/bar") => C[]
- // - GET("/foo/baz") => A[]
- // - GET("/foo") => 404 Not Found
- // This functions returns a handler ID which can be used later to remove
- // the handler.
- int AddHandler(const base::StringPiece& url,
- const base::StringPiece& method,
- std::unique_ptr<RequestHandlerInterface> handler);
+ // A helper method that returns the default handler for "https".
+ ProtocolHandler* GetDefaultHttpsHandler() const;
- // Similar to AddHandler() above but the handler is just a callback function.
- int AddHandlerCallback(
- const base::StringPiece& url,
- const base::StringPiece& method,
- const base::Callback<RequestHandlerInterface::HandlerSignature>&
- handler_callback);
+ // Returns true if the web server daemon is connected to DBus and our
+ // connection to it has been established.
+ bool IsConnected() const { return proxy_ != nullptr; }
- // 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);
+ // Set a user-callback to be invoked when a protocol handler is connect to the
+ // server daemon. Multiple calls to this method will overwrite previously set
+ // callbacks.
+ void OnProtocolHandlerConnected(
+ const base::Callback<void(ProtocolHandler*)>& callback);
- // Finds the handler ID given the exact match criteria. Note that using
- // this function could cause unexpected side effects if there are more than
- // one handler registered for given URL/Method parameters.
- // It is better to remember the handler ID from AddHandler() method and use
- // that ID to remove the handler, instead of looking the handler up using
- // URL/Method.
- int GetHandlerId(const base::StringPiece& url,
- const base::StringPiece& method) const;
-
- // Finds a handler for given URL/Method. This method does the criteria
- // matching and not exact match performed by GetHandlerId. This is the method
- // used to look up the handler for incoming HTTP requests.
- RequestHandlerInterface* FindHandler(
- const base::StringPiece& url,
- const base::StringPiece& method) const;
+ // Set a user-callback to be invoked when a protocol handler is disconnected
+ // from the server daemon (e.g. on shutdown). Multiple calls to this method
+ // will overwrite previously set callbacks.
+ void OnProtocolHandlerDisconnected(
+ const base::Callback<void(ProtocolHandler*)>& callback);
private:
- MHD_Daemon* server_ = nullptr;
- scoped_refptr<base::TaskRunner> task_runner_;
+ friend class ProtocolHandler;
+ class RequestHandler;
- struct LIBWEBSERV_PRIVATE HandlerMapEntry {
- std::string url;
- std::string method;
- std::unique_ptr<RequestHandlerInterface> handler;
- };
+ // Returns an existing protocol handler by ID. See documentation in
+ // ProtocolHandler about IDs and how they work with webservd.
+ LIBWEBSERV_PRIVATE ProtocolHandler* GetProtocolHandler(
+ const std::string& id) const;
- std::map<int, HandlerMapEntry> request_handlers_;
- int last_handler_id_{0};
+ // Handler invoked when a connection is established to web server daemon.
+ LIBWEBSERV_PRIVATE void Online(org::chromium::WebServer::ServerProxy* server);
- friend class ServerHelper;
- friend class Connection;
+ // Handler invoked when the web server daemon connection is dropped.
+ LIBWEBSERV_PRIVATE void Offline(const dbus::ObjectPath& object_path);
+
+ // Handler invoked when a new protocol handler D-Bus proxy object becomes
+ // available.
+ LIBWEBSERV_PRIVATE void ProtocolHandlerAdded(
+ org::chromium::WebServer::ProtocolHandlerProxy* handler);
+
+ // Handler invoked when a protocol handler D-Bus proxy object disappears.
+ LIBWEBSERV_PRIVATE void ProtocolHandlerRemoved(
+ const dbus::ObjectPath& object_path);
+
+ LIBWEBSERV_PRIVATE void AddProtocolHandler(
+ std::unique_ptr<ProtocolHandler> handler);
+
+ // 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_;
+ // D-Bus object adaptor for RequestHandlerInterface.
+ std::unique_ptr<org::chromium::WebServer::RequestHandlerAdaptor>
+ dbus_adaptor_;
+ // D-Bus object to handler registration of RequestHandlerInterface.
+ std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+
+ // A mapping of protocol handler IDs to the associated object.
+ // Handler IDs are either GUIDs or the two well-known handler IDs.
+ std::map<std::string, std::unique_ptr<ProtocolHandler>> protocol_handlers_;
+ // 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_;
+
+ // User-specified callbacks for server and protocol handler life-time events.
+ base::Closure on_server_online_;
+ base::Closure on_server_offline_;
+ base::Callback<void(ProtocolHandler*)> on_protocol_handler_connected_;
+ base::Callback<void(ProtocolHandler*)> on_protocol_handler_disconnected_;
+
+ // D-Bus object manager proxy that receives notification of web server
+ // daemon's D-Bus object creation and destruction.
+ std::unique_ptr<org::chromium::WebServer::ObjectManagerProxy> object_manager_;
+
+ // D-Bus proxy for the web server main object.
+ org::chromium::WebServer::ServerProxy* proxy_{nullptr};
+
+ // D-Bus service name used by the daemon hosting this object.
+ std::string service_name_;
+
DISALLOW_COPY_AND_ASSIGN(Server);
};
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml b/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml
deleted file mode 100644
index 8cd829f..0000000
--- a/webservd/dbus_bindings/org.chromium.WebServer.Manager.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<node name="/org/chromium/WebServer/Manager"
- xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
- <interface name="org.chromium.WebServer.Manager">
- <method name="Ping">
- <arg name="message" type="s" direction="out"/>
- <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
- </method>
- </interface>
-</node>
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml b/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml
new file mode 100644
index 0000000..438514a
--- /dev/null
+++ b/webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+ <interface name="org.chromium.WebServer.ProtocolHandler">
+ <!-- Methods -->
+ <method name="AddRequestHandler">
+ <tp:docstring>
+ Adds a handler for the given |url|, and optionally request |method|.
+ On success returns a handler ID.
+ </tp:docstring>
+ <arg name="url" type="s" direction="in"/>
+ <arg name="method" type="s" direction="in"/>
+ <arg name="service_name" type="s" direction="in"/>
+ <arg name="request_handler_id" type="s" direction="out"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+ </method>
+ <method name="RemoveRequestHandler">
+ <tp:docstring>
+ Removes a previously registered request handler.
+ The |handler_id| is the ID returned from AddHanlder() method.
+ </tp:docstring>
+ <arg name="request_handler_id" type="s" direction="in"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+ </method>
+ <method name="GetRequestFileData">
+ <tp:docstring>
+ Returns the contents of the given uploaded file. The |file_id| parameter
+ must correspond to the file_id member of FileInfo structure returned
+ by |Files| property for the given |request_id|.
+ </tp:docstring>
+ <arg name="request_id" type="s" direction="in"/>
+ <arg name="file_id" type="i" direction="in"/>
+ <arg name="contents" type="ay" direction="out"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+ </method>
+ <method name="CompleteRequest">
+ <tp:docstring>
+ Fulfills the request with specified |request_id| and provides response.
+ </tp:docstring>
+ <arg name="request_id" type="s" direction="in"/>
+ <arg name="status_code" type="i" direction="in"/>
+ <arg name="headers" type="a(ss)" direction="in"/>
+ <arg name="data" type="ay" direction="in"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="normal"/>
+ </method>
+ <!-- Properties -->
+ <property name="Id" type="s" access="read">
+ <tp:docstring>
+ Returns a unique ID of this instance.
+ </tp:docstring>
+ </property>
+ <property name="Port" type="q" access="read">
+ <tp:docstring>
+ Returns the port number this instance is serving requests on.
+ </tp:docstring>
+ </property>
+ <property name="Protocol" type="s" access="read">
+ <tp:docstring>
+ Returns the protocol name of this instance ("http" or "https").
+ </tp:docstring>
+ </property>
+ <property name="CertificateFingerprint" type="ay" access="read">
+ <tp:docstring>
+ Returns the TLS certificate fingerprint used for HTTPS instance or
+ empty array if this is an unsecured HTTP instance.
+ </tp:docstring>
+ </property>
+ </interface>
+</node>
diff --git a/webservd/dbus_bindings/org.chromium.WebServer.Server.xml b/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
index 88d78c9..bff6e94 100644
--- a/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
+++ b/webservd/dbus_bindings/org.chromium.WebServer.Server.xml
@@ -1,6 +1,23 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<node xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+<node name="/org/chromium/WebServer/Server"
+ xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
<interface name="org.chromium.WebServer.Server">
+ <!-- Methods -->
+ <method name="Ping">
+ <arg name="message" type="s" direction="out"/>
+ <annotation name="org.chromium.DBus.Method.Kind" value="simple"/>
+ </method>
+ <!-- Properties -->
+ <property name="DefaultHttp" type="o" access="read">
+ <tp:docstring>
+ Returns the D-Bus object path for the default HTTP protocol handler.
+ </tp:docstring>
+ </property>
+ <property name="DefaultHttps" type="o" access="read">
+ <tp:docstring>
+ Returns the D-Bus object path for the default HTTPS protocol handler.
+ </tp:docstring>
+ </property>
</interface>
</node>
diff --git a/webservd/dbus_protocol_handler.cc b/webservd/dbus_protocol_handler.cc
new file mode 100644
index 0000000..63a83b3
--- /dev/null
+++ b/webservd/dbus_protocol_handler.cc
@@ -0,0 +1,180 @@
+// Copyright 2015 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 "webserver/webservd/dbus_protocol_handler.h"
+
+#include <base/bind.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "webserver/webservd/dbus_request_handler.h"
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/server.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::DBusObject;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace webservd {
+
+DBusProtocolHandler::DBusProtocolHandler(
+ ExportedObjectManager* object_manager,
+ const dbus::ObjectPath& object_path,
+ ProtocolHandler* protocol_handler,
+ Server* server)
+ : dbus_object_{new DBusObject{object_manager, object_manager->GetBus(),
+ object_path}},
+ protocol_handler_{protocol_handler},
+ server_{server} {
+ dbus_adaptor_.SetId(protocol_handler->GetID());
+ dbus_adaptor_.SetPort(protocol_handler->GetPort());
+ dbus_adaptor_.SetProtocol(protocol_handler->GetProtocol());
+ dbus_adaptor_.SetCertificateFingerprint(
+ protocol_handler->GetCertificateFingerprint());
+}
+
+void DBusProtocolHandler::RegisterAsync(
+ const AsyncEventSequencer::CompletionAction& completion_callback) {
+ scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+ dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
+ dbus_object_->RegisterAsync(
+ sequencer->GetHandler("Failed exporting ProtocolHandler.", true));
+ sequencer->OnAllTasksCompletedCall({completion_callback});
+}
+
+ExportedObjectManager* DBusProtocolHandler::GetObjectManager() const {
+ return dbus_object_->GetObjectManager().get();
+}
+
+std::string DBusProtocolHandler::AddRequestHandler(
+ const std::string& in_url,
+ const std::string& in_method,
+ const std::string& in_service_name) {
+ auto p = dbus_service_data_.find(in_service_name);
+ if (p == dbus_service_data_.end()) {
+ DBusServiceData dbus_service_data;
+ dbus_service_data.handler_proxy.reset(
+ new RequestHandlerProxy{server_->GetBus(), in_service_name});
+ dbus_service_data.on_client_disconnected_callback =
+ base::Bind(&DBusProtocolHandler::OnClientDisconnected,
+ weak_ptr_factory_.GetWeakPtr(),
+ in_service_name);
+ server_->GetBus()->ListenForServiceOwnerChange(
+ in_service_name, dbus_service_data.on_client_disconnected_callback);
+ p = dbus_service_data_.emplace(in_service_name,
+ std::move(dbus_service_data)).first;
+ }
+ std::unique_ptr<RequestHandlerInterface> handler{
+ new DBusRequestHandler{server_, p->second.handler_proxy.get()}
+ };
+ std::string handler_id = protocol_handler_->AddRequestHandler(
+ in_url, in_method, std::move(handler));
+ p->second.handler_ids.insert(handler_id);
+ handler_to_service_name_map_.emplace(handler_id, in_service_name);
+
+ return handler_id;
+}
+
+bool DBusProtocolHandler::RemoveRequestHandler(
+ chromeos::ErrorPtr* error,
+ const std::string& in_handler_id) {
+
+ auto p = handler_to_service_name_map_.find(in_handler_id);
+ if (p == handler_to_service_name_map_.end()) {
+ chromeos::Error::AddToPrintf(error,
+ FROM_HERE,
+ chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED,
+ "Handler with ID %s does not exist",
+ in_handler_id.c_str());
+ return false;
+ }
+ std::string service_name = p->second;
+ CHECK(protocol_handler_->RemoveRequestHandler(in_handler_id));
+ DBusServiceData& dbus_service_data = dbus_service_data_[service_name];
+ CHECK_EQ(1u, dbus_service_data.handler_ids.erase(in_handler_id));
+ if (dbus_service_data.handler_ids.empty()) {
+ server_->GetBus()->UnlistenForServiceOwnerChange(
+ service_name, dbus_service_data.on_client_disconnected_callback);
+ dbus_service_data_.erase(service_name);
+ }
+ return true;
+}
+
+void DBusProtocolHandler::OnClientDisconnected(
+ const std::string& service_name,
+ const std::string& service_owner) {
+ // This method will be called when the client's D-Bus service owner has
+ // changed which could be either the client exiting (|service_owner| is empty)
+ // or is being replaced with another running instance.
+ // In either case, we need to remove the old client's handlers since the
+ // new client will register their own on start up anyway.
+ auto p = dbus_service_data_.find(service_name);
+ if (p == dbus_service_data_.end())
+ return;
+
+ for (const std::string& handler_id : p->second.handler_ids) {
+ handler_to_service_name_map_.erase(handler_id);
+ protocol_handler_->RemoveRequestHandler(handler_id);
+ }
+ server_->GetBus()->UnlistenForServiceOwnerChange(
+ service_name, p->second.on_client_disconnected_callback);
+ dbus_service_data_.erase(p);
+}
+
+bool DBusProtocolHandler::GetRequestFileData(
+ chromeos::ErrorPtr* error,
+ const std::string& in_request_id,
+ int32_t in_file_id,
+ std::vector<uint8_t>* out_contents) {
+ auto request = GetRequest(in_request_id, error);
+ if (!request)
+ return false;
+
+ if (request->GetFileData(in_file_id, out_contents))
+ return true;
+
+ chromeos::Error::AddToPrintf(error,
+ FROM_HERE,
+ chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED,
+ "File with ID %d does not exist",
+ in_file_id);
+ return false;
+}
+
+bool DBusProtocolHandler::CompleteRequest(
+ chromeos::ErrorPtr* error,
+ const std::string& in_request_id,
+ int32_t in_status_code,
+ const std::vector<std::tuple<std::string, std::string>>& in_headers,
+ const std::vector<uint8_t>& in_data) {
+ auto request = GetRequest(in_request_id, error);
+ if (!request)
+ return false;
+
+ if (request->Complete(in_status_code, in_headers, in_data)) {
+ return true;
+ }
+ chromeos::Error::AddTo(error, FROM_HERE, chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED, "Response already received");
+ return false;
+}
+
+Request* DBusProtocolHandler::GetRequest(const std::string& request_id,
+ chromeos::ErrorPtr* error) {
+ Request* request = protocol_handler_->GetRequest(request_id);
+ if (!request) {
+ chromeos::Error::AddToPrintf(error,
+ FROM_HERE,
+ chromeos::errors::dbus::kDomain,
+ DBUS_ERROR_FAILED,
+ "Unknown request ID: %s",
+ request_id.c_str());
+ }
+ return request;
+}
+
+} // namespace webservd
diff --git a/webservd/dbus_protocol_handler.h b/webservd/dbus_protocol_handler.h
new file mode 100644
index 0000000..d616f49
--- /dev/null
+++ b/webservd/dbus_protocol_handler.h
@@ -0,0 +1,121 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
+#define WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <dbus/bus.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webservd/org.chromium.WebServer.ProtocolHandler.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+} // dbus_utils
+} // chromeos
+
+namespace webservd {
+
+class ProtocolHandler;
+class Request;
+class Server;
+
+// This is a D-Bus interface object for the internal ProtocolHandler class.
+class DBusProtocolHandler final
+ : public org::chromium::WebServer::ProtocolHandlerInterface {
+ public:
+ DBusProtocolHandler(
+ chromeos::dbus_utils::ExportedObjectManager* object_manager,
+ const dbus::ObjectPath& object_path,
+ ProtocolHandler* protocol_handler,
+ Server* server);
+ void RegisterAsync(
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
+
+ // Returns the instance of D-Bus exported object manager.
+ chromeos::dbus_utils::ExportedObjectManager* GetObjectManager() const;
+
+ // Overrides from org::chromium::WebServer::DBusProtocolHandlerInterface.
+ std::string AddRequestHandler(
+ const std::string& in_url,
+ const std::string& in_method,
+ const std::string& in_service_name) override;
+
+ bool RemoveRequestHandler(chromeos::ErrorPtr* error,
+ const std::string& in_request_handler_id) override;
+
+ bool GetRequestFileData(chromeos::ErrorPtr* error,
+ const std::string& in_request_id,
+ int32_t in_file_id,
+ std::vector<uint8_t>* out_contents) override;
+
+ bool CompleteRequest(
+ chromeos::ErrorPtr* error,
+ const std::string& in_request_id,
+ int32_t in_status_code,
+ const std::vector<std::tuple<std::string, std::string>>& in_headers,
+ const std::vector<uint8_t>& in_data) override;
+
+ private:
+ using RequestHandlerProxy = org::chromium::WebServer::RequestHandlerProxy;
+
+ // Information about a request handler D-Bus back-end client.
+ struct DBusServiceData {
+ // A D-Bus proxy to the client's request handler object that actually
+ // processes requests registered for this client.
+ std::unique_ptr<RequestHandlerProxy> handler_proxy;
+ // A list of handler IDs registered by this client.
+ std::set<std::string> handler_ids;
+ // Called when the owner of the well known service name associated with this
+ // client changes. Since clients start up, this is called for the first
+ // time when they die.
+ dbus::Bus::GetServiceOwnerCallback on_client_disconnected_callback;
+ };
+
+ // Looks up a request with |request_id|.
+ // Returns nullptr and sets additional |error| information, if not found.
+ Request* GetRequest(const std::string& request_id, chromeos::ErrorPtr* error);
+
+ // Callback invoked when a client owning |service_name| is changed.
+ void OnClientDisconnected(const std::string& service_name,
+ const std::string& service_owner);
+
+ // D-Bus object adaptor for ProtocolHandler D-Bus object.
+ org::chromium::WebServer::ProtocolHandlerAdaptor dbus_adaptor_{this};
+ std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+
+ // Reference back to the real ProtocolHandler object.
+ ProtocolHandler* protocol_handler_{nullptr};
+ // Reference back to the Server class.
+ Server* server_{nullptr};
+
+ // Called when the owner of a service name changes. We're only interested in
+ // transitions to the empty string, indicating that a service name owner has
+ // died.
+ dbus::Bus::GetServiceOwnerCallback on_client_disconnected_callback_;
+
+ // A map that holds information regarding a server back-end client processing
+ // requests on the D-Bus service with the name used in the key of the map.
+ std::map<std::string, DBusServiceData> dbus_service_data_;
+ // Handler ID to service name map.
+ std::map<std::string, std::string> handler_to_service_name_map_;
+
+ base::WeakPtrFactory<DBusProtocolHandler> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(DBusProtocolHandler);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_DBUS_PROTOCOL_HANDLER_H_
diff --git a/webservd/dbus_request_handler.cc b/webservd/dbus_request_handler.cc
new file mode 100644
index 0000000..8e44dd2
--- /dev/null
+++ b/webservd/dbus_request_handler.cc
@@ -0,0 +1,80 @@
+// Copyright 2015 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 "webserver/webservd/dbus_request_handler.h"
+
+#include <tuple>
+#include <vector>
+
+#include <base/bind.h>
+#include <chromeos/http/http_request.h>
+#include <chromeos/mime_utils.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/server.h"
+
+namespace webservd {
+
+namespace {
+
+void OnError(Request* request,
+ bool debug,
+ chromeos::Error* error) {
+ std::string error_msg{"Internal Server Error"};
+ if (debug) {
+ error_msg += "\r\n" + error->GetMessage();
+ }
+ request->Complete(chromeos::http::status_code::InternalServerError, {},
+ chromeos::mime::text::kPlain, error_msg);
+}
+
+} // anonymous namespace
+
+DBusRequestHandler::DBusRequestHandler(Server* server,
+ RequestHandlerProxy* handler_proxy)
+ : server_{server},
+ handler_proxy_{handler_proxy} {
+}
+
+void DBusRequestHandler::HandleRequest(Request* request) {
+ std::vector<std::tuple<std::string, std::string>> headers;
+ for (const auto& pair : request->GetHeaders())
+ headers.emplace_back(pair.first, pair.second);
+
+ std::vector<std::tuple<int32_t, std::string, std::string, std::string,
+ std::string>> files;
+ int32_t index = 0;
+ for (const auto& file : request->GetFileInfo()) {
+ files.emplace_back(index++, file->field_name, file->file_name,
+ file->content_type, file->transfer_encoding);
+ }
+
+ std::vector<std::tuple<bool, std::string, std::string>> params;
+ for (const auto& pair : request->GetDataGet())
+ params.emplace_back(false, pair.first, pair.second);
+
+ for (const auto& pair : request->GetDataPost())
+ params.emplace_back(true, pair.first, pair.second);
+
+ auto error_callback = base::Bind(&OnError,
+ base::Unretained(request),
+ server_->UseDebugInfo());
+
+ auto request_id = std::make_tuple(request->GetProtocolHandlerID(),
+ request->GetRequestHandlerID(),
+ request->GetID(),
+ request->GetURL(),
+ request->GetMethod());
+
+ handler_proxy_->ProcessRequestAsync(request_id,
+ headers,
+ params,
+ files,
+ request->GetBody(),
+ base::Bind(&base::DoNothing),
+ error_callback);
+}
+
+} // namespace webservd
diff --git a/webservd/dbus_request_handler.h b/webservd/dbus_request_handler.h
new file mode 100644
index 0000000..f7a0617
--- /dev/null
+++ b/webservd/dbus_request_handler.h
@@ -0,0 +1,40 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
+#define WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
+
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/object_path.h>
+
+#include "libwebserv/dbus-proxies.h"
+#include "webserver/webservd/request_handler_interface.h"
+
+namespace webservd {
+
+class Server;
+
+// A D-Bus interface for a request handler.
+class DBusRequestHandler : public RequestHandlerInterface {
+ public:
+ using RequestHandlerProxy = org::chromium::WebServer::RequestHandlerProxy;
+ DBusRequestHandler(Server* server,
+ RequestHandlerProxy* handler_proxy);
+
+ // Called to process an incoming HTTP request this handler is subscribed
+ // to handle.
+ void HandleRequest(Request* request) override;
+
+ private:
+ Server* server_{nullptr};
+ RequestHandlerProxy* handler_proxy_{nullptr};
+
+ DISALLOW_COPY_AND_ASSIGN(DBusRequestHandler);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_DBUS_REQUEST_HANDLER_H_
diff --git a/webservd/etc/init/webservd.conf b/webservd/etc/init/webservd.conf
index 08740d9..08cb7f2 100644
--- a/webservd/etc/init/webservd.conf
+++ b/webservd/etc/init/webservd.conf
@@ -5,19 +5,63 @@
description "Brillo WebServer Daemon"
author "chromium-os-dev@chromium.org"
-start on starting system-services
+start on starting system-services and stopped iptables and stopped ip6tables
stop on stopping system-services
respawn
+env WEBSERVD_LOG_LEVEL=0
+env WEBSERVD_HTTP_PORT=80
+env WEBSERVD_HTTPS_PORT=443
+env WEBSERVD_DEBUG=false
+# TODO(zqiu): Temporary
+env APMANAGER_DHCP_SERVER_PORT=67
+
pre-start script
+ # Add a rule to the firewall to allow HTTP traffic except from the
+ # loopback interface (to prevent e.g. Chrome from connecting.)
+ # TODO(avakulenko): localhost is left enabled for now for testing...
+ #iptables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+ #ip6tables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+ #iptables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+ #ip6tables -I INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+ iptables -A INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+ ip6tables -A INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+ iptables -A INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+ ip6tables -A INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+
+ # TODO(zqiu): Temporary hack for adding firewall rule to accept DHCP traffic
+ # on behalf of apmanager. Putting it here instead of in the apmanager upstart
+ # script to avoid a race condition when multiple upstart scripts tried to
+ # execute iptables command at the same time. Will delete this when the
+ # support for runtime firewall hole punching in permission_broker is
+ # completed (crbug.com/435400).
+ iptables -I INPUT -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j ACCEPT
+ iptables -I INPUT -i lo -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j REJECT
+
mkdir -m 0755 -p /var/log/webservd
chown webservd:webservd /var/log/webservd
end script
-env WEBSERVD_LOG_LEVEL=0
+exec /usr/bin/webservd \
+ --v="${WEBSERVD_LOG_LEVEL}" \
+ --http_port="${WEBSERVD_HTTP_PORT}" \
+ --https_port="${WEBSERVD_HTTPS_PORT}" \
+ --debug="${WEBSERVD_DEBUG}" \
-# Minijail actually forks off our desired process.
-expect fork
+post-stop script
+ # Delete the rules we previously added
+ iptables -D INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+ ip6tables -D INPUT -p tcp --dport ${WEBSERVD_HTTP_PORT} -j ACCEPT
+ iptables -D INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
+ ip6tables -D INPUT -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j ACCEPT
-exec minijail0 -i -g webservd -u webservd /usr/bin/webservd \
- --v="${WEBSERVD_LOG_LEVEL}"
+ # TODO(zqiu): refer to comments above for adding these rules.
+ iptables -D INPUT -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j ACCEPT
+ iptables -D INPUT -i lo -p udp --dport ${APMANAGER_DHCP_SERVER_PORT} -j REJECT
+
+ # TODO(avakulenko): uncomment below
+ #iptables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+ #ip6tables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTP_PORT} -j REJECT
+ #iptables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+ #ip6tables -D INPUT -i lo -p tcp --dport ${WEBSERVD_HTTPS_PORT} -j REJECT
+end script
diff --git a/webservd/main.cc b/webservd/main.cc
index 9c9e385..97e05eb 100644
--- a/webservd/main.cc
+++ b/webservd/main.cc
@@ -3,14 +3,17 @@
// found in the LICENSE file.
#include <string>
+#include <sysexits.h>
#include <base/command_line.h>
#include <chromeos/dbus/async_event_sequencer.h>
#include <chromeos/dbus/exported_object_manager.h>
#include <chromeos/daemons/dbus_daemon.h>
+#include <chromeos/flag_helper.h>
+#include <chromeos/minijail/minijail.h>
#include <chromeos/syslog_logging.h>
-#include "webserver/webservd/manager.h"
+#include "webserver/webservd/server.h"
using chromeos::dbus_utils::AsyncEventSequencer;
@@ -18,21 +21,32 @@
const char kServiceName[] = "org.chromium.WebServer";
const char kRootServicePath[] = "/org/chromium/WebServer";
+const char kWebServerUserName[] = "webservd";
+const char kWebServerGroupName[] = "webservd";
class Daemon : public chromeos::DBusServiceDaemon {
public:
- Daemon() : DBusServiceDaemon(kServiceName, kRootServicePath) {}
+ Daemon(uint16_t http_port, uint16_t https_port, bool debug)
+ : DBusServiceDaemon{kServiceName, kRootServicePath},
+ http_port_{http_port},
+ https_port_{https_port},
+ debug_{debug} {}
protected:
void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override {
- manager_.reset(new webservd::Manager{object_manager_.get()});
- manager_->RegisterAsync(
- sequencer->GetHandler("Manager.RegisterAsync() failed.", true));
- LOG(INFO) << "webservd starting...";
+ server_.reset(new webservd::Server{object_manager_.get(),
+ http_port_,
+ https_port_,
+ debug_});
+ server_->RegisterAsync(
+ sequencer->GetHandler("Server.RegisterAsync() failed.", true));
}
private:
- std::unique_ptr<webservd::Manager> manager_;
+ uint16_t http_port_{0};
+ uint16_t https_port_{0};
+ bool debug_{false};
+ std::unique_ptr<webservd::Server> server_;
DISALLOW_COPY_AND_ASSIGN(Daemon);
};
@@ -40,10 +54,44 @@
} // namespace
int main(int argc, char* argv[]) {
- CommandLine::Init(argc, argv);
- chromeos::InitLog(chromeos::kLogToSyslog |
- chromeos::kLogToStderr |
- chromeos::kLogHeader);
- Daemon daemon;
+ DEFINE_int32(http_port, 80,
+ "HTTP port to listen for requests on (0 to disable)");
+ DEFINE_int32(https_port, 443,
+ "HTTPS port to listen for requests on (0 to disable)");
+ DEFINE_bool(log_to_stderr, false, "log trace messages to stderr as well");
+ DEFINE_bool(debug, false,
+ "return debug error information in web requests");
+ chromeos::FlagHelper::Init(argc, argv, "Brillo web server daemon");
+
+ int flags = chromeos::kLogToSyslog;
+ if (FLAGS_log_to_stderr)
+ flags |= chromeos::kLogToStderr;
+ chromeos::InitLog(flags | chromeos::kLogHeader);
+
+ if (FLAGS_http_port < 0 || FLAGS_http_port > 0xFFFF) {
+ LOG(ERROR) << "Invalid HTTP port specified: '" << FLAGS_http_port << "'.";
+ return EX_USAGE;
+ }
+
+ if (FLAGS_https_port < 0 || FLAGS_https_port > 0xFFFF) {
+ LOG(ERROR) << "Invalid HTTPS port specified: '" << FLAGS_https_port << "'.";
+ return EX_USAGE;
+ }
+
+ Daemon daemon{static_cast<uint16_t>(FLAGS_http_port),
+ static_cast<uint16_t>(FLAGS_https_port),
+ FLAGS_debug};
+
+ // Drop privileges and use 'webservd' user. We need to do this after Daemon
+ // object is constructed since it creates an instance of base::AtExitManager
+ // which is required for chromeos::Minijail::GetInstance() to work.
+ chromeos::Minijail* minijail_instance = chromeos::Minijail::GetInstance();
+ minijail* jail = minijail_instance->New();
+ minijail_instance->DropRoot(jail, kWebServerUserName, kWebServerGroupName);
+ // Permissions needed for the daemon to allow it to bind to ports like TCP
+ // 80.
+ minijail_instance->UseCapabilities(jail, CAP_TO_MASK(CAP_NET_BIND_SERVICE));
+ minijail_enter(jail);
+ minijail_instance->Destroy(jail);
return daemon.Run();
}
diff --git a/webservd/manager.cc b/webservd/manager.cc
deleted file mode 100644
index c228a09..0000000
--- a/webservd/manager.cc
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2015 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 "webserver/webservd/manager.h"
-
-#include <chromeos/dbus/async_event_sequencer.h>
-#include <chromeos/dbus/exported_object_manager.h>
-
-using chromeos::dbus_utils::AsyncEventSequencer;
-using chromeos::dbus_utils::DBusObject;
-using chromeos::dbus_utils::ExportedObjectManager;
-
-namespace webservd {
-
-Manager::Manager(ExportedObjectManager* object_manager)
- : dbus_object_{new DBusObject{
- object_manager, object_manager->GetBus(),
- org::chromium::WebServer::ManagerAdaptor::GetObjectPath()}} {}
-
-void Manager::RegisterAsync(
- const AsyncEventSequencer::CompletionAction& completion_callback) {
- scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
- dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
- dbus_object_->RegisterAsync(
- sequencer->GetHandler("Failed exporting Manager.", true));
- sequencer->OnAllTasksCompletedCall({completion_callback});
-}
-
-std::string Manager::Ping() {
- return "Web Server is running";
-}
-
-} // namespace webservd
diff --git a/webservd/manager.h b/webservd/manager.h
deleted file mode 100644
index 89ae864..0000000
--- a/webservd/manager.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2015 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.
-
-#ifndef WEBSERVER_WEBSERVD_MANAGER_H_
-#define WEBSERVER_WEBSERVD_MANAGER_H_
-
-#include <memory>
-#include <string>
-
-#include <base/macros.h>
-#include <chromeos/dbus/dbus_object.h>
-
-#include "webservd/org.chromium.WebServer.Manager.h"
-
-namespace chromeos {
-namespace dbus_utils {
-class ExportedObjectManager;
-} // dbus_utils
-} // chromeos
-
-namespace webservd {
-
-// Manages global state of webservd.
-class Manager : public org::chromium::WebServer::ManagerInterface {
- public:
- explicit Manager(chromeos::dbus_utils::ExportedObjectManager* object_manager);
- void RegisterAsync(
- const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
-
- // Overrides from org::chromium::WebServer::ManagerInterface.
- std::string Ping() override;
-
- private:
- org::chromium::WebServer::ManagerAdaptor dbus_adaptor_{this};
- std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
- DISALLOW_COPY_AND_ASSIGN(Manager);
-};
-
-} // namespace webservd
-
-#endif // WEBSERVER_WEBSERVD_MANAGER_H_
diff --git a/webservd/protocol_handler.cc b/webservd/protocol_handler.cc
new file mode 100644
index 0000000..402d865
--- /dev/null
+++ b/webservd/protocol_handler.cc
@@ -0,0 +1,326 @@
+// Copyright 2015 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 "webserver/webservd/protocol_handler.h"
+
+#include <limits>
+#include <microhttpd.h>
+
+#include <base/bind.h>
+#include <base/guid.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+
+#include "webserver/webservd/request.h"
+#include "webserver/webservd/request_handler_interface.h"
+#include "webserver/webservd/server_interface.h"
+
+namespace webservd {
+
+const char ProtocolHandler::kHttp[] = "http";
+const char ProtocolHandler::kHttps[] = "https";
+
+// Helper class to provide static callback methods to libmicrohttpd library,
+// with the ability to access private methods of Server class.
+class ServerHelper {
+ public:
+ static int ConnectionHandler(void *cls,
+ MHD_Connection* connection,
+ const char* url,
+ const char* method,
+ const char* version,
+ const char* upload_data,
+ size_t* upload_data_size,
+ void** con_cls) {
+ auto handler = reinterpret_cast<ProtocolHandler*>(cls);
+ if (nullptr == *con_cls) {
+ std::string request_handler_id = handler->FindRequestHandler(url, method);
+ std::unique_ptr<Request> request{new Request{
+ request_handler_id, url, method, connection, handler
+ }};
+ if (!request->BeginRequestData())
+ return MHD_NO;
+
+ // Pass the raw pointer here in order to interface with libmicrohttpd's
+ // old-style C API.
+ *con_cls = request.release();
+ } else {
+ auto request = reinterpret_cast<Request*>(*con_cls);
+ if (*upload_data_size) {
+ if (!request->AddRequestData(upload_data, *upload_data_size))
+ return MHD_NO;
+ *upload_data_size = 0;
+ } else {
+ request->EndRequestData();
+ }
+ }
+ return MHD_YES;
+ }
+
+ static void RequestCompleted(void* cls,
+ MHD_Connection* connection,
+ void** con_cls,
+ MHD_RequestTerminationCode toe) {
+ auto request = reinterpret_cast<Request*>(*con_cls);
+ *con_cls = nullptr;
+ delete request;
+ }
+};
+
+ProtocolHandler::ProtocolHandler(const std::string& id,
+ ServerInterface* server_interface)
+ : id_{id}, server_interface_{server_interface} {
+ if (id_.empty())
+ id_ = base::GenerateGUID();
+}
+
+ProtocolHandler::~ProtocolHandler() {
+ Stop();
+}
+
+std::string ProtocolHandler::AddRequestHandler(
+ const std::string& url,
+ const std::string& method,
+ std::unique_ptr<RequestHandlerInterface> handler) {
+ std::string handler_id = base::GenerateGUID();
+ request_handlers_.emplace(handler_id,
+ HandlerMapEntry{url, method, std::move(handler)});
+ return handler_id;
+}
+
+bool ProtocolHandler::RemoveRequestHandler(const std::string& handler_id) {
+ return request_handlers_.erase(handler_id) == 1;
+}
+
+std::string ProtocolHandler::FindRequestHandler(
+ const base::StringPiece& url,
+ const base::StringPiece& method) const {
+ size_t score = std::numeric_limits<size_t>::max();
+ std::string handler_id;
+ for (const auto& pair : request_handlers_) {
+ std::string handler_url = pair.second.url;
+ bool url_match = (handler_url == url);
+ bool method_match = (pair.second.method == method);
+
+ // Try exact match first. If everything matches, we have our handler.
+ if (url_match && method_match)
+ return pair.first;
+
+ // Calculate the current handler's similarity score. The lower the score
+ // the better the match is...
+ size_t current_score = 0;
+ if (!url_match && !handler_url.empty() && handler_url.back() == '/') {
+ if (url.starts_with(handler_url)) {
+ url_match = true;
+ // Use the difference in URL length as URL match quality proxy.
+ // The longer URL, the more specific (better) match is.
+ // Multiply by 2 to allow for extra score point for matching the method.
+ current_score = (url.size() - handler_url.size()) * 2;
+ }
+ }
+
+ if (!method_match && pair.second.method.empty()) {
+ // If the handler didn't specify the method it handles, this means
+ // it doesn't care. However this isn't the exact match, so bump
+ // the score up one point.
+ method_match = true;
+ ++current_score;
+ }
+
+ if (url_match && method_match && current_score < score) {
+ score = current_score;
+ handler_id = pair.first;
+ }
+ }
+
+ return handler_id;
+}
+
+bool ProtocolHandler::Start(uint16_t port) {
+ return StartWithTLS(port, {}, {}, {});
+}
+
+bool ProtocolHandler::StartWithTLS(
+ uint16_t port,
+ const chromeos::SecureBlob& private_key,
+ const chromeos::Blob& certificate,
+ const chromeos::Blob& certificate_fingerprint) {
+ if (server_) {
+ LOG(ERROR) << "Web server is already running.";
+ return false;
+ }
+
+ // Either both keys and certificate must be specified or both must be omitted.
+ CHECK_EQ(private_key.empty(), certificate.empty());
+ // Same with cert fingerprint.
+ CHECK_EQ(certificate.empty(), certificate_fingerprint.empty());
+
+ const bool use_tls = !private_key.empty();
+
+ LOG(INFO) << "Starting " << (use_tls ? "HTTPS" : "HTTP")
+ << " Server on port: " << port;
+
+ port_ = port;
+ protocol_ = (use_tls ? "https" : "http");
+ certificate_fingerprint_ = certificate_fingerprint;
+
+ auto callback_addr =
+ reinterpret_cast<intptr_t>(&ServerHelper::RequestCompleted);
+ uint32_t flags = MHD_NO_FLAG;
+ if (server_interface_->UseDebugInfo())
+ flags |= MHD_USE_DEBUG;
+
+ std::vector<MHD_OptionItem> options{
+ {MHD_OPTION_CONNECTION_LIMIT, 10, nullptr},
+ {MHD_OPTION_CONNECTION_TIMEOUT, 60, nullptr},
+ {MHD_OPTION_NOTIFY_COMPLETED, callback_addr, nullptr},
+ };
+
+ // libmicrohttpd expects both the key and certificate to be zero-terminated
+ // strings. Make sure they are terminated properly.
+ chromeos::SecureBlob private_key_copy = private_key;
+ chromeos::Blob certificate_copy = certificate;
+ private_key_copy.push_back(0);
+ certificate_copy.push_back(0);
+
+ if (use_tls) {
+ flags |= MHD_USE_SSL;
+ options.push_back(
+ MHD_OptionItem{MHD_OPTION_HTTPS_MEM_KEY, 0, private_key_copy.data()});
+ options.push_back(
+ MHD_OptionItem{MHD_OPTION_HTTPS_MEM_CERT, 0, certificate_copy.data()});
+ }
+
+ options.push_back(MHD_OptionItem{MHD_OPTION_END, 0, nullptr});
+
+ server_ = MHD_start_daemon(flags, port, nullptr, nullptr,
+ &ServerHelper::ConnectionHandler, this,
+ MHD_OPTION_ARRAY, options.data(), MHD_OPTION_END);
+ if (!server_) {
+ LOG(ERROR) << "Failed to start the web server on port " << port;
+ return false;
+ }
+ server_interface_->ProtocolHandlerStarted(this);
+ DoWork();
+ LOG(INFO) << "Server started";
+ return true;
+}
+
+bool ProtocolHandler::Stop() {
+ if (server_) {
+ LOG(INFO) << "Shutting down the web server...";
+ MHD_stop_daemon(server_);
+ server_ = nullptr;
+ server_interface_->ProtocolHandlerStopped(this);
+ LOG(INFO) << "Server shutdown complete";
+ }
+ port_ = 0;
+ protocol_.clear();
+ certificate_fingerprint_.clear();
+ return true;
+}
+
+void ProtocolHandler::AddRequest(Request* request) {
+ requests_.emplace(request->GetID(), request);
+}
+
+void ProtocolHandler::RemoveRequest(Request* request) {
+ requests_.erase(request->GetID());
+}
+
+Request* ProtocolHandler::GetRequest(const std::string& request_id) const {
+ auto p = requests_.find(request_id);
+ return (p != requests_.end()) ? p->second : nullptr;
+}
+
+// A file descriptor watcher class that oversees I/O operation notification
+// on particular socket file descriptor.
+class ProtocolHandler::Watcher final
+ : public base::MessageLoopForIO::Watcher{
+ public:
+ Watcher(ProtocolHandler* handler,
+ int fd,
+ base::MessageLoopForIO::Mode mode,
+ base::MessageLoopForIO* message_loop)
+ : handler_{handler} {
+ message_loop->WatchFileDescriptor(fd, false, mode, &controller_, this);
+ }
+
+ // Overrides from base::MessageLoopForIO::Watcher.
+ void OnFileCanReadWithoutBlocking(int fd) override {
+ handler_->DoWork();
+ }
+
+ void OnFileCanWriteWithoutBlocking(int fd) override {
+ handler_->DoWork();
+ }
+
+ private:
+ ProtocolHandler* handler_;
+ base::MessageLoopForIO::FileDescriptorWatcher controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(Watcher);
+};
+
+void ProtocolHandler::OnResponseDataReceived() {
+ base::MessageLoopForIO::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&ProtocolHandler::DoWork, weak_ptr_factory_.GetWeakPtr()));
+}
+
+void ProtocolHandler::DoWork() {
+ base::MessageLoopForIO* message_loop = base::MessageLoopForIO::current();
+
+ // Remove the old watchers first.
+ watchers_.clear();
+
+ // Check if there is any pending work to be done in libmicrohttpd.
+ MHD_run(server_);
+
+ // Get all the file descriptors from libmicrohttpd and watch for I/O
+ // operations on them.
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
+ int max_fd = MHD_INVALID_SOCKET;
+ FD_ZERO(&rs);
+ FD_ZERO(&ws);
+ FD_ZERO(&es);
+ CHECK_EQ(MHD_YES, MHD_get_fdset(server_, &rs, &ws, &es, &max_fd));
+ for (int fd = 0; fd <= max_fd; fd++) {
+ // libmicrohttpd is not using exception FDs, so lets put our expectations
+ // upfront.
+ CHECK(!FD_ISSET(fd, &es));
+ if (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws)) {
+ base::MessageLoopForIO::Mode mode = base::MessageLoopForIO::WATCH_READ;
+ if (FD_ISSET(fd, &rs) && FD_ISSET(fd, &ws))
+ mode = base::MessageLoopForIO::WATCH_READ_WRITE;
+ else if (FD_ISSET(fd, &rs))
+ mode = base::MessageLoopForIO::WATCH_READ;
+ else if (FD_ISSET(fd, &ws))
+ mode = base::MessageLoopForIO::WATCH_WRITE;
+ // libmicrohttpd should never use any of stdin/stdout/stderr descriptors.
+ CHECK_GT(fd, STDERR_FILENO);
+ watchers_.emplace_back(new Watcher{this, fd, mode, message_loop});
+ }
+ }
+
+ // Schedule a time-out timer, if asked by libmicrohttpd.
+ MHD_UNSIGNED_LONG_LONG mhd_timeout = 0;
+ if (!timer_scheduled_ && MHD_get_timeout(server_, &mhd_timeout) == MHD_YES) {
+ timer_scheduled_ = true;
+ message_loop->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&ProtocolHandler::DoWork,
+ weak_ptr_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(mhd_timeout));
+ }
+}
+
+void ProtocolHandler::TimerCallback() {
+ timer_scheduled_ = false;
+ DoWork();
+}
+
+} // namespace webservd
diff --git a/webservd/protocol_handler.h b/webservd/protocol_handler.h
new file mode 100644
index 0000000..f99dcf3
--- /dev/null
+++ b/webservd/protocol_handler.h
@@ -0,0 +1,136 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
+#define WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <base/strings/string_piece.h>
+#include <chromeos/secure_blob.h>
+
+struct MHD_Daemon;
+
+namespace webservd {
+
+class Request;
+class RequestHandlerInterface;
+class ServerInterface;
+
+// An instance of a handler for particular protocol (http/https) bound to a
+// particular port to handle requests on.
+class ProtocolHandler final {
+ public:
+ ProtocolHandler(const std::string& id,
+ ServerInterface* server_interface);
+ ~ProtocolHandler();
+
+ // Registers a new request handler for the given URL and request method.
+ // Returns a handler ID (GUID).
+ std::string AddRequestHandler(
+ const std::string& url,
+ const std::string& method,
+ std::unique_ptr<RequestHandlerInterface> handler);
+
+ // Removes a previously registered handler.
+ bool RemoveRequestHandler(const std::string& handler_id);
+
+ // Finds a handler for given URL/Method. This is the method used to look up
+ // the handler for incoming HTTP requests.
+ // Returns the handler_id or empty string if not found.
+ std::string FindRequestHandler(const base::StringPiece& url,
+ const base::StringPiece& method) const;
+ // Binds the socket and listens to HTTP requests on it.
+ bool Start(uint16_t port);
+
+ // Binds the socket and listens to HTTPS requests on it.
+ bool StartWithTLS(uint16_t port,
+ const chromeos::SecureBlob& private_key,
+ const chromeos::Blob& certificate,
+ const chromeos::Blob& certificate_fingerprint);
+
+ // Stops listening for requests.
+ bool Stop();
+
+ // Returns the port this handler listens for requests on.
+ uint16_t GetPort() const { return port_; }
+
+ // Returns the protocol name for this handler ("http" or "https").
+ const std::string& GetProtocol() const { return protocol_; }
+
+ // Returns the SHA-256 fingerprint of the TLS certificate used for https
+ // connection. Returns an empty byte array if this handler is serving http.
+ const chromeos::Blob& GetCertificateFingerprint() const {
+ return certificate_fingerprint_;
+ }
+
+ // Returns the unique request handler ID. This will normally by a GUID, unless
+ // this is one of the two default handlers for HTTP/HTTPS protocols with
+ // default ports, in which case the IDs will be "http" and "https"
+ // respectively.
+ const std::string& GetID() const { return id_; }
+
+ // Returns the pointer to the Server object.
+ ServerInterface* GetServer() const { return server_interface_; }
+
+ // Methods to store/remove/retrieve pending incoming requests for the duration
+ // of the request's processing.
+ void AddRequest(Request* request);
+ void RemoveRequest(Request* request);
+ Request* GetRequest(const std::string& request_id) const;
+
+ // Notification of incoming reply from the request handler.
+ void OnResponseDataReceived();
+
+ static const char kHttp[];
+ static const char kHttps[];
+
+ private:
+ friend class Request;
+ friend class ServerHelper;
+ class Watcher;
+ struct HandlerMapEntry {
+ std::string url;
+ std::string method;
+ std::unique_ptr<RequestHandlerInterface> handler;
+ };
+
+ // Called when new data is available on sockets for libmicrohttpd to process.
+ void DoWork();
+ // Called periodically as requested by libmicrohttpd.
+ void TimerCallback();
+
+ // libmicrohttpd daemon class.
+ MHD_Daemon* server_{nullptr};
+ // A map that stores registered request handlers (the key is handler ID).
+ std::map<std::string, HandlerMapEntry> request_handlers_;
+ // A map that stores pending requests (the key is request ID).
+ std::map<std::string, Request*> requests_;
+ // Protocol Handler ID.
+ std::string id_;
+ // Reference back to the Server.
+ ServerInterface* server_interface_{nullptr};
+ // The port we are listening to.
+ uint16_t port_{0};
+ // The protocol name ("http" or "https").
+ std::string protocol_;
+ // TLS certificate fingerprint (if any).
+ chromeos::Blob certificate_fingerprint_;
+ // File descriptor watchers for current active sockets.
+ std::vector<std::unique_ptr<Watcher>> watchers_;
+ // Set to true when a timer request is scheduled.
+ bool timer_scheduled_{false};
+
+ base::WeakPtrFactory<ProtocolHandler> weak_ptr_factory_{this};
+ DISALLOW_COPY_AND_ASSIGN(ProtocolHandler);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_PROTOCOL_HANDLER_H_
diff --git a/webservd/request.cc b/webservd/request.cc
new file mode 100644
index 0000000..11a5d38
--- /dev/null
+++ b/webservd/request.cc
@@ -0,0 +1,240 @@
+// Copyright 2015 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 "webserver/webservd/request.h"
+
+#include <microhttpd.h>
+
+#include <base/bind.h>
+#include <base/guid.h>
+#include <chromeos/http/http_request.h>
+#include <chromeos/http/http_utils.h>
+#include <chromeos/mime_utils.h>
+#include <chromeos/strings/string_utils.h>
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/request_handler_interface.h"
+
+namespace webservd {
+
+// Helper class to provide static callback methods to microhttpd library,
+// with the ability to access private methods of Request class.
+class RequestHelper {
+ public:
+ static int PostDataIterator(void* cls,
+ MHD_ValueKind kind,
+ const char* key,
+ const char* filename,
+ const char* content_type,
+ const char* transfer_encoding,
+ const char* data,
+ uint64_t off,
+ size_t size) {
+ auto self = reinterpret_cast<Request*>(cls);
+ return self->ProcessPostData(key, filename, content_type, transfer_encoding,
+ data, off, size) ? MHD_YES : MHD_NO;
+ }
+
+ static int ValueCallback(void* cls,
+ MHD_ValueKind kind,
+ const char* key,
+ const char* value) {
+ auto self = reinterpret_cast<Request*>(cls);
+ std::string data;
+ if (value)
+ data = value;
+ if (kind == MHD_HEADER_KIND) {
+ self->headers_.emplace_back(chromeos::http::GetCanonicalHeaderName(key),
+ data);
+ } else if (kind == MHD_COOKIE_KIND) {
+ // TODO(avakulenko): add support for cookies...
+ } else if (kind == MHD_POSTDATA_KIND) {
+ self->post_data_.emplace_back(key, data);
+ } else if (kind == MHD_GET_ARGUMENT_KIND) {
+ self->get_data_.emplace_back(key, data);
+ }
+ return MHD_YES;
+ }
+};
+
+FileInfo::FileInfo(const std::string& in_field_name,
+ const std::string& in_file_name,
+ const std::string& in_content_type,
+ const std::string& in_transfer_encoding)
+ : field_name(in_field_name),
+ file_name(in_file_name),
+ content_type(in_content_type),
+ transfer_encoding(in_transfer_encoding) {
+}
+
+Request::Request(
+ const std::string& request_handler_id,
+ const std::string& url,
+ const std::string& method,
+ MHD_Connection* connection,
+ ProtocolHandler* protocol_handler)
+ : id_{base::GenerateGUID()},
+ request_handler_id_{request_handler_id},
+ url_{url},
+ method_{method},
+ connection_{connection},
+ protocol_handler_{protocol_handler} {
+ post_processor_ = MHD_create_post_processor(
+ connection, 1024, &RequestHelper::PostDataIterator, this);
+}
+
+Request::~Request() {
+ if (post_processor_)
+ MHD_destroy_post_processor(post_processor_);
+ protocol_handler_->RemoveRequest(this);
+}
+
+bool Request::GetFileData(int file_id, std::vector<uint8_t>* contents) {
+ if (file_id < 0 || static_cast<size_t>(file_id) >= file_info_.size())
+ return false;
+ *contents = file_info_[file_id]->data;
+ return true;
+}
+
+bool Request::Complete(
+ int32_t status_code,
+ const std::vector<std::tuple<std::string, std::string>>& headers,
+ const std::vector<uint8_t>& data) {
+ if (state_ != State::kWaitingForResponse)
+ return false;
+
+ response_status_code_ = status_code;
+ response_headers_.reserve(headers.size());
+ for (const auto& tuple : headers) {
+ response_headers_.emplace_back(std::get<0>(tuple), std::get<1>(tuple));
+ }
+ response_data_ = data;
+ state_ = State::kResponseReceived;
+ protocol_handler_->OnResponseDataReceived();
+ return true;
+}
+
+bool Request::Complete(
+ int32_t status_code,
+ const std::vector<std::tuple<std::string, std::string>>& headers,
+ const std::string& mime_type,
+ const std::string& data) {
+ std::vector<std::tuple<std::string, std::string>> headers_copy;
+ headers_copy.emplace_back(chromeos::http::response_header::kContentType,
+ mime_type);
+ return Complete(status_code, headers_copy,
+ chromeos::string_utils::GetStringAsBytes(data));
+}
+
+const std::string& Request::GetProtocolHandlerID() const {
+ return protocol_handler_->GetID();
+}
+
+bool Request::BeginRequestData() {
+ MHD_get_connection_values(connection_, MHD_HEADER_KIND,
+ &RequestHelper::ValueCallback, this);
+ MHD_get_connection_values(connection_, MHD_COOKIE_KIND,
+ &RequestHelper::ValueCallback, this);
+ MHD_get_connection_values(connection_, MHD_POSTDATA_KIND,
+ &RequestHelper::ValueCallback, this);
+ MHD_get_connection_values(connection_, MHD_GET_ARGUMENT_KIND,
+ &RequestHelper::ValueCallback, this);
+ return true;
+}
+
+bool Request::AddRequestData(const void* data, size_t size) {
+ if (!post_processor_)
+ return AddRawRequestData(data, size);
+ return MHD_post_process(post_processor_,
+ static_cast<const char*>(data), size) == MHD_YES;
+}
+
+void Request::EndRequestData() {
+ if (state_ == State::kIdle) {
+ state_ = State::kWaitingForResponse;
+ if (!request_handler_id_.empty()) {
+ protocol_handler_->AddRequest(this);
+ auto p = protocol_handler_->request_handlers_.find(request_handler_id_);
+ CHECK(p != protocol_handler_->request_handlers_.end());
+ // Send the request over D-Bus and await the response...
+ p->second.handler->HandleRequest(this);
+ } else {
+ // There was no handler found when request was made, respond with
+ // 404 Page Not Found...
+ Complete(chromeos::http::status_code::NotFound, {},
+ chromeos::mime::text::kPlain, "Not Found");
+ }
+ } else if (state_ == State::kResponseReceived) {
+ MHD_Response* resp = MHD_create_response_from_buffer(
+ response_data_.size(), response_data_.data(), MHD_RESPMEM_PERSISTENT);
+ for (const auto& pair : response_headers_) {
+ MHD_add_response_header(resp, pair.first.c_str(), pair.second.c_str());
+ }
+ CHECK_EQ(MHD_YES,
+ MHD_queue_response(connection_, response_status_code_, resp))
+ << "Failed to queue response";
+ MHD_destroy_response(resp); // |resp| is ref-counted.
+ state_ = State::kDone;
+ }
+}
+
+bool Request::ProcessPostData(const char* key,
+ const char* filename,
+ const char* content_type,
+ const char* transfer_encoding,
+ const char* data,
+ uint64_t off,
+ size_t size) {
+ if (off > 0)
+ return AppendPostFieldData(key, data, size);
+
+ return AddPostFieldData(key, filename, content_type, transfer_encoding, data,
+ size);
+}
+
+
+bool Request::AddRawRequestData(const void* data, size_t size) {
+ const uint8_t* byte_data_ = static_cast<const uint8_t*>(data);
+ raw_data_.insert(raw_data_.end(), byte_data_, byte_data_ + size);
+ return true;
+}
+
+bool Request::AddPostFieldData(const char* key,
+ const char* filename,
+ const char* content_type,
+ const char* transfer_encoding,
+ const char* data,
+ size_t size) {
+ if (filename) {
+ std::unique_ptr<FileInfo> file_info{
+ new FileInfo{key, filename, content_type ? content_type : "",
+ transfer_encoding ? transfer_encoding : ""}};
+ file_info->data.assign(data, data + size);
+ file_info_.push_back(std::move(file_info));
+ last_posted_data_was_file_ = true;
+ return true;
+ }
+ std::string value{data, size};
+ post_data_.emplace_back(key, value);
+ last_posted_data_was_file_ = false;
+ return true;
+}
+
+bool Request::AppendPostFieldData(const char* key,
+ const char* data,
+ size_t size) {
+ if (last_posted_data_was_file_) {
+ CHECK(!file_info_.empty());
+ CHECK(file_info_.back()->field_name == key);
+ FileInfo* file_info = file_info_.back().get();
+ file_info->data.insert(file_info->data.end(), data, data + size);
+ return true;
+ }
+
+ CHECK(!post_data_.empty());
+ CHECK(post_data_.back().first == key);
+ post_data_.back().second.append(data, size);
+ return true;
+}
+
+} // namespace webservd
diff --git a/webservd/request.h b/webservd/request.h
new file mode 100644
index 0000000..15b3184
--- /dev/null
+++ b/webservd/request.h
@@ -0,0 +1,170 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_REQUEST_H_
+#define WEBSERVER_WEBSERVD_REQUEST_H_
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include <base/macros.h>
+
+struct MHD_Connection;
+struct MHD_PostProcessor;
+
+namespace webservd {
+
+class ProtocolHandler;
+
+using PairOfStrings = std::pair<std::string, std::string>;
+
+// This class represents the file information about a file uploaded via
+// POST request using multipart/form-data request.
+class FileInfo final {
+ public:
+ FileInfo(const std::string& in_field_name,
+ const std::string& in_file_name,
+ const std::string& in_content_type,
+ const std::string& in_transfer_encoding);
+
+ // The name of the form field for the file upload.
+ std::string field_name;
+ // The name of the file name specified in the form field.
+ std::string file_name;
+ // The content type of the file data.
+ std::string content_type;
+ // Data transfer encoding specified. Could be empty if no transfer encoding
+ // was specified.
+ std::string transfer_encoding;
+ // The file content data.
+ std::vector<uint8_t> data;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileInfo);
+};
+
+// A class that represents the HTTP request data.
+class Request final {
+ public:
+ Request(const std::string& request_handler_id,
+ const std::string& url,
+ const std::string& method,
+ MHD_Connection* connection,
+ ProtocolHandler* protocol_handler);
+ ~Request();
+
+ // Obtains the content data of uploaded file identified by |file_id|.
+ bool GetFileData(int file_id, std::vector<uint8_t>* contents);
+
+ // Finishes the request and provides the reply data.
+ bool Complete(
+ int32_t status_code,
+ const std::vector<std::tuple<std::string, std::string>>& headers,
+ const std::vector<uint8_t>& data);
+
+ // Helper function to provide the string data and mime type.
+ bool Complete(
+ int32_t status_code,
+ const std::vector<std::tuple<std::string, std::string>>& headers,
+ const std::string& mime_type,
+ const std::string& data);
+
+ // Returns the unique ID of this request (GUID).
+ const std::string& GetID() const { return id_; }
+
+ // Returns the unique ID of the request handler this request is processed by
+ // (GUID).
+ const std::string& GetRequestHandlerID() const { return request_handler_id_; }
+
+ // Returns the unique ID of the protocol handler this request is received
+ // from (GUID or "http"/"https" for the two default handlers).
+ const std::string& GetProtocolHandlerID() const;
+
+ // Returns the object path of the HTTP request (e.g. "/privet/info").
+ const std::string& GetURL() const { return url_; }
+
+ // Returns the request method (e.g. "GET", "POST", ...).
+ const std::string& GetMethod() const { return method_; }
+
+ // Returns the raw body of the request, or empty byte array of the request
+ // had no body or a POST request has been parsed into form data.
+ const std::vector<uint8_t>& GetBody() const { return raw_data_; }
+
+ // Returns the POST form field data.
+ const std::vector<PairOfStrings>& GetDataPost() const { return post_data_; }
+
+ // Returns query parameters specified on the URL (as in "?param=value").
+ const std::vector<PairOfStrings>& GetDataGet() const { return get_data_; }
+
+ // Returns the information about any files uploaded as part of POST request.
+ const std::vector<std::unique_ptr<FileInfo>>& GetFileInfo() const {
+ return file_info_;
+ }
+
+ // Returns the HTTP request headers.
+ const std::vector<PairOfStrings>& GetHeaders() const { return headers_; }
+
+ private:
+ friend class RequestHelper;
+ friend class ServerHelper;
+
+ enum class State { kIdle, kWaitingForResponse, kResponseReceived, kDone };
+
+ // Helper methods for processing request data coming from the raw HTTP
+ // connection.
+ // Helper callback methods used by ProtocolHandler's ConnectionHandler to
+ // transfer request headers and data to the Request object.
+ bool BeginRequestData();
+ bool AddRequestData(const void* data, size_t size);
+ void EndRequestData();
+
+ // Callback for libmicrohttpd's PostProcessor.
+ bool ProcessPostData(const char* key,
+ const char* filename,
+ const char* content_type,
+ const char* transfer_encoding,
+ const char* data,
+ uint64_t off,
+ size_t size);
+
+ // These methods parse the request headers and data so they can be accessed
+ // by request handlers later.
+ bool AddRawRequestData(const void* data, size_t size);
+ bool AddPostFieldData(const char* key,
+ const char* filename,
+ const char* content_type,
+ const char* transfer_encoding,
+ const char* data,
+ size_t size);
+ bool AppendPostFieldData(const char* key, const char* data, size_t size);
+
+ std::string id_;
+ std::string request_handler_id_;
+ std::string url_;
+ std::string method_;
+ MHD_Connection* connection_{nullptr};
+ MHD_PostProcessor* post_processor_{nullptr};
+ std::vector<uint8_t> raw_data_;
+ bool last_posted_data_was_file_{false};
+ State state_{State::kIdle};
+
+ std::vector<PairOfStrings> post_data_;
+ std::vector<PairOfStrings> get_data_;
+ std::vector<std::unique_ptr<FileInfo>> file_info_;
+ std::vector<PairOfStrings> headers_;
+
+ int response_status_code_{0};
+ std::vector<uint8_t> response_data_;
+ std::vector<PairOfStrings> response_headers_;
+ ProtocolHandler* protocol_handler_;
+
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_REQUEST_H_
diff --git a/webservd/request_handler_interface.h b/webservd/request_handler_interface.h
new file mode 100644
index 0000000..1cde9c4
--- /dev/null
+++ b/webservd/request_handler_interface.h
@@ -0,0 +1,31 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
+#define WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+namespace webservd {
+
+class Request;
+
+// An abstract interface to dispatch of HTTP requests to remote handlers over
+// implementation-specific IPC (e.g. D-Bus).
+class RequestHandlerInterface {
+ public:
+ RequestHandlerInterface() = default;
+ virtual ~RequestHandlerInterface() = default;
+
+ virtual void HandleRequest(Request* request) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RequestHandlerInterface);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_REQUEST_HANDLER_INTERFACE_H_
diff --git a/webservd/server.cc b/webservd/server.cc
new file mode 100644
index 0000000..87f1654
--- /dev/null
+++ b/webservd/server.cc
@@ -0,0 +1,156 @@
+// Copyright 2015 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 "webserver/webservd/server.h"
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <limits>
+
+#include <base/rand_util.h>
+#include <base/strings/stringprintf.h>
+#include <chromeos/dbus/async_event_sequencer.h>
+#include <chromeos/dbus/exported_object_manager.h>
+
+#include "webserver/webservd/dbus_protocol_handler.h"
+#include "webserver/webservd/protocol_handler.h"
+#include "webserver/webservd/utils.h"
+
+using chromeos::dbus_utils::AsyncEventSequencer;
+using chromeos::dbus_utils::DBusObject;
+using chromeos::dbus_utils::ExportedObjectManager;
+
+namespace webservd {
+
+Server::Server(ExportedObjectManager* object_manager,
+ uint16_t http_port,
+ uint16_t https_port,
+ bool debug)
+ : dbus_object_{new DBusObject{
+ object_manager, object_manager->GetBus(),
+ org::chromium::WebServer::ServerAdaptor::GetObjectPath()}},
+ http_port_{http_port},
+ https_port_{https_port},
+ debug_{debug} {
+ dbus_adaptor_.SetDefaultHttp(dbus::ObjectPath{"/"});
+ dbus_adaptor_.SetDefaultHttps(dbus::ObjectPath{"/"});
+}
+
+Server::~Server() {}
+
+void Server::RegisterAsync(
+ const AsyncEventSequencer::CompletionAction& completion_callback) {
+ scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
+ dbus_adaptor_.RegisterWithDBusObject(dbus_object_.get());
+
+ if (http_port_)
+ CreateProtocolHandler(http_port_, ProtocolHandler::kHttp, false);
+ if (https_port_)
+ CreateProtocolHandler(https_port_, ProtocolHandler::kHttps, true);
+
+ dbus_object_->RegisterAsync(
+ sequencer->GetHandler("Failed exporting Server.", true));
+
+ for (const auto& pair : protocol_handler_map_) {
+ pair.second->RegisterAsync(
+ sequencer->GetHandler("Failed exporting ProtocolHandler.", false));
+ }
+ sequencer->OnAllTasksCompletedCall({completion_callback});
+}
+
+std::string Server::Ping() {
+ return "Web Server is running";
+}
+
+void Server::ProtocolHandlerStarted(ProtocolHandler* handler) {
+ CHECK(protocol_handler_map_.find(handler) == protocol_handler_map_.end())
+ << "Protocol handler already registered";
+ std::string dbus_id;
+ if (handler->GetID() == ProtocolHandler::kHttp ||
+ handler->GetID() == ProtocolHandler::kHttps)
+ dbus_id = handler->GetID();
+ else
+ dbus_id = std::to_string(++last_protocol_handler_index_);
+
+ std::string path = base::StringPrintf("/org/chromium/WebServer/Servers/%s",
+ dbus_id.c_str());
+ dbus::ObjectPath object_path{path};
+ std::unique_ptr<DBusProtocolHandler> dbus_protocol_handler{
+ new DBusProtocolHandler{dbus_object_->GetObjectManager().get(),
+ object_path,
+ handler,
+ this}
+ };
+ protocol_handler_map_.emplace(handler, std::move(dbus_protocol_handler));
+ if (handler->GetID() == ProtocolHandler::kHttp)
+ dbus_adaptor_.SetDefaultHttps(object_path);
+ else if (handler->GetID() == ProtocolHandler::kHttps)
+ dbus_adaptor_.SetDefaultHttp(object_path);
+}
+
+void Server::ProtocolHandlerStopped(ProtocolHandler* handler) {
+ CHECK_EQ(1u, protocol_handler_map_.erase(handler))
+ << "Unknown protocol handler";
+}
+
+void Server::CreateProtocolHandler(uint16_t port,
+ const std::string& id,
+ bool use_tls) {
+ std::unique_ptr<ProtocolHandler> protocol_handler{
+ new ProtocolHandler{id, this}};
+
+ if (use_tls) {
+ InitTlsData();
+ if (protocol_handler->StartWithTLS(port,
+ TLS_private_key_,
+ TLS_certificate_,
+ TLS_certificate_fingerprint_))
+ protocol_handlers_.emplace(id, std::move(protocol_handler));
+ } else {
+ if (protocol_handler->Start(port))
+ protocol_handlers_.emplace(id, std::move(protocol_handler));
+ }
+}
+
+void Server::InitTlsData() {
+ if (!TLS_certificate_.empty())
+ return; // Already initialized.
+
+ // TODO(avakulenko): verify these constants and provide sensible values
+ // for the long-term.
+ const int kKeyLengthBits = 1024;
+ const base::TimeDelta kCertExpiration = base::TimeDelta::FromDays(365);
+ const char kCommonName[] = "Brillo device";
+
+ // Create the X509 certificate.
+ int cert_serial_number = base::RandInt(0, std::numeric_limits<int>::max());
+ auto cert =
+ CreateCertificate(cert_serial_number, kCertExpiration, kCommonName);
+
+ // Create RSA key pair.
+ auto rsa_key_pair = GenerateRSAKeyPair(kKeyLengthBits);
+
+ // Store the private key to a temp buffer.
+ // Do not assign it to |TLS_private_key_| yet until the end when we are sure
+ // everything else has worked out.
+ chromeos::SecureBlob private_key = StoreRSAPrivateKey(rsa_key_pair.get());
+
+ // Create EVP key and set it to the certificate.
+ auto key = std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)>{EVP_PKEY_new(),
+ EVP_PKEY_free};
+ CHECK(key.get());
+ // Transfer ownership of |rsa_key_pair| to |key|.
+ CHECK(EVP_PKEY_assign_RSA(key.get(), rsa_key_pair.release()));
+ CHECK(X509_set_pubkey(cert.get(), key.get()));
+
+ // Sign the certificate.
+ CHECK(X509_sign(cert.get(), key.get(), EVP_sha256()));
+
+ TLS_certificate_ = StoreCertificate(cert.get());
+ TLS_certificate_fingerprint_ = GetSha256Fingerprint(cert.get());
+ TLS_private_key_ = std::move(private_key);
+}
+
+} // namespace webservd
diff --git a/webservd/server.h b/webservd/server.h
new file mode 100644
index 0000000..c2bd2b2
--- /dev/null
+++ b/webservd/server.h
@@ -0,0 +1,81 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_SERVER_H_
+#define WEBSERVER_WEBSERVD_SERVER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+#include <chromeos/dbus/dbus_object.h>
+#include <chromeos/secure_blob.h>
+
+#include "webservd/org.chromium.WebServer.Server.h"
+#include "webserver/webservd/server_interface.h"
+
+namespace chromeos {
+namespace dbus_utils {
+class ExportedObjectManager;
+} // dbus_utils
+} // chromeos
+
+namespace webservd {
+
+class DBusProtocolHandler;
+class DBusServerRequest;
+
+// Top-level D-Bus object to interface with the server as a whole.
+class Server final : public org::chromium::WebServer::ServerInterface,
+ public ServerInterface {
+ public:
+ Server(chromeos::dbus_utils::ExportedObjectManager* object_manager,
+ uint16_t http_port,
+ uint16_t https_port,
+ bool debug);
+ // Need to off-line the destructor to allow |protocol_handler_map_| to contain
+ // a forward-declared pointer to DBusProtocolHandler.
+ ~Server();
+
+ void RegisterAsync(
+ const chromeos::dbus_utils::AsyncEventSequencer::CompletionAction& cb);
+
+ // Overrides from org::chromium::WebServer::ServerInterface.
+ std::string Ping() override;
+
+ // Overrides from webservd::ServerInterface.
+ void ProtocolHandlerStarted(ProtocolHandler* handler) override;
+ void ProtocolHandlerStopped(ProtocolHandler* handler) override;
+ bool UseDebugInfo() const override { return debug_; }
+
+ scoped_refptr<dbus::Bus> GetBus() { return dbus_object_->GetBus(); }
+
+ private:
+ void CreateProtocolHandler(uint16_t port,
+ const std::string& id,
+ bool use_tls);
+ void InitTlsData();
+
+ org::chromium::WebServer::ServerAdaptor dbus_adaptor_{this};
+ std::unique_ptr<chromeos::dbus_utils::DBusObject> dbus_object_;
+ uint16_t http_port_{0};
+ uint16_t https_port_{0};
+ bool debug_{false};
+ int last_protocol_handler_index_{0};
+ chromeos::Blob TLS_certificate_;
+ chromeos::Blob TLS_certificate_fingerprint_;
+ chromeos::SecureBlob TLS_private_key_;
+
+ std::map<ProtocolHandler*,
+ std::unique_ptr<DBusProtocolHandler>> protocol_handler_map_;
+ std::map<std::string, std::unique_ptr<ProtocolHandler>> protocol_handlers_;
+
+ DISALLOW_COPY_AND_ASSIGN(Server);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_SERVER_H_
diff --git a/webservd/server_interface.h b/webservd/server_interface.h
new file mode 100644
index 0000000..165aabb
--- /dev/null
+++ b/webservd/server_interface.h
@@ -0,0 +1,46 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
+#define WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
+
+#include <base/macros.h>
+
+namespace webservd {
+
+class ProtocolHandler;
+
+// An abstract interface to expose Server object to IPC transport layer such as
+// D-Bus.
+class ServerInterface {
+ public:
+ ServerInterface() = default;
+
+ // Called by ProtocolHandler to notify the server that a new protocol handler
+ // appears online or goes offline.
+ virtual void ProtocolHandlerStarted(ProtocolHandler* handler) = 0;
+ virtual void ProtocolHandlerStopped(ProtocolHandler* handler) = 0;
+
+ // Helper method to obtain the Debug-Request flag. This flag controls
+ // additional logging from libmicrohttpd as well as more debug info sent
+ // to browser in case of an error response.
+ virtual bool UseDebugInfo() const = 0;
+
+ protected:
+ // This interface should not be used to control the life-time of the class
+ // that derives from this interface. This is especially important when a mock
+ // server class is used. Since the life-time of the mock must be controlled
+ // by the test itself, we can't let some business logic suddenly delete
+ // the instance of this interface.
+ // So, just declare the destructor as protected, so nobody can just call
+ // delete on a pointer to ServerInterface.
+ ~ServerInterface() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ServerInterface);
+};
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_SERVER_INTERFACE_H_
diff --git a/webservd/utils.cc b/webservd/utils.cc
new file mode 100644
index 0000000..6c7da29
--- /dev/null
+++ b/webservd/utils.cc
@@ -0,0 +1,91 @@
+// Copyright 2015 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 "webserver/webservd/utils.h"
+
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+
+namespace webservd {
+
+std::unique_ptr<X509, void(*)(X509*)> CreateCertificate(
+ int serial_number,
+ const base::TimeDelta& cert_expiration,
+ const std::string& common_name) {
+ auto cert = std::unique_ptr<X509, void(*)(X509*)>{X509_new(), X509_free};
+ CHECK(cert.get());
+ X509_set_version(cert.get(), 2); // Using X.509 v3 certificate...
+
+ // Set certificate properties...
+ ASN1_INTEGER* sn = X509_get_serialNumber(cert.get());
+ ASN1_INTEGER_set(sn, serial_number);
+ X509_gmtime_adj(X509_get_notBefore(cert.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(cert.get()), cert_expiration.InSeconds());
+
+ // The issuer is the same as the subject, since this cert is self-signed.
+ X509_NAME* name = X509_get_subject_name(cert.get());
+ if (!common_name.empty()) {
+ X509_NAME_add_entry_by_txt(
+ name, "CN", MBSTRING_UTF8,
+ reinterpret_cast<const unsigned char*>(common_name.c_str()),
+ common_name.length(), -1, 0);
+ }
+ X509_set_issuer_name(cert.get(), name);
+ return cert;
+}
+
+std::unique_ptr<RSA, void(*)(RSA*)> GenerateRSAKeyPair(int key_length_bits) {
+ // Create RSA key pair.
+ auto rsa_key_pair = std::unique_ptr<RSA, void(*)(RSA*)>{RSA_new(), RSA_free};
+ CHECK(rsa_key_pair.get());
+ auto big_num = std::unique_ptr<BIGNUM, void(*)(BIGNUM*)>{BN_new(), BN_free};
+ CHECK(big_num.get());
+ CHECK(BN_set_word(big_num.get(), 65537));
+ CHECK(RSA_generate_key_ex(rsa_key_pair.get(), key_length_bits, big_num.get(),
+ nullptr));
+ return rsa_key_pair;
+}
+
+chromeos::SecureBlob StoreRSAPrivateKey(RSA* rsa_key_pair) {
+ auto bio =
+ std::unique_ptr<BIO, void(*)(BIO*)>{BIO_new(BIO_s_mem()), BIO_vfree};
+ CHECK(bio);
+ CHECK(PEM_write_bio_RSAPrivateKey(bio.get(), rsa_key_pair, nullptr, nullptr,
+ 0, nullptr, nullptr));
+ uint8_t* buffer = nullptr;
+ size_t size = BIO_get_mem_data(bio.get(), &buffer);
+ CHECK_GT(size, 0u);
+ CHECK(buffer);
+ chromeos::SecureBlob key_blob(buffer, size);
+ chromeos::SecureMemset(buffer, 0, size);
+ return key_blob;
+}
+
+chromeos::Blob StoreCertificate(X509* cert) {
+ auto bio =
+ std::unique_ptr<BIO, void(*)(BIO*)>{BIO_new(BIO_s_mem()), BIO_vfree};
+ CHECK(bio);
+ CHECK(PEM_write_bio_X509(bio.get(), cert));
+ uint8_t* buffer = nullptr;
+ size_t size = BIO_get_mem_data(bio.get(), &buffer);
+ CHECK_GT(size, 0u);
+ CHECK(buffer);
+ return chromeos::Blob(buffer, buffer + size);
+}
+
+// Same as openssl x509 -fingerprint -sha256.
+chromeos::Blob GetSha256Fingerprint(X509* cert) {
+ chromeos::Blob fingerprint(256 / 8);
+ uint32_t len = 0;
+ CHECK(X509_digest(cert, EVP_sha256(), fingerprint.data(), &len));
+ CHECK_EQ(len, fingerprint.size());
+ return fingerprint;
+}
+
+} // namespace webservd
diff --git a/webservd/utils.h b/webservd/utils.h
new file mode 100644
index 0000000..b84545a
--- /dev/null
+++ b/webservd/utils.h
@@ -0,0 +1,39 @@
+// Copyright 2015 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.
+
+#ifndef WEBSERVER_WEBSERVD_UTILS_H_
+#define WEBSERVER_WEBSERVD_UTILS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+#include <openssl/ossl_typ.h>
+
+#include <base/time/time.h>
+#include <chromeos/secure_blob.h>
+
+namespace webservd {
+
+// Creates a new X509 certificate.
+std::unique_ptr<X509, void(*)(X509*)> CreateCertificate(
+ int serial_number,
+ const base::TimeDelta& cert_expiration,
+ const std::string& common_name);
+
+// Generates an RSA public-private key pair of the specified strength.
+std::unique_ptr<RSA, void(*)(RSA*)> GenerateRSAKeyPair(int key_length_bits);
+
+// Serializes a private key from the key pair into a PEM string and returns
+// it as a binary blob.
+chromeos::SecureBlob StoreRSAPrivateKey(RSA* rsa_key_pair);
+
+// Serializes an X509 certificate using PEM format.
+chromeos::Blob StoreCertificate(X509* cert);
+
+// Same as openssl x509 -fingerprint -sha256.
+chromeos::Blob GetSha256Fingerprint(X509* cert);
+
+} // namespace webservd
+
+#endif // WEBSERVER_WEBSERVD_UTILS_H_
diff --git a/webserver.gyp b/webserver.gyp
index 8376c2b..f101abc 100644
--- a/webserver.gyp
+++ b/webserver.gyp
@@ -13,20 +13,23 @@
'target_name': 'libwebserv-<(libbase_ver)',
'type': 'shared_library',
'variables': {
- 'exported_deps': [
- 'libmicrohttpd',
- ],
- 'deps': ['<@(exported_deps)'],
- 'dbus_adaptors_out_dir': 'include/webservd',
- 'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
+ # Not using dbus_service_config here deliberately in order not to
+ # get tied to some constant service name, since it will be
+ # provided by the consumer of libwebserv library.
+ 'dbus_service_config': '',
+ 'dbus_adaptors_out_dir': 'include/libwebserv',
},
- 'includes': ['../common-mk/deps.gypi'],
+ 'includes': [
+ '../common-mk/deps.gypi',
+ '../common-mk/generate-dbus-adaptors.gypi'
+ ],
'sources': [
- 'libwebserv/connection.cc',
- 'libwebserv/response.cc',
+ 'libwebserv/protocol_handler.cc',
'libwebserv/request.cc',
'libwebserv/request_handler_callback.cc',
+ 'libwebserv/response.cc',
'libwebserv/server.cc',
+ 'libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml',
],
'actions': [
{
@@ -35,9 +38,10 @@
'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
'mock_output_file': 'include/webservd/dbus-mocks.h',
'proxy_output_file': 'include/webservd/dbus-proxies.h',
+ 'dbus_adaptors_out_dir': '',
},
'sources': [
- 'webservd/dbus_bindings/org.chromium.WebServer.Manager.xml',
+ 'webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml',
'webservd/dbus_bindings/org.chromium.WebServer.Server.xml',
],
'includes': ['../common-mk/generate-dbus-proxies.gypi'],
@@ -50,18 +54,46 @@
'variables': {
'exported_deps': [
'libmicrohttpd',
+ 'openssl',
],
'deps': ['<@(exported_deps)'],
'dbus_adaptors_out_dir': 'include/webservd',
'dbus_service_config': 'webservd/dbus_bindings/dbus-service-config.json',
},
+ 'link_settings': {
+ 'libraries': [
+ '-lminijail',
+ ],
+ },
'sources': [
- 'webservd/dbus_bindings/org.chromium.WebServer.Manager.xml',
+ 'webservd/dbus_bindings/org.chromium.WebServer.ProtocolHandler.xml',
'webservd/dbus_bindings/org.chromium.WebServer.Server.xml',
+ 'webservd/dbus_protocol_handler.cc',
+ 'webservd/dbus_request_handler.cc',
'webservd/main.cc',
- 'webservd/manager.cc',
+ 'webservd/protocol_handler.cc',
+ 'webservd/request.cc',
+ 'webservd/server.cc',
+ 'webservd/utils.cc',
],
'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+ 'actions': [
+ {
+ 'action_name': 'generate-libwebserv-proxies',
+ 'variables': {
+ # Not using dbus_service_config here deliberately in order not to
+ # get tied to some constant service name, since it will be
+ # provided by the consumer of libwebserv library.
+ 'dbus_service_config': '',
+ 'mock_output_file': 'include/libwebserv/dbus-mocks.h',
+ 'proxy_output_file': 'include/libwebserv/dbus-proxies.h',
+ },
+ 'sources': [
+ 'libwebserv/dbus_bindings/org.chromium.WebServer.RequestHandler.xml',
+ ],
+ 'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+ },
+ ],
},
],
'conditions': [