// Copyright (c) 2020 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 "tests/ceftests/test_server_runner.h" #include "include/cef_server.h" #include "include/wrapper/cef_closure_task.h" #include "include/wrapper/cef_helpers.h" #include "tests/gtest/include/gtest/gtest.h" namespace test_server { namespace { const int kHttpServerBacklog = 10; // Created on the UI thread and called on the dedicated server thread. class ServerHandler : public CefServerHandler { public: explicit ServerHandler(Runner::Delegate* delegate) : delegate_(delegate) {} ~ServerHandler() override { CEF_REQUIRE_UI_THREAD(); EXPECT_FALSE(server_); delegate_->OnServerHandlerDeleted(); } void Shutdown() { server_->Shutdown(); } // CefServerHandler methods: void OnServerCreated(CefRefPtr server) override { EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread()); server_ = server; NotifyServerCreated("http://" + server->GetAddress().ToString()); } void OnServerDestroyed(CefRefPtr server) override { EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread()); server_ = nullptr; NotifyServerDestroyed(); } void OnClientConnected(CefRefPtr server, int connection_id) override { EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread()); EXPECT_TRUE(server->HasConnection()); EXPECT_TRUE(server->IsValidConnection(connection_id)); } void OnClientDisconnected(CefRefPtr server, int connection_id) override { EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread()); EXPECT_FALSE(server->IsValidConnection(connection_id)); } void OnHttpRequest(CefRefPtr server, int connection_id, const CefString& client_address, CefRefPtr request) override { EXPECT_TRUE(server->GetTaskRunner()->BelongsToCurrentThread()); NotifyTestServerRequest(server, connection_id, client_address, request); } void OnWebSocketRequest(CefRefPtr server, int connection_id, const CefString& client_address, CefRefPtr request, CefRefPtr callback) override {} void OnWebSocketConnected(CefRefPtr server, int connection_id) override {} void OnWebSocketMessage(CefRefPtr server, int connection_id, const void* data, size_t data_size) override {} private: void NotifyServerCreated(const std::string& server_origin) { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(&ServerHandler::NotifyServerCreated, this, server_origin)); return; } delegate_->OnServerCreated(server_origin); } void NotifyServerDestroyed() { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(&ServerHandler::NotifyServerDestroyed, this)); return; } delegate_->OnServerDestroyed(); } void NotifyTestServerRequest(CefRefPtr server, int connection_id, const CefString& client_address, CefRefPtr request) { if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce( &ServerHandler::NotifyTestServerRequest, this, server, connection_id, client_address, request)); return; } auto response_callback = base::BindRepeating(&ServerHandler::SendResponse, server, connection_id); delegate_->OnTestServerRequest(request, response_callback); } static void SendResponse(CefRefPtr server, int connection_id, CefRefPtr response, const std::string& response_data) { // Execute on the server thread because some methods require it. CefRefPtr task_runner = server->GetTaskRunner(); if (!task_runner->BelongsToCurrentThread()) { task_runner->PostTask(CefCreateClosureTask( base::BindOnce(ServerHandler::SendResponse, server, connection_id, response, response_data))); return; } if (!server->IsValidConnection(connection_id)) { // This can occur if the connected browser has already closed. return; } const int response_code = response->GetStatus(); if (response_code <= 0) { // Intentionally not responding for incomplete request tests. return; } const CefString& content_type = response->GetMimeType(); int64_t content_length = static_cast(response_data.size()); CefResponse::HeaderMap extra_headers; response->GetHeaderMap(extra_headers); server->SendHttpResponse(connection_id, response_code, content_type, content_length, extra_headers); if (response_data == kIncompleteDoNotSendData) { // Intentionally not sending data for incomplete request tests. return; } if (content_length != 0) { server->SendRawData(connection_id, response_data.data(), response_data.size()); server->CloseConnection(connection_id); } // The connection should be closed. EXPECT_FALSE(server->IsValidConnection(connection_id)); } Runner::Delegate* const delegate_; CefRefPtr server_; IMPLEMENT_REFCOUNTING(ServerHandler); DISALLOW_COPY_AND_ASSIGN(ServerHandler); }; class ServerRunner : public Runner { public: explicit ServerRunner(Delegate* delegate) : Runner(delegate) {} void StartServer() override { CEF_REQUIRE_UI_THREAD(); DCHECK(!handler_); handler_ = new ServerHandler(delegate_); CefServer::CreateServer(kHttpServerAddress, kHttpServerPort, kHttpServerBacklog, handler_); } void ShutdownServer() override { CEF_REQUIRE_UI_THREAD(); DCHECK(handler_); handler_->Shutdown(); handler_ = nullptr; } private: CefRefPtr handler_; }; } // namespace std::unique_ptr Runner::CreateNormal(Delegate* delegate) { return std::make_unique(delegate); } } // namespace test_server