blob: 9bb528d48dd8a8edd78a17e8a9eb74383497e1ac [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/h5vcc/dial/dial_server.h"
#include "base/threading/thread_task_runner_handle.h"
#if defined(DIAL_SERVER)
#include <memory>
#include <utility>
#include "base/strings/string_util.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/network/network_module.h"
#include "cobalt/web/context.h"
#include "cobalt/web/environment_settings.h"
#include "net/dial/dial_service_handler.h"
#include "net/server/http_server_request_info.h"
namespace cobalt {
namespace h5vcc {
namespace dial {
namespace {
DialServer::Method StringToMethod(const std::string& method) {
if (base::LowerCaseEqualsASCII(method, "get")) {
return DialServer::kGet;
} else if (base::LowerCaseEqualsASCII(method, "post")) {
return DialServer::kPost;
} else if (base::LowerCaseEqualsASCII(method, "delete")) {
return DialServer::kDelete;
}
NOTREACHED();
return DialServer::kGet;
}
} // namespace
class DialServer::ServiceHandler : public net::DialServiceHandler {
public:
ServiceHandler(const base::WeakPtr<DialServer>& dial_server,
const std::string& service_name);
// net::DialServiceHandler implementation.
const std::string& service_name() const override { return service_name_; }
void HandleRequest(const std::string& path,
const net::HttpServerRequestInfo& request,
const CompletionCB& completion_cb) override;
private:
~ServiceHandler();
void OnHandleRequest(const std::string& path,
const net::HttpServerRequestInfo& request,
const CompletionCB& completion_cb);
// Keep a weak ptr to the dial server, so that it can be safely destroyed
// while we are deregistering our handler from the DialService.
base::WeakPtr<DialServer> dial_server_;
std::string service_name_;
// The message loop we should dispatch HandleRequest to. This is the
// message loop we were constructed on.
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
};
DialServer::DialServer(script::EnvironmentSettings* settings,
const std::string& service_name) {
service_handler_ = new ServiceHandler(AsWeakPtr(), service_name);
auto* web_settings =
base::polymorphic_downcast<web::EnvironmentSettings*>(settings);
DCHECK(web_settings);
if (web_settings && web_settings->context()->network_module()) {
dial_service_proxy_ =
web_settings->context()->network_module()->dial_service_proxy();
}
if (dial_service_proxy_) {
DLOG(INFO) << "DialServer running at: "
<< dial_service_proxy_->host_address();
dial_service_proxy_->Register(service_handler_);
}
}
DialServer::~DialServer() {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (dial_service_proxy_) {
dial_service_proxy_->Deregister(service_handler_);
}
}
bool DialServer::OnDelete(
const std::string& path,
const DialHttpRequestCallbackWrapper::ScriptValue& handler) {
return AddHandler(kDelete, path, handler);
}
bool DialServer::OnGet(
const std::string& path,
const DialHttpRequestCallbackWrapper::ScriptValue& handler) {
return AddHandler(kGet, path, handler);
}
bool DialServer::OnPost(
const std::string& path,
const DialHttpRequestCallbackWrapper::ScriptValue& handler) {
return AddHandler(kPost, path, handler);
}
const std::string& DialServer::service_name() const {
return service_handler_->service_name();
}
bool DialServer::AddHandler(
Method method, const std::string& path,
const DialHttpRequestCallbackWrapper::ScriptValue& handler) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
scoped_refptr<DialHttpRequestCallbackWrapper> callback =
new DialHttpRequestCallbackWrapper(this, handler);
std::pair<CallbackMap::iterator, bool> ret =
callback_map_[method].insert(std::make_pair(path, callback));
return ret.second;
}
// Transform the net request info into a JS version and run the script
// callback, if there is one registered. Return the response and status.
std::unique_ptr<net::HttpServerResponseInfo> DialServer::RunCallback(
const std::string& path, const net::HttpServerRequestInfo& request) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
std::unique_ptr<net::HttpServerResponseInfo> response;
Method method = StringToMethod(request.method);
CallbackMap::const_iterator it = callback_map_[method].find(path);
if (it == callback_map_[method].end()) {
// No handler for this method. Signal failure.
return response;
}
scoped_refptr<DialHttpRequestCallbackWrapper> callback = it->second;
scoped_refptr<DialHttpRequest> dial_request = new DialHttpRequest(
path, request.method, request.data, dial_service_proxy_->host_address());
scoped_refptr<DialHttpResponse> dial_response =
new DialHttpResponse(path, request.method);
script::CallbackResult<bool> ret =
callback->callback.value().Run(dial_request, dial_response);
if (ret.result) {
response = dial_response->ToHttpServerResponseInfo();
}
return response;
}
DialServer::ServiceHandler::ServiceHandler(
const base::WeakPtr<DialServer>& dial_server,
const std::string& service_name)
: dial_server_(dial_server), service_name_(service_name) {
task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
DialServer::ServiceHandler::~ServiceHandler() {}
void DialServer::ServiceHandler::HandleRequest(
const std::string& path, const net::HttpServerRequestInfo& request,
const CompletionCB& completion_cb) {
// This gets called on the DialService/Network thread.
// Post it to the WebModule thread.
DCHECK_NE(base::ThreadTaskRunnerHandle::Get(), task_runner_);
task_runner_->PostTask(
FROM_HERE, base::Bind(&ServiceHandler::OnHandleRequest, this, path,
request, completion_cb));
}
void DialServer::ServiceHandler::OnHandleRequest(
const std::string& path, const net::HttpServerRequestInfo& request,
const CompletionCB& completion_cb) {
DCHECK_EQ(base::ThreadTaskRunnerHandle::Get(), task_runner_);
if (!dial_server_) {
completion_cb.Run(std::unique_ptr<net::HttpServerResponseInfo>());
return;
}
std::unique_ptr<net::HttpServerResponseInfo> response =
dial_server_->RunCallback(path, request);
completion_cb.Run(std::move(response));
}
} // namespace dial
} // namespace h5vcc
} // namespace cobalt
#endif // defined(DIAL_SERVER)