libweave: Extracted interaction with libwebserv into WebServClient

Class implements weave::HttpServer using libwebserv and it's going to be
moved out of libweave.

BUG=brillo:1252
TEST='FEATURES=test emerge-gizmo buffet'

Change-Id: I069831d50e39478c281073a62ad81ef63f3065a6
Reviewed-on: https://chromium-review.googlesource.com/290141
Commit-Queue: Vitaly Buka <vitalybuka@chromium.org>
Trybot-Ready: Vitaly Buka <vitalybuka@chromium.org>
Tested-by: Vitaly Buka <vitalybuka@chromium.org>
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index 1c619fa..c72b4d8 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -67,7 +67,7 @@
         '../libweave/src/privet/privet_types.cc',
         '../libweave/src/privet/publisher.cc',
         '../libweave/src/privet/security_manager.cc',
-        '../libweave/src/privet/privet_types.cc',
+        '../libweave/src/privet/webserv_client.cc',
         '../libweave/src/privet/wifi_bootstrap_manager.cc',
         '../libweave/src/privet/wifi_ssid_generator.cc',
         '../libweave/src/registration_status.cc',
diff --git a/libweave/include/weave/http_server.h b/libweave/include/weave/http_server.h
new file mode 100644
index 0000000..db30252
--- /dev/null
+++ b/libweave/include/weave/http_server.h
@@ -0,0 +1,54 @@
+// 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 LIBWEAVE_INCLUDE_WEAVE_HTTP_SERVER_H_
+#define LIBWEAVE_INCLUDE_WEAVE_HTTP_SERVER_H_
+
+#include <string>
+#include <vector>
+
+#include <chromeos/secure_blob.h>
+
+#include <base/callback.h>
+
+namespace weave {
+
+class HttpServer {
+ public:
+  class Request {
+   public:
+    virtual const std::string& GetPath() const = 0;
+    virtual std::string GetFirstHeader(const std::string& name) const = 0;
+    virtual const std::vector<uint8_t>& GetData() const = 0;
+
+   protected:
+    virtual ~Request() = default;
+  };
+
+  using OnReplyCallback = base::Callback<void(int status_code,
+                                              const std::string& data,
+                                              const std::string& mime_type)>;
+
+  using OnRequestCallback =
+      base::Callback<void(const Request& request,
+                          const OnReplyCallback& callback)>;
+
+  // Adds notification callback for server started/stopped serving requests.
+  virtual void AddOnStateChangedCallback(const base::Closure& callback) = 0;
+
+  // Adds callback called on new http/https requests with the given path prefix.
+  virtual void AddRequestHandler(const std::string& path_prefix,
+                                 const OnRequestCallback& callback) = 0;
+
+  virtual uint16_t GetHttpPort() const = 0;
+  virtual uint16_t GetHttpsPort() const = 0;
+  virtual const chromeos::Blob& GetHttpsCertificateFingerprint() const = 0;
+
+ protected:
+  virtual ~HttpServer() = default;
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_INCLUDE_WEAVE_HTTP_SERVER_H_
diff --git a/libweave/src/privet/privet_manager.cc b/libweave/src/privet/privet_manager.cc
index 32142f7..3e9f604 100644
--- a/libweave/src/privet/privet_manager.cc
+++ b/libweave/src/privet/privet_manager.cc
@@ -11,6 +11,7 @@
 #include <base/bind.h>
 #include <base/command_line.h>
 #include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
 #include <base/memory/weak_ptr.h>
 #include <base/scoped_observer.h>
 #include <base/strings/string_number_conversions.h>
@@ -34,7 +35,9 @@
 #include "libweave/src/privet/device_delegate.h"
 #include "libweave/src/privet/privet_handler.h"
 #include "libweave/src/privet/publisher.h"
+#include "libweave/src/privet/webserv_client.h"
 #include "weave/network.h"
+#include "weave/http_server.h"
 
 namespace weave {
 namespace privet {
@@ -42,14 +45,6 @@
 namespace {
 
 using chromeos::dbus_utils::AsyncEventSequencer;
-using libwebserv::ProtocolHandler;
-using libwebserv::Request;
-using libwebserv::Response;
-
-std::string GetFirstHeader(const Request& request, const std::string& name) {
-  std::vector<std::string> headers = request.GetHeader(name);
-  return headers.empty() ? std::string() : headers.front();
-}
 
 }  // namespace
 
@@ -93,30 +88,16 @@
       new PrivetHandler(cloud_.get(), device_.get(), security_.get(),
                         wifi_bootstrap_manager_.get(), publisher_.get()));
 
-  web_server_.reset(new libwebserv::Server);
-  web_server_->OnProtocolHandlerConnected(base::Bind(
-      &Manager::OnProtocolHandlerConnected, weak_ptr_factory_.GetWeakPtr()));
-  web_server_->OnProtocolHandlerDisconnected(base::Bind(
-      &Manager::OnProtocolHandlerDisconnected, weak_ptr_factory_.GetWeakPtr()));
-
-  web_server_->Connect(bus, buffet::kServiceName,
-                       sequencer->GetHandler("Server::Connect failed.", true),
-                       base::Bind(&base::DoNothing),
-                       base::Bind(&base::DoNothing));
-
-  web_server_->GetDefaultHttpHandler()->AddHandlerCallback(
-      "/privet/", "",
-      base::Bind(&Manager::PrivetRequestHandler, base::Unretained(this)));
-  web_server_->GetDefaultHttpsHandler()->AddHandlerCallback(
-      "/privet/", "",
-      base::Bind(&Manager::PrivetRequestHandler, base::Unretained(this)));
+  web_server_.reset(new WebServClient{bus, sequencer});
+  web_server_->AddOnStateChangedCallback(base::Bind(
+      &Manager::OnHttpServerStatusChanged, weak_ptr_factory_.GetWeakPtr()));
+  web_server_->AddRequestHandler("/privet/",
+                                 base::Bind(&Manager::PrivetRequestHandler,
+                                            weak_ptr_factory_.GetWeakPtr()));
   if (options.enable_ping) {
-    web_server_->GetDefaultHttpHandler()->AddHandlerCallback(
-        "/privet/ping", chromeos::http::request_type::kGet,
-        base::Bind(&Manager::HelloWorldHandler, base::Unretained(this)));
-    web_server_->GetDefaultHttpsHandler()->AddHandlerCallback(
-        "/privet/ping", chromeos::http::request_type::kGet,
-        base::Bind(&Manager::HelloWorldHandler, base::Unretained(this)));
+    web_server_->AddRequestHandler("/privet/ping",
+                                   base::Bind(&Manager::HelloWorldHandler,
+                                              weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
@@ -141,55 +122,55 @@
 }
 
 void Manager::Shutdown() {
-  web_server_->Disconnect();
+  web_server_.reset();
 }
 
 void Manager::OnDeviceInfoChanged() {
   OnChanged();
 }
 
-void Manager::PrivetRequestHandler(std::unique_ptr<Request> request,
-                                   std::unique_ptr<Response> response) {
+void Manager::PrivetRequestHandler(
+    const HttpServer::Request& request,
+    const HttpServer::OnReplyCallback& callback) {
   std::string auth_header =
-      GetFirstHeader(*request, chromeos::http::request_header::kAuthorization);
+      request.GetFirstHeader(chromeos::http::request_header::kAuthorization);
   if (auth_header.empty() && disable_security_)
     auth_header = "Privet anonymous";
-  std::string data(request->GetData().begin(), request->GetData().end());
+  std::string data(request.GetData().begin(), request.GetData().end());
   VLOG(3) << "Input: " << data;
 
   base::DictionaryValue empty;
   std::unique_ptr<base::Value> value;
-  const base::DictionaryValue* dictionary = nullptr;
+  const base::DictionaryValue* dictionary = &empty;
 
-  if (data.empty()) {
-    dictionary = &empty;
-  } else {
-    std::string content_type = chromeos::mime::RemoveParameters(
-        GetFirstHeader(*request, chromeos::http::request_header::kContentType));
-    if (content_type == chromeos::mime::application::kJson) {
-      value.reset(base::JSONReader::Read(data).release());
-      if (value)
-        value->GetAsDictionary(&dictionary);
-    }
+  std::string content_type = chromeos::mime::RemoveParameters(
+      request.GetFirstHeader(chromeos::http::request_header::kContentType));
+  if (content_type == chromeos::mime::application::kJson) {
+    value.reset(base::JSONReader::Read(data).release());
+    if (value)
+      value->GetAsDictionary(&dictionary);
   }
 
   privet_handler_->HandleRequest(
-      request->GetPath(), auth_header, dictionary,
-      base::Bind(&Manager::PrivetResponseHandler, base::Unretained(this),
-                 base::Passed(&response)));
+      request.GetPath(), auth_header, dictionary,
+      base::Bind(&Manager::PrivetResponseHandler,
+                 weak_ptr_factory_.GetWeakPtr(), callback));
 }
 
-void Manager::PrivetResponseHandler(std::unique_ptr<Response> response,
+void Manager::PrivetResponseHandler(const HttpServer::OnReplyCallback& callback,
                                     int status,
                                     const base::DictionaryValue& output) {
   VLOG(3) << "status: " << status << ", Output: " << output;
-  response->ReplyWithJson(status, &output);
+  std::string data;
+  base::JSONWriter::WriteWithOptions(
+      output, base::JSONWriter::OPTIONS_PRETTY_PRINT, &data);
+  callback.Run(status, data, chromeos::mime::application::kJson);
 }
 
-void Manager::HelloWorldHandler(std::unique_ptr<Request> request,
-                                std::unique_ptr<Response> response) {
-  response->ReplyWithText(chromeos::http::status_code::Ok, "Hello, world!",
-                          chromeos::mime::text::kPlain);
+void Manager::HelloWorldHandler(const HttpServer::Request& request,
+                                const HttpServer::OnReplyCallback& callback) {
+  callback.Run(chromeos::http::status_code::Ok, "Hello, world!",
+               chromeos::mime::text::kPlain);
 }
 
 void Manager::OnChanged() {
@@ -201,27 +182,15 @@
   OnChanged();
 }
 
-void Manager::OnProtocolHandlerConnected(ProtocolHandler* protocol_handler) {
-  if (protocol_handler->GetName() == ProtocolHandler::kHttp) {
-    device_->SetHttpPort(*protocol_handler->GetPorts().begin());
+void Manager::OnHttpServerStatusChanged() {
+  if (device_->GetHttpEnpoint().first != web_server_->GetHttpPort()) {
+    device_->SetHttpPort(web_server_->GetHttpPort());
     if (publisher_)
       publisher_->Update();
-  } else if (protocol_handler->GetName() == ProtocolHandler::kHttps) {
-    device_->SetHttpsPort(*protocol_handler->GetPorts().begin());
-    security_->SetCertificateFingerprint(
-        protocol_handler->GetCertificateFingerprint());
   }
-}
-
-void Manager::OnProtocolHandlerDisconnected(ProtocolHandler* protocol_handler) {
-  if (protocol_handler->GetName() == ProtocolHandler::kHttp) {
-    device_->SetHttpPort(0);
-    if (publisher_)
-      publisher_->Update();
-  } else if (protocol_handler->GetName() == ProtocolHandler::kHttps) {
-    device_->SetHttpsPort(0);
-    security_->SetCertificateFingerprint({});
-  }
+  device_->SetHttpsPort(web_server_->GetHttpsPort());
+  security_->SetCertificateFingerprint(
+      web_server_->GetHttpsCertificateFingerprint());
 }
 
 }  // namespace privet
diff --git a/libweave/src/privet/privet_manager.h b/libweave/src/privet/privet_manager.h
index bdcb298..29b803d 100644
--- a/libweave/src/privet/privet_manager.h
+++ b/libweave/src/privet/privet_manager.h
@@ -17,6 +17,7 @@
 #include "libweave/src/privet/security_manager.h"
 #include "libweave/src/privet/wifi_bootstrap_manager.h"
 #include "weave/device.h"
+#include "weave/http_server.h"
 
 namespace chromeos {
 namespace dbus_utils {
@@ -47,6 +48,7 @@
 class PrivetHandler;
 class Publisher;
 class SecurityManager;
+class WebServClient;
 
 class Manager : public Privet, public CloudDelegate::Observer {
  public:
@@ -77,24 +79,20 @@
   // CloudDelegate::Observer
   void OnDeviceInfoChanged() override;
 
-  void PrivetRequestHandler(std::unique_ptr<libwebserv::Request> request,
-                            std::unique_ptr<libwebserv::Response> response);
+  void PrivetRequestHandler(const HttpServer::Request& request,
+                            const HttpServer::OnReplyCallback& callback);
 
-  void PrivetResponseHandler(std::unique_ptr<libwebserv::Response> response,
+  void PrivetResponseHandler(const HttpServer::OnReplyCallback& callback,
                              int status,
                              const base::DictionaryValue& output);
 
-  void HelloWorldHandler(std::unique_ptr<libwebserv::Request> request,
-                         std::unique_ptr<libwebserv::Response> response);
+  void HelloWorldHandler(const HttpServer::Request& request,
+                         const HttpServer::OnReplyCallback& callback);
 
   void OnChanged();
   void OnConnectivityChanged(bool online);
 
-  void OnProtocolHandlerConnected(
-      libwebserv::ProtocolHandler* protocol_handler);
-
-  void OnProtocolHandlerDisconnected(
-      libwebserv::ProtocolHandler* protocol_handler);
+  void OnHttpServerStatusChanged();
 
   bool disable_security_{false};
   std::unique_ptr<CloudDelegate> cloud_;
@@ -103,7 +101,8 @@
   std::unique_ptr<WifiBootstrapManager> wifi_bootstrap_manager_;
   std::unique_ptr<Publisher> publisher_;
   std::unique_ptr<PrivetHandler> privet_handler_;
-  std::unique_ptr<libwebserv::Server> web_server_;
+
+  std::unique_ptr<WebServClient> web_server_;
 
   ScopedObserver<CloudDelegate, CloudDelegate::Observer> cloud_observer_{this};
 
diff --git a/libweave/src/privet/webserv_client.cc b/libweave/src/privet/webserv_client.cc
new file mode 100644
index 0000000..b8ba74d
--- /dev/null
+++ b/libweave/src/privet/webserv_client.cc
@@ -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.
+
+#include "libweave/src/privet/webserv_client.h"
+//
+#include <memory>
+#include <string>
+
+#include <libwebserv/protocol_handler.h>
+#include <libwebserv/request.h>
+#include <libwebserv/response.h>
+#include <libwebserv/server.h>
+
+#include "buffet/dbus_constants.h"
+
+namespace weave {
+namespace privet {
+
+namespace {
+
+class RequestImpl : public HttpServer::Request {
+ public:
+  explicit RequestImpl(std::unique_ptr<libwebserv::Request> request)
+      : request_{std::move(request)} {}
+  ~RequestImpl() override {}
+
+  // HttpServer::Request implementation.
+  const std::string& GetPath() const override { return request_->GetPath(); }
+  std::string GetFirstHeader(const std::string& name) const override {
+    return request_->GetFirstHeader(name);
+  }
+  const std::vector<uint8_t>& GetData() const override {
+    return request_->GetData();
+  }
+
+ private:
+  std::unique_ptr<libwebserv::Request> request_;
+
+  DISALLOW_COPY_AND_ASSIGN(RequestImpl);
+};
+
+}  // namespace
+
+WebServClient::~WebServClient() {
+  web_server_->Disconnect();
+}
+
+void WebServClient::AddOnStateChangedCallback(const base::Closure& callback) {
+  on_state_changed_callbacks_.push_back(callback);
+  callback.Run();
+}
+
+void WebServClient::AddRequestHandler(const std::string& path_prefix,
+                                      const OnRequestCallback& callback) {
+  web_server_->GetDefaultHttpHandler()->AddHandlerCallback(
+      path_prefix, "", base::Bind(&WebServClient::OnRequest,
+                                  weak_ptr_factory_.GetWeakPtr(), callback));
+  web_server_->GetDefaultHttpsHandler()->AddHandlerCallback(
+      path_prefix, "", base::Bind(&WebServClient::OnRequest,
+                                  weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+uint16_t WebServClient::GetHttpPort() const {
+  return http_port_;
+}
+
+uint16_t WebServClient::GetHttpsPort() const {
+  return https_port_;
+}
+
+const chromeos::Blob& WebServClient::GetHttpsCertificateFingerprint() const {
+  return certificate_;
+}
+
+WebServClient::WebServClient(
+    const scoped_refptr<dbus::Bus>& bus,
+    chromeos::dbus_utils::AsyncEventSequencer* sequencer) {
+  web_server_.reset(new libwebserv::Server);
+  web_server_->OnProtocolHandlerConnected(
+      base::Bind(&WebServClient::OnProtocolHandlerConnected,
+                 weak_ptr_factory_.GetWeakPtr()));
+  web_server_->OnProtocolHandlerDisconnected(
+      base::Bind(&WebServClient::OnProtocolHandlerDisconnected,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  web_server_->Connect(bus, buffet::kServiceName,
+                       sequencer->GetHandler("Server::Connect failed.", true),
+                       base::Bind(&base::DoNothing),
+                       base::Bind(&base::DoNothing));
+}
+
+void WebServClient::OnRequest(const OnRequestCallback& callback,
+                              std::unique_ptr<libwebserv::Request> request,
+                              std::unique_ptr<libwebserv::Response> response) {
+  callback.Run(
+      RequestImpl{std::move(request)},
+      base::Bind(&WebServClient::OnResponse, weak_ptr_factory_.GetWeakPtr(),
+                 base::Passed(&response)));
+}
+
+void WebServClient::OnResponse(std::unique_ptr<libwebserv::Response> response,
+                               int status_code,
+                               const std::string& data,
+                               const std::string& mime_type) {
+  response->Reply(status_code, data.data(), data.size(), mime_type);
+}
+
+void WebServClient::OnProtocolHandlerConnected(
+    libwebserv::ProtocolHandler* protocol_handler) {
+  if (protocol_handler->GetName() == libwebserv::ProtocolHandler::kHttp) {
+    http_port_ = *protocol_handler->GetPorts().begin();
+  } else if (protocol_handler->GetName() ==
+             libwebserv::ProtocolHandler::kHttps) {
+    https_port_ = *protocol_handler->GetPorts().begin();
+    certificate_ = protocol_handler->GetCertificateFingerprint();
+  }
+  for (const auto& cb : on_state_changed_callbacks_)
+    cb.Run();
+}
+
+void WebServClient::OnProtocolHandlerDisconnected(
+    libwebserv::ProtocolHandler* protocol_handler) {
+  if (protocol_handler->GetName() == libwebserv::ProtocolHandler::kHttp) {
+    http_port_ = 0;
+  } else if (protocol_handler->GetName() ==
+             libwebserv::ProtocolHandler::kHttps) {
+    https_port_ = 0;
+    certificate_.clear();
+  }
+  for (const auto& cb : on_state_changed_callbacks_)
+    cb.Run();
+}
+
+}  // namespace privet
+}  // namespace weave
diff --git a/libweave/src/privet/webserv_client.h b/libweave/src/privet/webserv_client.h
new file mode 100644
index 0000000..43eb859
--- /dev/null
+++ b/libweave/src/privet/webserv_client.h
@@ -0,0 +1,82 @@
+// 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 LIBWEAVE_SRC_PRIVET_WEBSERV_CLIENT_H_
+#define LIBWEAVE_SRC_PRIVET_WEBSERV_CLIENT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+
+#include "weave/http_server.h"
+
+namespace dbus {
+class Bus;
+}
+
+namespace chromeos {
+namespace dbus_utils {
+class AsyncEventSequencer;
+}
+}
+
+namespace libwebserv {
+class ProtocolHandler;
+class Request;
+class Response;
+class Server;
+}
+
+namespace weave {
+namespace privet {
+
+// Wrapper around libwebserv that implements HttpServer interface.
+class WebServClient : public HttpServer {
+ public:
+  WebServClient(const scoped_refptr<dbus::Bus>& bus,
+                chromeos::dbus_utils::AsyncEventSequencer* sequencer);
+  ~WebServClient() override;
+
+  // HttpServer implementation.
+  void AddOnStateChangedCallback(const base::Closure& callback) override;
+  void AddRequestHandler(const std::string& path_prefix,
+                         const OnRequestCallback& callback) override;
+  uint16_t GetHttpPort() const override;
+  uint16_t GetHttpsPort() const override;
+  const chromeos::Blob& GetHttpsCertificateFingerprint() const override;
+
+ private:
+  void OnRequest(const OnRequestCallback& callback,
+                 std::unique_ptr<libwebserv::Request> request,
+                 std::unique_ptr<libwebserv::Response> response);
+
+  void OnResponse(std::unique_ptr<libwebserv::Response> response,
+                  int status_code,
+                  const std::string& data,
+                  const std::string& mime_type);
+
+  void OnProtocolHandlerConnected(
+      libwebserv::ProtocolHandler* protocol_handler);
+
+  void OnProtocolHandlerDisconnected(
+      libwebserv::ProtocolHandler* protocol_handler);
+
+  uint16_t http_port_{0};
+  uint16_t https_port_{0};
+  chromeos::Blob certificate_;
+
+  std::vector<base::Closure> on_state_changed_callbacks_;
+
+  std::unique_ptr<libwebserv::Server> web_server_;
+
+  base::WeakPtrFactory<WebServClient> weak_ptr_factory_{this};
+  DISALLOW_COPY_AND_ASSIGN(WebServClient);
+};
+
+}  // namespace privet
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_PRIVET_WEBSERV_CLIENT_H_