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(const StartDoneCallback & callback)123 void Start(const StartDoneCallback& callback) {
124 CEF_REQUIRE_UI_THREAD();
125 if (!origin_.empty()) {
126 // The server is already running.
127 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(callback);
134
135 // Only create the handler a single time.
136 if (!handler_) {
137 handler_ = new ServerHandler();
138 }
139 }
140
Stop(const DoneCallback & callback)141 void Stop(const DoneCallback& callback) {
142 CEF_REQUIRE_UI_THREAD();
143 if (!handler_) {
144 // The server is not currently running.
145 callback.Run();
146 return;
147 }
148
149 // Only 1 stop callback supported.
150 DCHECK(stop_callback_.is_null());
151 stop_callback_ = 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 StartDoneCallbackList::const_iterator it = start_callback_list_.begin();
182 for (; it != start_callback_list_.end(); ++it) {
183 (*it).Run(origin_);
184 }
185 start_callback_list_.clear();
186 }
187
NotifyServerDestroyed()188 void NotifyServerDestroyed() {
189 CEF_REQUIRE_UI_THREAD();
190
191 origin_.clear();
192 handler_ = nullptr;
193 }
194
195 // All server-related objects have been torn down.
NotifyServerHandlerDeleted()196 void NotifyServerHandlerDeleted() {
197 CEF_REQUIRE_UI_THREAD();
198
199 DCHECK(!stop_callback_.is_null());
200 stop_callback_.Run();
201 stop_callback_.Reset();
202
203 delete this;
204 }
205
NotifyClientConnected(CefRefPtr<CefServer> server,int connection_id)206 void NotifyClientConnected(CefRefPtr<CefServer> server, int connection_id) {
207 CEF_REQUIRE_UI_THREAD();
208
209 if (observer_list_.empty())
210 return;
211
212 // Use a copy in case |observer_list_| is modified during iteration.
213 ObserverList list = observer_list_;
214
215 ObserverList::const_iterator it = list.begin();
216 for (; it != list.end(); ++it) {
217 if ((*it)->OnClientConnected(server, connection_id)) {
218 break;
219 }
220 }
221 }
222
NotifyClientDisconnected(CefRefPtr<CefServer> server,int connection_id)223 void NotifyClientDisconnected(CefRefPtr<CefServer> server,
224 int connection_id) {
225 CEF_REQUIRE_UI_THREAD();
226
227 if (observer_list_.empty())
228 return;
229
230 // Use a copy in case |observer_list_| is modified during iteration.
231 ObserverList list = observer_list_;
232
233 ObserverList::const_iterator it = list.begin();
234 for (; it != list.end(); ++it) {
235 if ((*it)->OnClientDisconnected(server, connection_id)) {
236 break;
237 }
238 }
239 }
240
NotifyHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)241 void NotifyHttpRequest(CefRefPtr<CefServer> server,
242 int connection_id,
243 const CefString& client_address,
244 CefRefPtr<CefRequest> request) {
245 CEF_REQUIRE_UI_THREAD();
246
247 // TODO(chrome-runtime): Debug why favicon requests don't always have the
248 // correct resource type.
249 const std::string& url = request->GetURL();
250 if (request->GetResourceType() == RT_FAVICON ||
251 url.find("/favicon.ico") != std::string::npos) {
252 // We don't currently handle favicon requests.
253 server->SendHttp404Response(connection_id);
254 return;
255 }
256
257 DCHECK(!observer_list_.empty()) << url;
258
259 // Use a copy in case |observer_list_| is modified during iteration.
260 ObserverList list = observer_list_;
261
262 bool handled = false;
263
264 ObserverList::const_iterator it = list.begin();
265 for (; it != list.end(); ++it) {
266 if ((*it)->OnHttpRequest(server, connection_id, client_address,
267 request)) {
268 handled = true;
269 break;
270 }
271 }
272
273 if (!handled) {
274 server->SendHttp500Response(connection_id, "Unhandled request.");
275 }
276 }
277
278 private:
279 CefRefPtr<ServerHandler> handler_;
280 std::string origin_;
281
282 typedef std::vector<StartDoneCallback> StartDoneCallbackList;
283 StartDoneCallbackList start_callback_list_;
284
285 DoneCallback stop_callback_;
286
287 typedef std::vector<Observer*> ObserverList;
288 ObserverList observer_list_;
289
290 DISALLOW_COPY_AND_ASSIGN(ServerManager);
291 };
292
GetServerManager()293 ServerManager* GetServerManager() {
294 return g_manager;
295 }
296
GetOrCreateServerManager()297 ServerManager* GetOrCreateServerManager() {
298 if (!g_manager) {
299 new ServerManager();
300 DCHECK(g_manager);
301 }
302 return g_manager;
303 }
304
305 // static
NotifyServerCreated(const std::string & server_origin)306 void ServerHandler::NotifyServerCreated(const std::string& server_origin) {
307 if (!CefCurrentlyOn(TID_UI)) {
308 CefPostTask(TID_UI,
309 base::Bind(ServerHandler::NotifyServerCreated, server_origin));
310 return;
311 }
312
313 GetServerManager()->NotifyServerCreated(server_origin);
314 }
315
316 // static
NotifyServerDestroyed()317 void ServerHandler::NotifyServerDestroyed() {
318 if (!CefCurrentlyOn(TID_UI)) {
319 CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyServerDestroyed));
320 return;
321 }
322
323 GetServerManager()->NotifyServerDestroyed();
324 }
325
326 // static
NotifyServerHandlerDeleted()327 void ServerHandler::NotifyServerHandlerDeleted() {
328 if (!CefCurrentlyOn(TID_UI)) {
329 CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyServerHandlerDeleted));
330 return;
331 }
332
333 GetServerManager()->NotifyServerHandlerDeleted();
334 }
335
336 // static
NotifyClientConnected(CefRefPtr<CefServer> server,int connection_id)337 void ServerHandler::NotifyClientConnected(CefRefPtr<CefServer> server,
338 int connection_id) {
339 if (!CefCurrentlyOn(TID_UI)) {
340 CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyClientConnected, server,
341 connection_id));
342 return;
343 }
344
345 GetServerManager()->NotifyClientConnected(server, connection_id);
346 }
347
348 // static
NotifyClientDisconnected(CefRefPtr<CefServer> server,int connection_id)349 void ServerHandler::NotifyClientDisconnected(CefRefPtr<CefServer> server,
350 int connection_id) {
351 if (!CefCurrentlyOn(TID_UI)) {
352 CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyClientDisconnected,
353 server, connection_id));
354 return;
355 }
356
357 GetServerManager()->NotifyClientDisconnected(server, connection_id);
358 }
359
360 // static
NotifyHttpRequest(CefRefPtr<CefServer> server,int connection_id,const CefString & client_address,CefRefPtr<CefRequest> request)361 void ServerHandler::NotifyHttpRequest(CefRefPtr<CefServer> server,
362 int connection_id,
363 const CefString& client_address,
364 CefRefPtr<CefRequest> request) {
365 if (!CefCurrentlyOn(TID_UI)) {
366 CefPostTask(TID_UI, base::Bind(ServerHandler::NotifyHttpRequest, server,
367 connection_id, client_address, request));
368 return;
369 }
370
371 GetServerManager()->NotifyHttpRequest(server, connection_id, client_address,
372 request);
373 }
374
375 // May be created on any thread but will be destroyed on the UI thread.
376 class ObserverRegistration : public CefRegistration {
377 public:
ObserverRegistration(Observer * const observer)378 explicit ObserverRegistration(Observer* const observer)
379 : observer_(observer) {
380 DCHECK(observer_);
381 }
382
~ObserverRegistration()383 ~ObserverRegistration() override {
384 CEF_REQUIRE_UI_THREAD();
385
386 ServerManager* manager = GetServerManager();
387 if (manager) {
388 manager->RemoveObserver(observer_);
389 observer_->OnUnregistered();
390 }
391 }
392
Initialize()393 void Initialize() {
394 CEF_REQUIRE_UI_THREAD();
395 GetOrCreateServerManager()->AddObserver(observer_);
396 observer_->OnRegistered();
397 }
398
399 private:
400 Observer* const observer_;
401
402 IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(ObserverRegistration);
403 DISALLOW_COPY_AND_ASSIGN(ObserverRegistration);
404 };
405
InitializeRegistration(CefRefPtr<ObserverRegistration> registration,const DoneCallback & callback)406 void InitializeRegistration(CefRefPtr<ObserverRegistration> registration,
407 const DoneCallback& callback) {
408 if (!CefCurrentlyOn(TID_UI)) {
409 CefPostTask(TID_UI,
410 base::Bind(InitializeRegistration, registration, callback));
411 return;
412 }
413
414 DCHECK(!g_stopping);
415
416 registration->Initialize();
417 if (!callback.is_null())
418 callback.Run();
419 }
420
421 } // namespace
422
Start(const StartDoneCallback & callback)423 void Start(const StartDoneCallback& callback) {
424 DCHECK(!callback.is_null());
425 if (!CefCurrentlyOn(TID_UI)) {
426 CefPostTask(TID_UI, base::Bind(Start, callback));
427 return;
428 }
429
430 DCHECK(!g_stopping);
431
432 GetOrCreateServerManager()->Start(callback);
433 }
434
Stop(const DoneCallback & callback)435 void Stop(const DoneCallback& callback) {
436 DCHECK(!callback.is_null());
437 if (!CefCurrentlyOn(TID_UI)) {
438 CefPostTask(TID_UI, base::Bind(Stop, callback));
439 return;
440 }
441
442 // Stop will be called one time on test framework shutdown.
443 DCHECK(!g_stopping);
444 g_stopping = true;
445
446 ServerManager* manager = GetServerManager();
447 if (manager) {
448 manager->Stop(callback);
449 } else {
450 callback.Run();
451 }
452 }
453
AddObserver(Observer * observer,const DoneCallback & callback)454 CefRefPtr<CefRegistration> AddObserver(Observer* observer,
455 const DoneCallback& callback) {
456 DCHECK(observer);
457 CefRefPtr<ObserverRegistration> registration =
458 new ObserverRegistration(observer);
459 InitializeRegistration(registration, callback);
460 return registration.get();
461 }
462
AddObserverAndStart(Observer * observer,const StartDoneCallback & callback)463 CefRefPtr<CefRegistration> AddObserverAndStart(
464 Observer* observer,
465 const StartDoneCallback& callback) {
466 return AddObserver(observer, base::Bind(Start, callback));
467 }
468
SendResponse(CefRefPtr<CefServer> server,int connection_id,CefRefPtr<CefResponse> response,const std::string & response_data)469 void SendResponse(CefRefPtr<CefServer> server,
470 int connection_id,
471 CefRefPtr<CefResponse> response,
472 const std::string& response_data) {
473 const int response_code = response->GetStatus();
474 const CefString& content_type = response->GetMimeType();
475 int64 content_length = static_cast<int64>(response_data.size());
476
477 CefResponse::HeaderMap extra_headers;
478 response->GetHeaderMap(extra_headers);
479
480 server->SendHttpResponse(connection_id, response_code, content_type,
481 content_length, extra_headers);
482
483 if (content_length != 0) {
484 server->SendRawData(connection_id, response_data.data(),
485 response_data.size());
486 server->CloseConnection(connection_id);
487 }
488 }
489
490 // ObserverHelper
491
ObserverHelper()492 ObserverHelper::ObserverHelper() : weak_ptr_factory_(this) {
493 CEF_REQUIRE_UI_THREAD();
494 }
495
~ObserverHelper()496 ObserverHelper::~ObserverHelper() {
497 DCHECK(state_ == State::NONE);
498 }
499
Initialize()500 void ObserverHelper::Initialize() {
501 CEF_REQUIRE_UI_THREAD();
502 DCHECK(state_ == State::NONE);
503 state_ = State::INITIALIZING;
504 registration_ = AddObserverAndStart(
505 this,
506 base::Bind(&ObserverHelper::OnStartDone, weak_ptr_factory_.GetWeakPtr()));
507 }
508
Shutdown()509 void ObserverHelper::Shutdown() {
510 CEF_REQUIRE_UI_THREAD();
511 DCHECK(state_ == State::INITIALIZED);
512 state_ = State::SHUTTINGDOWN;
513 registration_ = nullptr;
514 }
515
OnStartDone(const std::string & server_origin)516 void ObserverHelper::OnStartDone(const std::string& server_origin) {
517 DCHECK(state_ == State::INITIALIZING);
518 state_ = State::INITIALIZED;
519 OnInitialized(server_origin);
520 }
521
OnRegistered()522 void ObserverHelper::OnRegistered() {
523 DCHECK(state_ == State::INITIALIZING);
524 }
525
OnUnregistered()526 void ObserverHelper::OnUnregistered() {
527 DCHECK(state_ == State::SHUTTINGDOWN);
528 state_ = State::NONE;
529 OnShutdown();
530 }
531
532 } // namespace test_server
533