2017-11-09 20:00:47 +01:00
|
|
|
// Copyright (c) 2017 The Chromium Embedded Framework 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 "libcef/browser/server_impl.h"
|
|
|
|
|
|
|
|
#include "libcef/browser/thread_util.h"
|
|
|
|
#include "libcef/common/request_impl.h"
|
|
|
|
#include "libcef/common/task_runner_impl.h"
|
|
|
|
|
|
|
|
#include "base/bind.h"
|
|
|
|
#include "base/format_macros.h"
|
|
|
|
#include "base/memory/ptr_util.h"
|
|
|
|
#include "base/strings/stringprintf.h"
|
|
|
|
#include "base/threading/thread.h"
|
|
|
|
#include "net/base/net_errors.h"
|
|
|
|
#include "net/http/http_request_headers.h"
|
|
|
|
#include "net/server/http_server_request_info.h"
|
|
|
|
#include "net/server/http_server_response_info.h"
|
|
|
|
#include "net/socket/server_socket.h"
|
|
|
|
#include "net/socket/tcp_server_socket.h"
|
2018-03-20 21:15:08 +01:00
|
|
|
#include "net/traffic_annotation/network_traffic_annotation.h"
|
2017-11-09 20:00:47 +01:00
|
|
|
|
|
|
|
#define CEF_CURRENTLY_ON_HT() CurrentlyOnHandlerThread()
|
|
|
|
#define CEF_REQUIRE_HT() DCHECK(CEF_CURRENTLY_ON_HT())
|
|
|
|
|
|
|
|
#define CEF_REQUIRE_HT_RETURN(var) \
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) { \
|
|
|
|
NOTREACHED() << "called on invalid thread"; \
|
|
|
|
return var; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CEF_REQUIRE_HT_RETURN_VOID() \
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) { \
|
|
|
|
NOTREACHED() << "called on invalid thread"; \
|
|
|
|
return; \
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CEF_POST_TASK_HT(task) task_runner_->PostTask(FROM_HERE, task);
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2017-11-17 17:33:50 +01:00
|
|
|
const char kReferrerLowerCase[] = "referer";
|
|
|
|
|
2017-11-09 20:00:47 +01:00
|
|
|
// Wrap a string in a unique_ptr to avoid extra copies.
|
|
|
|
std::unique_ptr<std::string> CreateUniqueString(const void* data,
|
|
|
|
size_t data_size) {
|
|
|
|
std::unique_ptr<std::string> ptr;
|
|
|
|
if (data && data_size > 0) {
|
|
|
|
ptr.reset(new std::string(static_cast<const char*>(data), data_size));
|
|
|
|
} else {
|
|
|
|
ptr.reset(new std::string());
|
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefRequest> CreateRequest(const std::string& address,
|
|
|
|
const net::HttpServerRequestInfo& info,
|
|
|
|
bool is_websocket) {
|
|
|
|
DCHECK(!address.empty());
|
|
|
|
DCHECK(!info.method.empty());
|
|
|
|
DCHECK(!info.path.empty());
|
|
|
|
|
|
|
|
CefRefPtr<CefPostData> post_data;
|
|
|
|
if (!info.data.empty()) {
|
|
|
|
post_data = CefPostData::Create();
|
|
|
|
CefRefPtr<CefPostDataElement> post_element = CefPostDataElement::Create();
|
|
|
|
post_element->SetToBytes(info.data.size(), info.data.data());
|
|
|
|
post_data->AddElement(post_element);
|
|
|
|
}
|
|
|
|
|
2017-11-17 17:33:50 +01:00
|
|
|
std::string referer;
|
|
|
|
|
2017-11-09 20:00:47 +01:00
|
|
|
CefRequest::HeaderMap header_map;
|
|
|
|
if (!info.headers.empty()) {
|
|
|
|
net::HttpServerRequestInfo::HeadersMap::const_iterator it =
|
|
|
|
info.headers.begin();
|
|
|
|
for (; it != info.headers.end(); ++it) {
|
2017-11-17 17:33:50 +01:00
|
|
|
// Don't include Referer in the header map.
|
|
|
|
if (base::LowerCaseEqualsASCII(it->first, kReferrerLowerCase)) {
|
|
|
|
referer = it->second;
|
|
|
|
} else {
|
|
|
|
header_map.insert(std::make_pair(it->first, it->second));
|
|
|
|
}
|
2017-11-09 20:00:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefRequestImpl> request = new CefRequestImpl();
|
|
|
|
request->Set((is_websocket ? "ws://" : "http://") + address + info.path,
|
|
|
|
info.method, post_data, header_map);
|
2017-11-17 17:33:50 +01:00
|
|
|
if (!referer.empty())
|
|
|
|
request->SetReferrer(referer, REFERRER_POLICY_DEFAULT);
|
2017-11-09 20:00:47 +01:00
|
|
|
request->SetReadOnly(true);
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Callback implementation for WebSocket acceptance. Always executes on the UI
|
|
|
|
// thread so we can avoid multiple execution by clearing the |impl_| reference.
|
|
|
|
class AcceptWebSocketCallback : public CefCallback {
|
|
|
|
public:
|
|
|
|
AcceptWebSocketCallback(CefRefPtr<CefServerImpl> impl,
|
|
|
|
int connection_id,
|
|
|
|
net::HttpServerRequestInfo request_info)
|
|
|
|
: impl_(impl),
|
|
|
|
connection_id_(connection_id),
|
|
|
|
request_info_(request_info) {}
|
|
|
|
|
|
|
|
~AcceptWebSocketCallback() override {
|
|
|
|
if (impl_)
|
|
|
|
impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Continue() override {
|
|
|
|
if (!CEF_CURRENTLY_ON_UIT()) {
|
|
|
|
CEF_POST_TASK(CEF_UIT,
|
|
|
|
base::BindOnce(&AcceptWebSocketCallback::Continue, this));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!impl_)
|
|
|
|
return;
|
|
|
|
impl_->ContinueWebSocketRequest(connection_id_, request_info_, true);
|
|
|
|
impl_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Cancel() override {
|
|
|
|
if (!CEF_CURRENTLY_ON_UIT()) {
|
|
|
|
CEF_POST_TASK(CEF_UIT,
|
|
|
|
base::BindOnce(&AcceptWebSocketCallback::Cancel, this));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!impl_)
|
|
|
|
return;
|
|
|
|
impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
|
|
|
|
impl_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
CefRefPtr<CefServerImpl> impl_;
|
|
|
|
int connection_id_;
|
|
|
|
net::HttpServerRequestInfo request_info_;
|
|
|
|
|
|
|
|
IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(AcceptWebSocketCallback);
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(AcceptWebSocketCallback);
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
// CefServer
|
|
|
|
|
|
|
|
// static
|
|
|
|
void CefServer::CreateServer(const CefString& address,
|
|
|
|
uint16 port,
|
|
|
|
int backlog,
|
|
|
|
CefRefPtr<CefServerHandler> handler) {
|
|
|
|
CefRefPtr<CefServerImpl> server(new CefServerImpl(handler));
|
|
|
|
server->Start(address, port, backlog);
|
|
|
|
}
|
|
|
|
|
|
|
|
// CefServerImpl
|
|
|
|
|
|
|
|
struct CefServerImpl::ConnectionInfo {
|
|
|
|
ConnectionInfo() : is_websocket(false), is_websocket_pending(false) {}
|
|
|
|
|
|
|
|
// True if this connection is a WebSocket connection.
|
|
|
|
bool is_websocket;
|
|
|
|
bool is_websocket_pending;
|
|
|
|
};
|
|
|
|
|
|
|
|
CefServerImpl::CefServerImpl(CefRefPtr<CefServerHandler> handler)
|
|
|
|
: handler_(handler) {
|
|
|
|
DCHECK(handler_);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::Start(const std::string& address,
|
|
|
|
uint16 port,
|
|
|
|
int backlog) {
|
|
|
|
DCHECK(!address.empty());
|
|
|
|
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefServerImpl::StartOnUIThread, this,
|
|
|
|
address, port, backlog));
|
|
|
|
}
|
|
|
|
|
|
|
|
CefRefPtr<CefTaskRunner> CefServerImpl::GetTaskRunner() {
|
|
|
|
if (task_runner_)
|
|
|
|
return new CefTaskRunnerImpl(task_runner_);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::Shutdown() {
|
|
|
|
CEF_POST_TASK_HT(
|
|
|
|
base::BindOnce(&CefServerImpl::ShutdownOnHandlerThread, this));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefServerImpl::IsRunning() {
|
|
|
|
CEF_REQUIRE_HT_RETURN(false);
|
|
|
|
return !!server_.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
CefString CefServerImpl::GetAddress() {
|
|
|
|
return address_;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefServerImpl::HasConnection() {
|
|
|
|
CEF_REQUIRE_HT_RETURN(false);
|
|
|
|
return !connection_info_map_.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefServerImpl::IsValidConnection(int connection_id) {
|
|
|
|
CEF_REQUIRE_HT_RETURN(false);
|
|
|
|
return connection_info_map_.find(connection_id) != connection_info_map_.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendHttp200Response(int connection_id,
|
|
|
|
const CefString& content_type,
|
|
|
|
const void* data,
|
|
|
|
size_t data_size) {
|
|
|
|
SendHttp200ResponseInternal(connection_id, content_type,
|
|
|
|
CreateUniqueString(data, data_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendHttp404Response(int connection_id) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp404Response, this,
|
|
|
|
connection_id));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (info->is_websocket) {
|
|
|
|
LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
|
|
|
|
<< connection_id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->Send404(connection_id, NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendHttp500Response(int connection_id,
|
|
|
|
const CefString& error_message) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp500Response, this,
|
|
|
|
connection_id, error_message));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (info->is_websocket) {
|
|
|
|
LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
|
|
|
|
<< connection_id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->Send500(connection_id, error_message, NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendHttpResponse(int connection_id,
|
|
|
|
int response_code,
|
|
|
|
const CefString& content_type,
|
|
|
|
int64 content_length,
|
|
|
|
const HeaderMap& extra_headers) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttpResponse, this,
|
|
|
|
connection_id, response_code, content_type,
|
|
|
|
content_length, extra_headers));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (info->is_websocket) {
|
|
|
|
LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
|
|
|
|
<< connection_id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
net::HttpServerResponseInfo response(
|
|
|
|
static_cast<net::HttpStatusCode>(response_code));
|
|
|
|
|
|
|
|
HeaderMap::const_iterator it = extra_headers.begin();
|
|
|
|
for (; it != extra_headers.end(); ++it)
|
|
|
|
response.AddHeader(it->first, it->second);
|
|
|
|
|
|
|
|
response.AddHeader(net::HttpRequestHeaders::kContentType, content_type);
|
|
|
|
if (content_length >= 0) {
|
|
|
|
response.AddHeader(
|
|
|
|
net::HttpRequestHeaders::kContentLength,
|
|
|
|
base::StringPrintf("%" PRIuS, static_cast<size_t>(content_length)));
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->SendResponse(connection_id, response, NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
if (content_length == 0) {
|
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendRawData(int connection_id,
|
|
|
|
const void* data,
|
|
|
|
size_t data_size) {
|
|
|
|
if (!data || data_size == 0)
|
|
|
|
return;
|
|
|
|
SendRawDataInternal(connection_id, CreateUniqueString(data, data_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::CloseConnection(int connection_id) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(
|
|
|
|
base::BindOnce(&CefServerImpl::CloseConnection, this, connection_id));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ValidateServer() && GetConnectionInfo(connection_id)) {
|
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendWebSocketMessage(int connection_id,
|
|
|
|
const void* data,
|
|
|
|
size_t data_size) {
|
|
|
|
if (!data || data_size == 0)
|
|
|
|
return;
|
|
|
|
SendWebSocketMessageInternal(connection_id,
|
|
|
|
CreateUniqueString(data, data_size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::ContinueWebSocketRequest(
|
|
|
|
int connection_id,
|
|
|
|
const net::HttpServerRequestInfo& request_info,
|
|
|
|
bool allow) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::ContinueWebSocketRequest,
|
|
|
|
this, connection_id, request_info, allow));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
DCHECK(info);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DCHECK(info->is_websocket);
|
|
|
|
DCHECK(info->is_websocket_pending);
|
|
|
|
if (!info->is_websocket || !info->is_websocket_pending)
|
|
|
|
return;
|
|
|
|
|
|
|
|
info->is_websocket_pending = false;
|
|
|
|
|
|
|
|
if (allow) {
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->AcceptWebSocket(connection_id, request_info,
|
|
|
|
NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
handler_->OnWebSocketConnected(this, connection_id);
|
|
|
|
} else {
|
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendHttp200ResponseInternal(
|
|
|
|
int connection_id,
|
|
|
|
const CefString& content_type,
|
|
|
|
std::unique_ptr<std::string> data) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp200ResponseInternal,
|
|
|
|
this, connection_id, content_type,
|
|
|
|
base::Passed(std::move(data))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (info->is_websocket) {
|
|
|
|
LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
|
|
|
|
<< connection_id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->Send200(connection_id, *data, content_type,
|
|
|
|
NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
server_->Close(connection_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendRawDataInternal(int connection_id,
|
|
|
|
std::unique_ptr<std::string> data) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendRawDataInternal, this,
|
|
|
|
connection_id,
|
|
|
|
base::Passed(std::move(data))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!GetConnectionInfo(connection_id))
|
|
|
|
return;
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->SendRaw(connection_id, *data, NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::SendWebSocketMessageInternal(
|
|
|
|
int connection_id,
|
|
|
|
std::unique_ptr<std::string> data) {
|
|
|
|
if (!CEF_CURRENTLY_ON_HT()) {
|
|
|
|
CEF_POST_TASK_HT(
|
|
|
|
base::BindOnce(&CefServerImpl::SendWebSocketMessageInternal, this,
|
|
|
|
connection_id, base::Passed(std::move(data))));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ValidateServer())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!info->is_websocket || info->is_websocket_pending) {
|
|
|
|
LOG(ERROR) << "Invalid attempt to send WebSocket message for connection_id "
|
|
|
|
<< connection_id;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-20 21:15:08 +01:00
|
|
|
server_->SendOverWebSocket(connection_id, *data, NO_TRAFFIC_ANNOTATION_YET);
|
2017-11-09 20:00:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::OnConnect(int connection_id) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
CreateConnectionInfo(connection_id);
|
|
|
|
handler_->OnClientConnected(this, connection_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::OnHttpRequest(
|
|
|
|
int connection_id,
|
|
|
|
const net::HttpServerRequestInfo& request_info) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
DCHECK(info);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DCHECK(!info->is_websocket);
|
|
|
|
|
|
|
|
handler_->OnHttpRequest(this, connection_id, request_info.peer.ToString(),
|
|
|
|
CreateRequest(address_, request_info, false));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::OnWebSocketRequest(
|
|
|
|
int connection_id,
|
|
|
|
const net::HttpServerRequestInfo& request_info) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
DCHECK(info);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DCHECK(!info->is_websocket);
|
|
|
|
info->is_websocket = true;
|
|
|
|
info->is_websocket_pending = true;
|
|
|
|
|
|
|
|
// Will eventually result in a call to ContinueWebSocketRequest.
|
|
|
|
CefRefPtr<CefCallback> callback =
|
|
|
|
new AcceptWebSocketCallback(this, connection_id, request_info);
|
|
|
|
handler_->OnWebSocketRequest(
|
|
|
|
this, connection_id, request_info.peer.ToString(),
|
|
|
|
CreateRequest(address_, request_info, true), callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::OnWebSocketMessage(int connection_id,
|
|
|
|
const std::string& data) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
ConnectionInfo* info = GetConnectionInfo(connection_id);
|
|
|
|
if (!info)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DCHECK(info->is_websocket);
|
|
|
|
DCHECK(!info->is_websocket_pending);
|
|
|
|
|
|
|
|
handler_->OnWebSocketMessage(this, connection_id, data.data(), data.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::OnClose(int connection_id) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
RemoveConnectionInfo(connection_id);
|
|
|
|
handler_->OnClientDisconnected(this, connection_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::StartOnUIThread(const std::string& address,
|
|
|
|
uint16 port,
|
|
|
|
int backlog) {
|
|
|
|
CEF_REQUIRE_UIT();
|
|
|
|
DCHECK(!thread_);
|
|
|
|
|
|
|
|
std::unique_ptr<base::Thread> thread(
|
|
|
|
new base::Thread(base::StringPrintf("%s:%d", address.c_str(), port)));
|
|
|
|
base::Thread::Options options;
|
|
|
|
options.message_loop_type = base::MessageLoop::TYPE_IO;
|
|
|
|
if (thread->StartWithOptions(options)) {
|
|
|
|
// Add a reference that will be released in ShutdownOnUIThread().
|
|
|
|
AddRef();
|
|
|
|
|
|
|
|
thread_ = std::move(thread);
|
|
|
|
task_runner_ = thread_->task_runner();
|
|
|
|
|
|
|
|
CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::StartOnHandlerThread, this,
|
|
|
|
address, port, backlog));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::StartOnHandlerThread(const std::string& address,
|
|
|
|
uint16 port,
|
|
|
|
int backlog) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
std::unique_ptr<net::ServerSocket> socket(
|
|
|
|
new net::TCPServerSocket(nullptr, net::NetLogSource()));
|
|
|
|
if (socket->ListenWithAddressAndPort(address, port, backlog) == net::OK) {
|
|
|
|
server_.reset(new net::HttpServer(std::move(socket), this));
|
|
|
|
|
|
|
|
net::IPEndPoint address;
|
|
|
|
if (server_->GetLocalAddress(&address) == net::OK)
|
|
|
|
address_ = address.ToString();
|
|
|
|
}
|
|
|
|
|
|
|
|
handler_->OnServerCreated(this);
|
|
|
|
|
|
|
|
if (!server_) {
|
|
|
|
// Server failed to start.
|
|
|
|
handler_->OnServerDestroyed(this);
|
|
|
|
|
|
|
|
CEF_POST_TASK(CEF_UIT,
|
|
|
|
base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::ShutdownOnHandlerThread() {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
if (server_) {
|
|
|
|
// Stop the server.
|
|
|
|
server_.reset();
|
|
|
|
|
|
|
|
if (!connection_info_map_.empty()) {
|
|
|
|
// Clear |connection_info_map_| first so any calls from
|
|
|
|
// OnClientDisconnected will fail as expected.
|
|
|
|
ConnectionInfoMap temp_map;
|
|
|
|
temp_map.swap(connection_info_map_);
|
|
|
|
|
|
|
|
// OnClose won't be called for clients that are connected when the server
|
|
|
|
// shuts down, so send the disconnected notification here.
|
|
|
|
ConnectionInfoMap::const_iterator it = temp_map.begin();
|
|
|
|
for (; it != temp_map.end(); ++it) {
|
|
|
|
handler_->OnClientDisconnected(this, it->first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
handler_->OnServerDestroyed(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
CEF_POST_TASK(CEF_UIT,
|
|
|
|
base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::ShutdownOnUIThread() {
|
|
|
|
CEF_REQUIRE_UIT();
|
|
|
|
|
|
|
|
handler_ = nullptr;
|
|
|
|
|
|
|
|
if (thread_) {
|
|
|
|
// Stop the handler thread as a background task so the UI thread isn't
|
|
|
|
// blocked.
|
2018-03-20 21:15:08 +01:00
|
|
|
CEF_POST_BACKGROUND_TASK(
|
2017-11-09 20:00:47 +01:00
|
|
|
BindOnce([](std::unique_ptr<base::Thread>) {}, std::move(thread_)));
|
|
|
|
|
|
|
|
// Release the reference that was added in StartupOnUIThread().
|
|
|
|
Release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefServerImpl::ValidateServer() const {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
if (!server_) {
|
|
|
|
LOG(ERROR) << "Server is not running";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefServerImpl::ConnectionInfo* CefServerImpl::CreateConnectionInfo(
|
|
|
|
int connection_id) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
|
|
|
|
#if DCHECK_IS_ON()
|
|
|
|
ConnectionInfoMap::const_iterator it =
|
|
|
|
connection_info_map_.find(connection_id);
|
|
|
|
DCHECK(it == connection_info_map_.end());
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ConnectionInfo* info = new ConnectionInfo();
|
|
|
|
connection_info_map_.insert(
|
|
|
|
std::make_pair(connection_id, base::WrapUnique(info)));
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
CefServerImpl::ConnectionInfo* CefServerImpl::GetConnectionInfo(
|
|
|
|
int connection_id) const {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
ConnectionInfoMap::const_iterator it =
|
|
|
|
connection_info_map_.find(connection_id);
|
|
|
|
if (it != connection_info_map_.end())
|
|
|
|
return it->second.get();
|
|
|
|
|
|
|
|
LOG(ERROR) << "Invalid connection_id " << connection_id;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CefServerImpl::RemoveConnectionInfo(int connection_id) {
|
|
|
|
CEF_REQUIRE_HT();
|
|
|
|
ConnectionInfoMap::iterator it = connection_info_map_.find(connection_id);
|
|
|
|
DCHECK(it != connection_info_map_.end());
|
|
|
|
if (it != connection_info_map_.end())
|
|
|
|
connection_info_map_.erase(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CefServerImpl::CurrentlyOnHandlerThread() const {
|
|
|
|
return task_runner_ && task_runner_->BelongsToCurrentThread();
|
|
|
|
}
|