• 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(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