• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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