1 // Copyright (c) 2020 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "tests/ceftests/test_server.h"
6
7 #include <vector>
8
9 #include "include/wrapper/cef_closure_task.h"
10 #include "include/wrapper/cef_helpers.h"
11
12 namespace test_server {
13
14 // Must use a different port than server_unittest.cc.
15 const char kServerAddress[] = "127.0.0.1";
16 const uint16 kServerPort = 8098;
17 const char kServerScheme[] = "http";
18 const char kServerOrigin[] = "http://127.0.0.1:8098";
19
20 namespace {
21
22 class ServerManager;
23 ServerManager* g_manager = nullptr;
24
25 // True if Stop() has been called.
26 bool g_stopping = false;
27
28 // Created on the UI thread and called on the dedicated server thread.
29 class ServerHandler : public CefServerHandler {
30 public:
ServerHandler()31 ServerHandler() {
32 CefServer::CreateServer(kServerAddress, kServerPort, 10, this);
33 }
34
~ServerHandler()35 ~ServerHandler() override {
36 DCHECK(!server_);
37 NotifyServerHandlerDeleted();
38 }
39
Shutdown()40 void Shutdown() { server_->Shutdown(); }
41
42 protected:
43 // CefServerHandler methods:
OnServerCreated(CefRefPtr<CefServer> server)44 void OnServerCreated(CefRefPtr<CefServer> server) override {
45 server_ = server;
46 NotifyServerCreated(kServerOrigin);
47 }
48
OnServerDestroyed(CefRefPtr<CefServer> server)49 void OnServerDestroyed(CefRefPtr<CefServer> server) override {
50 server_ = nullptr;
51 NotifyServerDestroyed();
52 }
53
OnClientConnected(CefRefPtr<CefServer> server,int connection_id)54 void OnClientConnected(CefRefPtr<CefServer> server,
55 int connection_id) override {
56 DCHECK(server->HasConnection());
57 DCHECK(server->IsValidConnection(connection_id));
58 NotifyClientConnected(server, connection_id);
59 }
60
OnClientDisconnected(CefRefPtr<CefServer> server,int connection_id)61 void OnClientDisconnected(CefRefPtr<CefServer> server,
62 int connection_id) override {
63 DCHECK(!server->IsValidConnection(connection_id));
64 NotifyClientDisconnected(server, connection_id);
65 }
66
OnHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)67 void OnHttpRequest(CefRefPtr<CefServer> server,
68 int connection_id,
69 const CefString& client_address,
70 CefRefPtr<CefRequest> request) override {
71 NotifyHttpRequest(server, connection_id, client_address, request);
72 }
73
OnWebSocketRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request,CefRefPtr<CefCallback> callback)74 void OnWebSocketRequest(CefRefPtr<CefServer> server,
75 int connection_id,
76 const CefString& client_address,
77 CefRefPtr<CefRequest> request,
78 CefRefPtr<CefCallback> callback) override {}
OnWebSocketConnected(CefRefPtr<CefServer> server,int connection_id)79 void OnWebSocketConnected(CefRefPtr<CefServer> server,
80 int connection_id) override {}
OnWebSocketMessage(CefRefPtr<CefServer> server,int connection_id,const void * data,size_t data_size)81 void OnWebSocketMessage(CefRefPtr<CefServer> server,
82 int connection_id,
83 const void* data,
84 size_t data_size) override {}
85
86 private:
87 static void NotifyServerCreated(const std::string& server_origin);
88 static void NotifyServerDestroyed();
89 static void NotifyServerHandlerDeleted();
90 static void NotifyClientConnected(CefRefPtr<CefServer> server,
91 int connection_id);
92 static void NotifyClientDisconnected(CefRefPtr<CefServer> server,
93 int connection_id);
94 static void NotifyHttpRequest(CefRefPtr<CefServer> server,
95 int connection_id,
96 const CefString& client_address,
97 CefRefPtr<CefRequest> request);
98
99 CefRefPtr<CefServer> server_;
100
101 IMPLEMENT_REFCOUNTING(ServerHandler);
102 DISALLOW_COPY_AND_ASSIGN(ServerHandler);
103 };
104
105 // Only accessed on the UI thread. Deletes itself after the server is stopped.
106 class ServerManager {
107 public:
ServerManager()108 ServerManager() {
109 CEF_REQUIRE_UI_THREAD();
110 DCHECK(!g_manager);
111 g_manager = this;
112 }
113
~ServerManager()114 ~ServerManager() {
115 CEF_REQUIRE_UI_THREAD();
116 DCHECK(observer_list_.empty());
117 DCHECK(start_callback_list_.empty());
118 DCHECK(stop_callback_.is_null());
119
120 g_manager = nullptr;
121 }
122
Start(StartDoneCallback callback)123 void Start(StartDoneCallback callback) {
124 CEF_REQUIRE_UI_THREAD();
125 if (!origin_.empty()) {
126 // The server is already running.
127 std::move(callback).Run(origin_);
128 return;
129 }
130
131 // If tests run in parallel, and the server is starting, then there may be
132 // multiple pending callbacks.
133 start_callback_list_.push_back(std::move(callback));
134
135 // Only create the handler a single time.
136 if (!handler_) {
137 handler_ = new ServerHandler();
138 }
139 }
140
Stop(DoneCallback callback)141 void Stop(DoneCallback callback) {
142 CEF_REQUIRE_UI_THREAD();
143 if (!handler_) {
144 // The server is not currently running.
145 std::move(callback).Run();
146 return;
147 }
148
149 // Only 1 stop callback supported.
150 DCHECK(stop_callback_.is_null());
151 stop_callback_ = std::move(callback);
152
153 handler_->Shutdown();
154 }
155
AddObserver(Observer * observer)156 void AddObserver(Observer* observer) {
157 CEF_REQUIRE_UI_THREAD();
158 observer_list_.push_back(observer);
159 }
160
RemoveObserver(Observer * observer)161 void RemoveObserver(Observer* observer) {
162 CEF_REQUIRE_UI_THREAD();
163 bool found = false;
164 ObserverList::iterator it = observer_list_.begin();
165 for (; it != observer_list_.end(); ++it) {
166 if (*it == observer) {
167 observer_list_.erase(it);
168 found = true;
169 break;
170 }
171 }
172 DCHECK(found);
173 }
174
NotifyServerCreated(const std::string & server_origin)175 void NotifyServerCreated(const std::string& server_origin) {
176 CEF_REQUIRE_UI_THREAD();
177
178 DCHECK(origin_.empty());
179 origin_ = server_origin;
180
181 for (auto& callback : start_callback_list_) {
182 std::move(callback).Run(origin_);
183 }
184 start_callback_list_.clear();
185 }
186
NotifyServerDestroyed()187 void NotifyServerDestroyed() {
188 CEF_REQUIRE_UI_THREAD();
189
190 origin_.clear();
191 handler_ = nullptr;
192 }
193
194 // All server-related objects have been torn down.
NotifyServerHandlerDeleted()195 void NotifyServerHandlerDeleted() {
196 CEF_REQUIRE_UI_THREAD();
197
198 DCHECK(!stop_callback_.is_null());
199 std::move(stop_callback_).Run();
200
201 delete this;
202 }
203
NotifyClientConnected(CefRefPtr<CefServer> server,int connection_id)204 void NotifyClientConnected(CefRefPtr<CefServer> server, int connection_id) {
205 CEF_REQUIRE_UI_THREAD();
206
207 if (observer_list_.empty())
208 return;
209
210 // Use a copy in case |observer_list_| is modified during iteration.
211 ObserverList list = observer_list_;
212
213 ObserverList::const_iterator it = list.begin();
214 for (; it != list.end(); ++it) {
215 if ((*it)->OnClientConnected(server, connection_id)) {
216 break;
217 }
218 }
219 }
220
NotifyClientDisconnected(CefRefPtr<CefServer> server,int connection_id)221 void NotifyClientDisconnected(CefRefPtr<CefServer> server,
222 int connection_id) {
223 CEF_REQUIRE_UI_THREAD();
224
225 if (observer_list_.empty())
226 return;
227
228 // Use a copy in case |observer_list_| is modified during iteration.
229 ObserverList list = observer_list_;
230
231 ObserverList::const_iterator it = list.begin();
232 for (; it != list.end(); ++it) {
233 if ((*it)->OnClientDisconnected(server, connection_id)) {
234 break;
235 }
236 }
237 }
238
NotifyHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)239 void NotifyHttpRequest(CefRefPtr<CefServer> server,
240 int connection_id,
241 const CefString& client_address,
242 CefRefPtr<CefRequest> request) {
243 CEF_REQUIRE_UI_THREAD();
244
245 // TODO(chrome-runtime): Debug why favicon requests don't always have the
246 // correct resource type.
247 const std::string& url = request->GetURL();
248 if (request->GetResourceType() == RT_FAVICON ||
249 url.find("/favicon.ico") != std::string::npos) {
250 // We don't currently handle favicon requests.
251 server->SendHttp404Response(connection_id);
252 return;
253 }
254
255 DCHECK(!observer_list_.empty()) << url;
256
257 // Use a copy in case |observer_list_| is modified during iteration.
258 ObserverList list = observer_list_;
259
260 bool handled = false;
261
262 ObserverList::const_iterator it = list.begin();
263 for (; it != list.end(); ++it) {
264 if ((*it)->OnHttpRequest(server, connection_id, client_address,
265 request)) {
266 handled = true;
267 break;
268 }
269 }
270
271 if (!handled) {
272 server->SendHttp500Response(connection_id, "Unhandled request.");
273 }
274 }
275
276 private:
277 CefRefPtr<ServerHandler> handler_;
278 std::string origin_;
279
280 using StartDoneCallbackList = std::vector<StartDoneCallback>;
281 StartDoneCallbackList start_callback_list_;
282
283 DoneCallback stop_callback_;
284
285 using ObserverList = std::vector<Observer*>;
286 ObserverList observer_list_;
287
288 DISALLOW_COPY_AND_ASSIGN(ServerManager);
289 };
290
GetServerManager()291 ServerManager* GetServerManager() {
292 return g_manager;
293 }
294
GetOrCreateServerManager()295 ServerManager* GetOrCreateServerManager() {
296 if (!g_manager) {
297 new ServerManager();
298 DCHECK(g_manager);
299 }
300 return g_manager;
301 }
302
303 // static
NotifyServerCreated(const std::string & server_origin)304 void ServerHandler::NotifyServerCreated(const std::string& server_origin) {
305 if (!CefCurrentlyOn(TID_UI)) {
306 CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyServerCreated,
307 server_origin));
308 return;
309 }
310
311 GetServerManager()->NotifyServerCreated(server_origin);
312 }
313
314 // static
NotifyServerDestroyed()315 void ServerHandler::NotifyServerDestroyed() {
316 if (!CefCurrentlyOn(TID_UI)) {
317 CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyServerDestroyed));
318 return;
319 }
320
321 GetServerManager()->NotifyServerDestroyed();
322 }
323
324 // static
NotifyServerHandlerDeleted()325 void ServerHandler::NotifyServerHandlerDeleted() {
326 if (!CefCurrentlyOn(TID_UI)) {
327 CefPostTask(TID_UI,
328 base::BindOnce(ServerHandler::NotifyServerHandlerDeleted));
329 return;
330 }
331
332 GetServerManager()->NotifyServerHandlerDeleted();
333 }
334
335 // static
NotifyClientConnected(CefRefPtr<CefServer> server,int connection_id)336 void ServerHandler::NotifyClientConnected(CefRefPtr<CefServer> server,
337 int connection_id) {
338 if (!CefCurrentlyOn(TID_UI)) {
339 CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyClientConnected,
340 server, connection_id));
341 return;
342 }
343
344 GetServerManager()->NotifyClientConnected(server, connection_id);
345 }
346
347 // static
NotifyClientDisconnected(CefRefPtr<CefServer> server,int connection_id)348 void ServerHandler::NotifyClientDisconnected(CefRefPtr<CefServer> server,
349 int connection_id) {
350 if (!CefCurrentlyOn(TID_UI)) {
351 CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyClientDisconnected,
352 server, connection_id));
353 return;
354 }
355
356 GetServerManager()->NotifyClientDisconnected(server, connection_id);
357 }
358
359 // static
NotifyHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)360 void ServerHandler::NotifyHttpRequest(CefRefPtr<CefServer> server,
361 int connection_id,
362 const CefString& client_address,
363 CefRefPtr<CefRequest> request) {
364 if (!CefCurrentlyOn(TID_UI)) {
365 CefPostTask(TID_UI, base::BindOnce(ServerHandler::NotifyHttpRequest, server,
366 connection_id, client_address, request));
367 return;
368 }
369
370 GetServerManager()->NotifyHttpRequest(server, connection_id, client_address,
371 request);
372 }
373
374 // May be created on any thread but will be destroyed on the UI thread.
375 class ObserverRegistration : public CefRegistration {
376 public:
ObserverRegistration(Observer * const observer)377 explicit ObserverRegistration(Observer* const observer)
378 : observer_(observer) {
379 DCHECK(observer_);
380 }
381
~ObserverRegistration()382 ~ObserverRegistration() override {
383 CEF_REQUIRE_UI_THREAD();
384
385 ServerManager* manager = GetServerManager();
386 if (manager) {
387 manager->RemoveObserver(observer_);
388 observer_->OnUnregistered();
389 }
390 }
391
Initialize()392 void Initialize() {
393 CEF_REQUIRE_UI_THREAD();
394 GetOrCreateServerManager()->AddObserver(observer_);
395 observer_->OnRegistered();
396 }
397
398 private:
399 Observer* const observer_;
400
401 IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ObserverRegistration);
402 DISALLOW_COPY_AND_ASSIGN(ObserverRegistration);
403 };
404
InitializeRegistration(CefRefPtr<ObserverRegistration> registration,DoneCallback callback)405 void InitializeRegistration(CefRefPtr<ObserverRegistration> registration,
406 DoneCallback callback) {
407 if (!CefCurrentlyOn(TID_UI)) {
408 CefPostTask(TID_UI, base::BindOnce(InitializeRegistration, registration,
409 std::move(callback)));
410 return;
411 }
412
413 DCHECK(!g_stopping);
414
415 registration->Initialize();
416 if (!callback.is_null())
417 std::move(callback).Run();
418 }
419
420 } // namespace
421
Start(StartDoneCallback callback)422 void Start(StartDoneCallback callback) {
423 DCHECK(!callback.is_null());
424 if (!CefCurrentlyOn(TID_UI)) {
425 CefPostTask(TID_UI, base::BindOnce(Start, std::move(callback)));
426 return;
427 }
428
429 DCHECK(!g_stopping);
430
431 GetOrCreateServerManager()->Start(std::move(callback));
432 }
433
Stop(DoneCallback callback)434 void Stop(DoneCallback callback) {
435 DCHECK(!callback.is_null());
436 if (!CefCurrentlyOn(TID_UI)) {
437 CefPostTask(TID_UI, base::BindOnce(Stop, std::move(callback)));
438 return;
439 }
440
441 // Stop will be called one time on test framework shutdown.
442 DCHECK(!g_stopping);
443 g_stopping = true;
444
445 ServerManager* manager = GetServerManager();
446 if (manager) {
447 manager->Stop(std::move(callback));
448 } else {
449 std::move(callback).Run();
450 }
451 }
452
AddObserver(Observer * observer,DoneCallback callback)453 CefRefPtr<CefRegistration> AddObserver(Observer* observer,
454 DoneCallback callback) {
455 DCHECK(observer);
456 CefRefPtr<ObserverRegistration> registration =
457 new ObserverRegistration(observer);
458 InitializeRegistration(registration, std::move(callback));
459 return registration.get();
460 }
461
AddObserverAndStart(Observer * observer,StartDoneCallback callback)462 CefRefPtr<CefRegistration> AddObserverAndStart(Observer* observer,
463 StartDoneCallback callback) {
464 return AddObserver(observer, base::BindOnce(Start, std::move(callback)));
465 }
466
SendResponse(CefRefPtr<CefServer> server,int connection_id,CefRefPtr<CefResponse> response,const std::string & response_data)467 void SendResponse(CefRefPtr<CefServer> server,
468 int connection_id,
469 CefRefPtr<CefResponse> response,
470 const std::string& response_data) {
471 const int response_code = response->GetStatus();
472 const CefString& content_type = response->GetMimeType();
473 int64 content_length = static_cast<int64>(response_data.size());
474
475 CefResponse::HeaderMap extra_headers;
476 response->GetHeaderMap(extra_headers);
477
478 server->SendHttpResponse(connection_id, response_code, content_type,
479 content_length, extra_headers);
480
481 if (content_length != 0) {
482 server->SendRawData(connection_id, response_data.data(),
483 response_data.size());
484 server->CloseConnection(connection_id);
485 }
486 }
487
488 // ObserverHelper
489
ObserverHelper()490 ObserverHelper::ObserverHelper() : weak_ptr_factory_(this) {
491 CEF_REQUIRE_UI_THREAD();
492 }
493
~ObserverHelper()494 ObserverHelper::~ObserverHelper() {
495 DCHECK(state_ == State::NONE);
496 }
497
Initialize()498 void ObserverHelper::Initialize() {
499 CEF_REQUIRE_UI_THREAD();
500 DCHECK(state_ == State::NONE);
501 state_ = State::INITIALIZING;
502 registration_ =
503 AddObserverAndStart(this, base::BindOnce(&ObserverHelper::OnStartDone,
504 weak_ptr_factory_.GetWeakPtr()));
505 }
506
Shutdown()507 void ObserverHelper::Shutdown() {
508 CEF_REQUIRE_UI_THREAD();
509 DCHECK(state_ == State::INITIALIZED);
510 state_ = State::SHUTTINGDOWN;
511 registration_ = nullptr;
512 }
513
OnStartDone(const std::string & server_origin)514 void ObserverHelper::OnStartDone(const std::string& server_origin) {
515 DCHECK(state_ == State::INITIALIZING);
516 state_ = State::INITIALIZED;
517 OnInitialized(server_origin);
518 }
519
OnRegistered()520 void ObserverHelper::OnRegistered() {
521 DCHECK(state_ == State::INITIALIZING);
522 }
523
OnUnregistered()524 void ObserverHelper::OnUnregistered() {
525 DCHECK(state_ == State::SHUTTINGDOWN);
526 state_ = State::NONE;
527 OnShutdown();
528 }
529
530 } // namespace test_server
531