Chromium Code Reviews| Index: services/sky_packaged_app_viewer/content_handler_impl.cc |
| diff --git a/services/sky_packaged_app_viewer/content_handler_impl.cc b/services/sky_packaged_app_viewer/content_handler_impl.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e2eda57c0f2bd3a114c933826359956c1b864846 |
| --- /dev/null |
| +++ b/services/sky_packaged_app_viewer/content_handler_impl.cc |
| @@ -0,0 +1,201 @@ |
| +// Copyright 2015 The Chromium 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 "services/sky_packaged_app_viewer/content_handler_impl.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/files/scoped_temp_dir.h" |
| +#include "mojo/common/data_pipe_utils.h" |
| +#include "mojo/public/cpp/application/connect.h" |
| +#include "mojo/public/cpp/bindings/strong_binding.h" |
| +#include "mojo/public/cpp/utility/run_loop.h" |
| +#include "mojo/services/http_server/public/cpp/http_server_util.h" |
| +#include "mojo/services/http_server/public/interfaces/http_server.mojom.h" |
| +#include "mojo/services/http_server/public/interfaces/http_server_factory.mojom.h" |
| +#include "third_party/zlib/google/zip_reader.h" |
| + |
| +namespace sky_packaged_app_viewer { |
| + |
| +class HttpHandler : public http_server::HttpHandler { |
| + public: |
| + HttpHandler(const base::Callback<void(uint16_t)>& handler_ready_callback, |
| + base::FilePath package_root, |
| + http_server::HttpServerFactoryPtr http_server_factory) |
| + : binding_(this), |
| + handler_ready_callback_(handler_ready_callback), |
| + package_root_(package_root) { |
| + http_server::HttpHandlerPtr http_handler_ptr; |
| + binding_.Bind(GetProxy(&http_handler_ptr)); |
| + |
| + http_server_factory->CreateHttpServer(GetProxy(&http_server_).Pass(), |
| + nullptr); |
| + http_server_->SetHandler( |
| + "/.*", http_handler_ptr.Pass(), |
| + base::Bind(&HttpHandler::AddHandlerCallback, base::Unretained(this))); |
| + } |
| + |
| + ~HttpHandler() override {} |
| + |
| + // http_server::HttpHandler: |
| + void HandleRequest(http_server::HttpRequestPtr request, |
| + const mojo::Callback<void(http_server::HttpResponsePtr)>& |
| + callback) override { |
| + std::string relative_path = request->relative_url.get().substr(1); |
| + |
| + // TODO(blundell): Add logic into creating sky_packaged_applications to |
| + // make it unnecessary to strip off the "packages/" prefix, namely, |
| + // putting all the dependent packages under "packages/". |
| + std::string packages_prefix = "packages/"; |
| + if (!relative_path.compare(0, packages_prefix.size(), packages_prefix)) |
| + relative_path.erase(0, packages_prefix.size()); |
| + |
| + base::FilePath entry_path = package_root_.Append(relative_path); |
| + std::string contents; |
| + if (!base::ReadFileToString(entry_path, &contents)) { |
| + NOTREACHED(); |
| + return; |
| + } |
| + |
| + callback.Run(http_server::CreateHttpResponse(200, contents)); |
| + } |
| + |
| + void AddHandlerCallback(bool result) { |
| + CHECK(result); |
| + http_server_->GetPort( |
| + base::Bind(&HttpHandler::GetPortCallback, base::Unretained(this))); |
| + } |
| + |
| + void GetPortCallback(uint16_t port) { handler_ready_callback_.Run(port); } |
| + |
| + mojo::Binding<http_server::HttpHandler> binding_; |
| + base::Callback<void(uint16_t)> handler_ready_callback_; |
| + base::FilePath package_root_; |
| + http_server::HttpServerPtr http_server_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(HttpHandler); |
| +}; |
| + |
| +class SkyPackagedApplication : public mojo::Application { |
| + public: |
| + SkyPackagedApplication(mojo::InterfaceRequest<mojo::Application> application, |
| + mojo::URLResponsePtr response) |
| + : binding_(this, application.Pass()), |
| + initial_response_(response.Pass()), |
| + port_(0) {} |
| + |
| + void Initialize(mojo::ShellPtr shell, |
| + mojo::Array<mojo::String> args, |
| + const mojo::String& url) override { |
| + shell_ = shell.Pass(); |
| + } |
| + |
| + void AcceptConnection(const mojo::String& requestor_url, |
| + mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| + mojo::ServiceProviderPtr exposed_services, |
| + const mojo::String& url) override { |
| + if (initial_response_) { |
| + // TODO(blundell): Once I'm saving pending requests, can just add |
| + // |services| and |exposed_services| to pending requests list instead of |
| + // threading them through. |
| + OnResponseReceived(mojo::URLLoaderPtr(), services.Pass(), |
| + exposed_services.Pass(), initial_response_.Pass()); |
| + } else { |
| + if (port_) { |
| + ConnectToSkyApplication(services.Pass(), exposed_services.Pass()); |
| + } else { |
| + // TODO(blundell): If the handler ready isn't yet, need to save |
| + // |services| and |exposed_services| and connect once the handler is |
| + // ready. |
| + DCHECK(0); |
| + } |
| + } |
| + } |
| + |
| + void RequestQuit() override { mojo::RunLoop::current()->Quit(); } |
| + |
| + private: |
| + void OnHandlerReady(mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| + mojo::ServiceProviderPtr exposed_services, |
| + uint16_t port) { |
| + port_ = port; |
| + ConnectToSkyApplication(services.Pass(), exposed_services.Pass()); |
| + } |
| + |
| + void ConnectToSkyApplication( |
| + mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| + mojo::ServiceProviderPtr exposed_services) { |
| + shell_->ConnectToApplication( |
| + "http://localhost:" + std::to_string(port_) + "/main.sky", |
|
eseidel
2015/04/15 19:45:04
We're eventually going to remove the main.sky and
|
| + services.Pass(), exposed_services.Pass()); |
|
qsr
2015/04/15 21:56:29
I think we should not do this. Application identit
|
| + } |
| + |
| + void OnResponseReceived( |
| + mojo::URLLoaderPtr loader, |
| + mojo::InterfaceRequest<mojo::ServiceProvider> services, |
| + mojo::ServiceProviderPtr exposed_services, |
| + mojo::URLResponsePtr response) { |
| + // Extract the application. |
| + CHECK(unpacked_app_directory_.CreateUniqueTempDir()); |
| + ExtractApplication(response.Pass()); |
|
eseidel
2015/04/15 19:45:04
Eventually one could just serve straight out of th
|
| + base::FilePath package_root = unpacked_app_directory_.path(); |
| + |
| + // Start up the http handler that will serve the application. |
| + mojo::ServiceProviderPtr service_provider; |
| + shell_->ConnectToApplication("mojo:http_server", |
| + mojo::GetProxy(&service_provider), nullptr); |
| + http_server::HttpServerFactoryPtr http_server_factory; |
| + mojo::ConnectToService(service_provider.get(), &http_server_factory); |
| + |
| + http_handler_.reset(new HttpHandler( |
| + base::Bind(&SkyPackagedApplication::OnHandlerReady, |
| + base::Unretained(this), base::Passed(&services), |
| + base::Passed(&exposed_services)), |
| + package_root, http_server_factory.Pass())); |
| + } |
| + |
| + // TODO(blundell): The below two functions should be moved into a utility |
| + // file somewhere where they can be shared by this file, the Dart content |
| + // handler, and the Python content handler. |
| + void ExtractApplication(mojo::URLResponsePtr response) { |
| + zip::ZipReader reader; |
| + const std::string input_data = CopyToString(response->body.Pass()); |
| + CHECK(reader.OpenFromString(input_data)); |
| + base::FilePath temp_dir_path = unpacked_app_directory_.path(); |
| + while (reader.HasMore()) { |
| + CHECK(reader.OpenCurrentEntryInZip()); |
| + CHECK(reader.ExtractCurrentEntryIntoDirectory(temp_dir_path)); |
| + CHECK(reader.AdvanceToNextEntry()); |
| + } |
| + } |
| + |
| + std::string CopyToString(mojo::ScopedDataPipeConsumerHandle body) { |
| + std::string body_str; |
| + bool result = mojo::common::BlockingCopyToString(body.Pass(), &body_str); |
| + DCHECK(result); |
| + return body_str; |
| + } |
| + |
| + mojo::StrongBinding<mojo::Application> binding_; |
| + scoped_ptr<HttpHandler> http_handler_; |
| + mojo::ShellPtr shell_; |
| + mojo::URLResponsePtr initial_response_; |
| + uint16_t port_; |
| + base::ScopedTempDir unpacked_app_directory_; |
| +}; |
| + |
| +ContentHandlerImpl::ContentHandlerImpl( |
| + mojo::InterfaceRequest<mojo::ContentHandler> request) |
| + : binding_(this, request.Pass()) { |
| +} |
| + |
| +ContentHandlerImpl::~ContentHandlerImpl() { |
| +} |
| + |
| +void ContentHandlerImpl::StartApplication( |
| + mojo::InterfaceRequest<mojo::Application> application, |
| + mojo::URLResponsePtr response) { |
| + new SkyPackagedApplication(application.Pass(), response.Pass()); |
| +} |
| + |
| +} // namespace sky_packaged_app_viewer |