mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			662 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			662 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // 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"
 | |
| #include "net/traffic_annotation/network_traffic_annotation.h"
 | |
| 
 | |
| #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 {
 | |
| 
 | |
| const char kReferrerLowerCase[] = "referer";
 | |
| 
 | |
| // 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);
 | |
|   }
 | |
| 
 | |
|   std::string referer;
 | |
| 
 | |
|   CefRequest::HeaderMap header_map;
 | |
|   if (!info.headers.empty()) {
 | |
|     net::HttpServerRequestInfo::HeadersMap::const_iterator it =
 | |
|         info.headers.begin();
 | |
|     for (; it != info.headers.end(); ++it) {
 | |
|       // 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));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CefRefPtr<CefRequestImpl> request = new CefRequestImpl();
 | |
|   request->Set((is_websocket ? "ws://" : "http://") + address + info.path,
 | |
|                info.method, post_data, header_map);
 | |
|   if (!referer.empty())
 | |
|     request->SetReferrer(referer, REFERRER_POLICY_DEFAULT);
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   server_->Send404(connection_id, MISSING_TRAFFIC_ANNOTATION);
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   server_->Send500(connection_id, error_message, MISSING_TRAFFIC_ANNOTATION);
 | |
|   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)));
 | |
|   }
 | |
| 
 | |
|   server_->SendResponse(connection_id, response, MISSING_TRAFFIC_ANNOTATION);
 | |
|   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) {
 | |
|     server_->AcceptWebSocket(connection_id, request_info,
 | |
|                              MISSING_TRAFFIC_ANNOTATION);
 | |
|     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;
 | |
|   }
 | |
| 
 | |
|   server_->Send200(connection_id, *data, content_type,
 | |
|                    MISSING_TRAFFIC_ANNOTATION);
 | |
|   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;
 | |
| 
 | |
|   server_->SendRaw(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
 | |
| }
 | |
| 
 | |
| 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;
 | |
|   }
 | |
| 
 | |
|   server_->SendOverWebSocket(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
 | |
| }
 | |
| 
 | |
| 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, 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_pump_type = base::MessagePumpType::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.
 | |
|     CEF_POST_BACKGROUND_TASK(BindOnce(
 | |
|         [](std::unique_ptr<base::Thread> thread) {
 | |
|           // Calling PlatformThread::Join() on the UI thread is otherwise
 | |
|           // disallowed.
 | |
|           base::ScopedAllowBaseSyncPrimitivesForTesting
 | |
|               scoped_allow_sync_primitives;
 | |
|           thread.reset();
 | |
|         },
 | |
|         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();
 | |
| }
 |