mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Add "cef/" prefix for CEF #includes in libcef/ directory. Sort #includes by following https://google.github.io/styleguide/cppguide.html#Names_and_Order_of_Includes
		
			
				
	
	
		
			293 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2022 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 "cef/libcef/browser/test/test_server_impl.h"
 | |
| 
 | |
| #include "base/logging.h"
 | |
| #include "base/strings/string_util.h"
 | |
| #include "base/task/single_thread_task_runner.h"
 | |
| #include "base/threading/thread_checker.h"
 | |
| #include "cef/libcef/common/net/http_header_utils.h"
 | |
| #include "net/http/http_request_headers.h"
 | |
| #include "net/test/embedded_test_server/embedded_test_server.h"
 | |
| #include "net/test/embedded_test_server/http_response.h"
 | |
| 
 | |
| using namespace net::test_server;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| class CefTestServerConnectionImpl : public CefTestServerConnection {
 | |
|  public:
 | |
|   explicit CefTestServerConnectionImpl(
 | |
|       base::WeakPtr<HttpResponseDelegate> delegate)
 | |
|       : delegate_(delegate),
 | |
|         task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
 | |
|     DCHECK(delegate_);
 | |
|     DCHECK(task_runner_);
 | |
|   }
 | |
| 
 | |
|   void SendHttp200Response(const CefString& content_type,
 | |
|                            const void* data,
 | |
|                            size_t data_size) override {
 | |
|     auto response = std::make_unique<BasicHttpResponse>();
 | |
|     response->set_code(net::HTTP_OK);
 | |
|     response->set_content_type(std::string_view(content_type.ToString()));
 | |
|     response->set_content(
 | |
|         std::string_view(reinterpret_cast<const char*>(data), data_size));
 | |
|     SendBasicHttpResponse(std::move(response));
 | |
|   }
 | |
| 
 | |
|   void SendHttp404Response() override {
 | |
|     auto response = std::make_unique<BasicHttpResponse>();
 | |
|     response->set_code(net::HTTP_NOT_FOUND);
 | |
|     SendBasicHttpResponse(std::move(response));
 | |
|   }
 | |
| 
 | |
|   void SendHttp500Response(const CefString& error_message) override {
 | |
|     auto response = std::make_unique<BasicHttpResponse>();
 | |
|     response->set_code(net::HTTP_INTERNAL_SERVER_ERROR);
 | |
|     response->set_content_type(std::string_view("text/html"));
 | |
|     response->set_content(std::string_view(error_message.ToString()));
 | |
|     SendBasicHttpResponse(std::move(response));
 | |
|   }
 | |
| 
 | |
|   void SendHttpResponse(int response_code,
 | |
|                         const CefString& content_type,
 | |
|                         const void* data,
 | |
|                         size_t data_size,
 | |
|                         const HeaderMap& extra_headers) override {
 | |
|     auto response = std::make_unique<BasicHttpResponse>();
 | |
|     response->set_code(static_cast<net::HttpStatusCode>(response_code));
 | |
|     response->set_content_type(std::string_view(content_type.ToString()));
 | |
|     response->set_content(
 | |
|         std::string_view(reinterpret_cast<const char*>(data), data_size));
 | |
|     for (const auto& [key, value] : extra_headers) {
 | |
|       response->AddCustomHeader(key.ToString(), value.ToString());
 | |
|     }
 | |
|     SendBasicHttpResponse(std::move(response));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void SendBasicHttpResponse(std::unique_ptr<BasicHttpResponse> response) {
 | |
|     if (!task_runner_->BelongsToCurrentThread()) {
 | |
|       task_runner_->PostTask(
 | |
|           FROM_HERE,
 | |
|           base::BindOnce(&CefTestServerConnectionImpl::SendBasicHttpResponse,
 | |
|                          this, std::move(response)));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (delegate_) {
 | |
|       response->SendResponse(delegate_);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   base::WeakPtr<HttpResponseDelegate> delegate_;
 | |
|   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 | |
| 
 | |
|   IMPLEMENT_REFCOUNTING(CefTestServerConnectionImpl);
 | |
| };
 | |
| 
 | |
| class CefHttpResponse : public HttpResponse {
 | |
|  public:
 | |
|   CefHttpResponse(CefRefPtr<CefTestServer> server,
 | |
|                   CefRefPtr<CefTestServerHandler> handler,
 | |
|                   CefRefPtr<CefRequest> request)
 | |
|       : server_(server), handler_(handler), request_(request) {
 | |
|     DCHECK(server_);
 | |
|     DCHECK(handler_);
 | |
|     DCHECK(request_);
 | |
|   }
 | |
| 
 | |
|   CefHttpResponse(const CefHttpResponse&) = delete;
 | |
|   CefHttpResponse& operator=(const CefHttpResponse&) = delete;
 | |
| 
 | |
|   void SendResponse(base::WeakPtr<HttpResponseDelegate> delegate) override {
 | |
|     CefRefPtr<CefTestServerConnectionImpl> connection(
 | |
|         new CefTestServerConnectionImpl(delegate));
 | |
|     const bool handled =
 | |
|         handler_->OnTestServerRequest(server_, request_, connection.get());
 | |
|     if (handled) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     LOG(WARNING) << "Request not handled. Returning 404: "
 | |
|                  << request_->GetURL().ToString();
 | |
|     connection->SendHttp404Response();
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   CefRefPtr<CefTestServer> server_;
 | |
|   CefRefPtr<CefTestServerHandler> handler_;
 | |
|   CefRefPtr<CefRequest> request_;
 | |
| };
 | |
| 
 | |
| CefRefPtr<CefRequest> CreateCefRequest(const HttpRequest& request) {
 | |
|   CefRefPtr<CefPostData> post_data;
 | |
|   if (!request.content.empty()) {
 | |
|     post_data = CefPostData::Create();
 | |
|     auto element = CefPostDataElement::Create();
 | |
|     element->SetToBytes(request.content.size(), request.content.c_str());
 | |
|     post_data->AddElement(element);
 | |
|   }
 | |
| 
 | |
|   CefRequest::HeaderMap header_map;
 | |
|   CefString referer;
 | |
| 
 | |
|   HttpHeaderUtils::ParseHeaders(request.all_headers, header_map);
 | |
| 
 | |
|   // CefRequest will strip the Referer header from the map, so we don't need to
 | |
|   // do that here.
 | |
|   for (const auto& [key, value] : header_map) {
 | |
|     if (base::EqualsCaseInsensitiveASCII(key.ToString(),
 | |
|                                          net::HttpRequestHeaders::kReferer)) {
 | |
|       referer = value;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   auto cef_request = CefRequest::Create();
 | |
|   cef_request->Set(request.GetURL().spec(), request.method_string, post_data,
 | |
|                    header_map);
 | |
|   if (!referer.empty()) {
 | |
|     cef_request->SetReferrer(referer, REFERRER_POLICY_DEFAULT);
 | |
|   }
 | |
|   return cef_request;
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| class CefTestServerImpl::Context {
 | |
|  public:
 | |
|   Context(CefRefPtr<CefTestServer> server,
 | |
|           CefRefPtr<CefTestServerHandler> handler)
 | |
|       : server_(server), handler_(handler) {
 | |
|     DCHECK(server_);
 | |
|     DCHECK(handler_);
 | |
|   }
 | |
| 
 | |
|   Context(const Context&) = delete;
 | |
|   Context& operator=(const Context&) = delete;
 | |
| 
 | |
|   ~Context() {
 | |
|     // The server should not be running.
 | |
|     DCHECK(!test_server_);
 | |
|   }
 | |
| 
 | |
|   bool Start(uint16_t port,
 | |
|              bool https_server,
 | |
|              cef_test_cert_type_t https_cert_type) {
 | |
|     DCHECK(thread_checker_.CalledOnValidThread());
 | |
| 
 | |
|     DCHECK(!test_server_);
 | |
|     test_server_ = std::make_unique<EmbeddedTestServer>(
 | |
|         https_server ? EmbeddedTestServer::TYPE_HTTPS
 | |
|                      : EmbeddedTestServer::TYPE_HTTP);
 | |
| 
 | |
|     // Unretained is safe because Stop is called before |this| is destroyed.
 | |
|     test_server_->RegisterRequestHandler(
 | |
|         base::BindRepeating(&Context::HandleRequest, base::Unretained(this)));
 | |
| 
 | |
|     if (https_server) {
 | |
|       switch (https_cert_type) {
 | |
|         case CEF_TEST_CERT_OK_IP:
 | |
|           // Default value.
 | |
|           break;
 | |
|         case CEF_TEST_CERT_OK_DOMAIN:
 | |
|           test_server_->SetSSLConfig(
 | |
|               EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
 | |
|           break;
 | |
|         case CEF_TEST_CERT_EXPIRED:
 | |
|           test_server_->SetSSLConfig(EmbeddedTestServer::CERT_EXPIRED);
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     test_server_handle_ =
 | |
|         test_server_->StartAndReturnHandle(static_cast<int>(port));
 | |
|     if (!test_server_handle_) {
 | |
|       test_server_.reset();
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     origin_ = test_server_->base_url();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void Stop() {
 | |
|     // Should be called on the creation thread.
 | |
|     DCHECK(thread_checker_.CalledOnValidThread());
 | |
| 
 | |
|     DCHECK(test_server_);
 | |
| 
 | |
|     // Destruction of |test_server_handle_| will stop the server and block until
 | |
|     // the dedicated server thread has shut down.
 | |
|     test_server_handle_ = EmbeddedTestServerHandle();
 | |
|     test_server_.reset();
 | |
| 
 | |
|     server_ = nullptr;
 | |
|     handler_ = nullptr;
 | |
|   }
 | |
| 
 | |
|   const GURL& origin() const { return origin_; }
 | |
| 
 | |
|  private:
 | |
|   std::unique_ptr<HttpResponse> HandleRequest(const HttpRequest& request) {
 | |
|     // Should be on the dedicated server thread.
 | |
|     DCHECK(!thread_checker_.CalledOnValidThread());
 | |
|     return std::make_unique<CefHttpResponse>(server_, handler_,
 | |
|                                              CreateCefRequest(request));
 | |
|   }
 | |
| 
 | |
|   // Safe to access on any thread.
 | |
|   CefRefPtr<CefTestServer> server_;
 | |
|   CefRefPtr<CefTestServerHandler> handler_;
 | |
|   GURL origin_;
 | |
| 
 | |
|   base::ThreadChecker thread_checker_;
 | |
| 
 | |
|   // Only accessed on the creation thread.
 | |
|   std::unique_ptr<EmbeddedTestServer> test_server_;
 | |
|   EmbeddedTestServerHandle test_server_handle_;
 | |
| };
 | |
| 
 | |
| bool CefTestServerImpl::Start(uint16_t port,
 | |
|                               bool https_server,
 | |
|                               cef_test_cert_type_t https_cert_type,
 | |
|                               CefRefPtr<CefTestServerHandler> handler) {
 | |
|   DCHECK(!context_);
 | |
|   context_ = std::make_unique<CefTestServerImpl::Context>(this, handler);
 | |
|   if (context_->Start(port, https_server, https_cert_type)) {
 | |
|     const auto& origin = context_->origin().spec();
 | |
|     // Remove the trailing '/'
 | |
|     origin_ = origin.substr(0, origin.length() - 1);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   context_.reset();
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void CefTestServerImpl::Stop() {
 | |
|   DCHECK(context_);
 | |
|   context_->Stop();
 | |
|   context_.reset();
 | |
| }
 | |
| 
 | |
| CefString CefTestServerImpl::GetOrigin() {
 | |
|   return origin_;
 | |
| }
 | |
| 
 | |
| // static
 | |
| CefRefPtr<CefTestServer> CefTestServer::CreateAndStart(
 | |
|     uint16_t port,
 | |
|     bool https_server,
 | |
|     cef_test_cert_type_t https_cert_type,
 | |
|     CefRefPtr<CefTestServerHandler> handler) {
 | |
|   CefRefPtr<CefTestServerImpl> server(new CefTestServerImpl());
 | |
|   if (server->Start(port, https_server, https_cert_type, handler)) {
 | |
|     return server;
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 |