1 // Copyright (c) 2017 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 "libcef/browser/server_impl.h"
6
7 #include "libcef/browser/thread_util.h"
8 #include "libcef/common/request_impl.h"
9 #include "libcef/common/task_runner_impl.h"
10
11 #include "base/bind.h"
12 #include "base/format_macros.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task/thread_pool.h"
16 #include "base/threading/thread.h"
17 #include "net/base/net_errors.h"
18 #include "net/http/http_request_headers.h"
19 #include "net/server/http_server_request_info.h"
20 #include "net/server/http_server_response_info.h"
21 #include "net/socket/server_socket.h"
22 #include "net/socket/tcp_server_socket.h"
23 #include "net/traffic_annotation/network_traffic_annotation.h"
24
25 #define CEF_CURRENTLY_ON_HT() CurrentlyOnHandlerThread()
26 #define CEF_REQUIRE_HT() DCHECK(CEF_CURRENTLY_ON_HT())
27
28 #define CEF_REQUIRE_HT_RETURN(var) \
29 if (!CEF_CURRENTLY_ON_HT()) { \
30 NOTREACHED() << "called on invalid thread"; \
31 return var; \
32 }
33
34 #define CEF_REQUIRE_HT_RETURN_VOID() \
35 if (!CEF_CURRENTLY_ON_HT()) { \
36 NOTREACHED() << "called on invalid thread"; \
37 return; \
38 }
39
40 #define CEF_POST_TASK_HT(task) task_runner_->PostTask(FROM_HERE, task);
41
42 namespace {
43
44 const char kReferrerLowerCase[] = "referer";
45
46 // Wrap a string in a unique_ptr to avoid extra copies.
CreateUniqueString(const void * data,size_t data_size)47 std::unique_ptr<std::string> CreateUniqueString(const void* data,
48 size_t data_size) {
49 std::unique_ptr<std::string> ptr;
50 if (data && data_size > 0) {
51 ptr.reset(new std::string(static_cast<const char*>(data), data_size));
52 } else {
53 ptr.reset(new std::string());
54 }
55 return ptr;
56 }
57
CreateRequest(const std::string & address,const net::HttpServerRequestInfo & info,bool is_websocket)58 CefRefPtr<CefRequest> CreateRequest(const std::string& address,
59 const net::HttpServerRequestInfo& info,
60 bool is_websocket) {
61 DCHECK(!address.empty());
62 DCHECK(!info.method.empty());
63 DCHECK(!info.path.empty());
64
65 CefRefPtr<CefPostData> post_data;
66 if (!info.data.empty()) {
67 post_data = CefPostData::Create();
68 CefRefPtr<CefPostDataElement> post_element = CefPostDataElement::Create();
69 post_element->SetToBytes(info.data.size(), info.data.data());
70 post_data->AddElement(post_element);
71 }
72
73 std::string referer;
74
75 CefRequest::HeaderMap header_map;
76 if (!info.headers.empty()) {
77 net::HttpServerRequestInfo::HeadersMap::const_iterator it =
78 info.headers.begin();
79 for (; it != info.headers.end(); ++it) {
80 // Don't include Referer in the header map.
81 if (base::LowerCaseEqualsASCII(it->first, kReferrerLowerCase)) {
82 referer = it->second;
83 } else {
84 header_map.insert(std::make_pair(it->first, it->second));
85 }
86 }
87 }
88
89 CefRefPtr<CefRequestImpl> request = new CefRequestImpl();
90 request->Set((is_websocket ? "ws://" : "http://") + address + info.path,
91 info.method, post_data, header_map);
92 if (!referer.empty())
93 request->SetReferrer(referer, REFERRER_POLICY_DEFAULT);
94 request->SetReadOnly(true);
95 return request;
96 }
97
98 // Callback implementation for WebSocket acceptance. Always executes on the UI
99 // thread so we can avoid multiple execution by clearing the |impl_| reference.
100 class AcceptWebSocketCallback : public CefCallback {
101 public:
AcceptWebSocketCallback(CefRefPtr<CefServerImpl> impl,int connection_id,net::HttpServerRequestInfo request_info)102 AcceptWebSocketCallback(CefRefPtr<CefServerImpl> impl,
103 int connection_id,
104 net::HttpServerRequestInfo request_info)
105 : impl_(impl),
106 connection_id_(connection_id),
107 request_info_(request_info) {}
108
109 AcceptWebSocketCallback(const AcceptWebSocketCallback&) = delete;
110 AcceptWebSocketCallback& operator=(const AcceptWebSocketCallback&) = delete;
111
~AcceptWebSocketCallback()112 ~AcceptWebSocketCallback() override {
113 if (impl_)
114 impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
115 }
116
Continue()117 void Continue() override {
118 if (!CEF_CURRENTLY_ON_UIT()) {
119 CEF_POST_TASK(CEF_UIT,
120 base::BindOnce(&AcceptWebSocketCallback::Continue, this));
121 return;
122 }
123 if (!impl_)
124 return;
125 impl_->ContinueWebSocketRequest(connection_id_, request_info_, true);
126 impl_ = nullptr;
127 }
128
Cancel()129 void Cancel() override {
130 if (!CEF_CURRENTLY_ON_UIT()) {
131 CEF_POST_TASK(CEF_UIT,
132 base::BindOnce(&AcceptWebSocketCallback::Cancel, this));
133 return;
134 }
135 if (!impl_)
136 return;
137 impl_->ContinueWebSocketRequest(connection_id_, request_info_, false);
138 impl_ = nullptr;
139 }
140
141 private:
142 CefRefPtr<CefServerImpl> impl_;
143 int connection_id_;
144 net::HttpServerRequestInfo request_info_;
145
146 IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(AcceptWebSocketCallback);
147 };
148
149 } // namespace
150
151 // CefServer
152
153 // static
CreateServer(const CefString & address,uint16 port,int backlog,CefRefPtr<CefServerHandler> handler)154 void CefServer::CreateServer(const CefString& address,
155 uint16 port,
156 int backlog,
157 CefRefPtr<CefServerHandler> handler) {
158 CefRefPtr<CefServerImpl> server(new CefServerImpl(handler));
159 server->Start(address, port, backlog);
160 }
161
162 // CefServerImpl
163
164 struct CefServerImpl::ConnectionInfo {
ConnectionInfoCefServerImpl::ConnectionInfo165 ConnectionInfo() : is_websocket(false), is_websocket_pending(false) {}
166
167 // True if this connection is a WebSocket connection.
168 bool is_websocket;
169 bool is_websocket_pending;
170 };
171
CefServerImpl(CefRefPtr<CefServerHandler> handler)172 CefServerImpl::CefServerImpl(CefRefPtr<CefServerHandler> handler)
173 : handler_(handler) {
174 DCHECK(handler_);
175 }
176
Start(const std::string & address,uint16 port,int backlog)177 void CefServerImpl::Start(const std::string& address,
178 uint16 port,
179 int backlog) {
180 DCHECK(!address.empty());
181 CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefServerImpl::StartOnUIThread, this,
182 address, port, backlog));
183 }
184
GetTaskRunner()185 CefRefPtr<CefTaskRunner> CefServerImpl::GetTaskRunner() {
186 if (task_runner_)
187 return new CefTaskRunnerImpl(task_runner_);
188 return nullptr;
189 }
190
Shutdown()191 void CefServerImpl::Shutdown() {
192 CEF_POST_TASK_HT(
193 base::BindOnce(&CefServerImpl::ShutdownOnHandlerThread, this));
194 }
195
IsRunning()196 bool CefServerImpl::IsRunning() {
197 CEF_REQUIRE_HT_RETURN(false);
198 return !!server_.get();
199 }
200
GetAddress()201 CefString CefServerImpl::GetAddress() {
202 return address_;
203 }
204
HasConnection()205 bool CefServerImpl::HasConnection() {
206 CEF_REQUIRE_HT_RETURN(false);
207 return !connection_info_map_.empty();
208 }
209
IsValidConnection(int connection_id)210 bool CefServerImpl::IsValidConnection(int connection_id) {
211 CEF_REQUIRE_HT_RETURN(false);
212 return connection_info_map_.find(connection_id) != connection_info_map_.end();
213 }
214
SendHttp200Response(int connection_id,const CefString & content_type,const void * data,size_t data_size)215 void CefServerImpl::SendHttp200Response(int connection_id,
216 const CefString& content_type,
217 const void* data,
218 size_t data_size) {
219 SendHttp200ResponseInternal(connection_id, content_type,
220 CreateUniqueString(data, data_size));
221 }
222
SendHttp404Response(int connection_id)223 void CefServerImpl::SendHttp404Response(int connection_id) {
224 if (!CEF_CURRENTLY_ON_HT()) {
225 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp404Response, this,
226 connection_id));
227 return;
228 }
229
230 if (!ValidateServer())
231 return;
232
233 ConnectionInfo* info = GetConnectionInfo(connection_id);
234 if (!info)
235 return;
236
237 if (info->is_websocket) {
238 LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
239 << connection_id;
240 return;
241 }
242
243 server_->Send404(connection_id, MISSING_TRAFFIC_ANNOTATION);
244 server_->Close(connection_id);
245 }
246
SendHttp500Response(int connection_id,const CefString & error_message)247 void CefServerImpl::SendHttp500Response(int connection_id,
248 const CefString& error_message) {
249 if (!CEF_CURRENTLY_ON_HT()) {
250 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp500Response, this,
251 connection_id, error_message));
252 return;
253 }
254
255 if (!ValidateServer())
256 return;
257
258 ConnectionInfo* info = GetConnectionInfo(connection_id);
259 if (!info)
260 return;
261
262 if (info->is_websocket) {
263 LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
264 << connection_id;
265 return;
266 }
267
268 server_->Send500(connection_id, error_message, MISSING_TRAFFIC_ANNOTATION);
269 server_->Close(connection_id);
270 }
271
SendHttpResponse(int connection_id,int response_code,const CefString & content_type,int64 content_length,const HeaderMap & extra_headers)272 void CefServerImpl::SendHttpResponse(int connection_id,
273 int response_code,
274 const CefString& content_type,
275 int64 content_length,
276 const HeaderMap& extra_headers) {
277 if (!CEF_CURRENTLY_ON_HT()) {
278 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttpResponse, this,
279 connection_id, response_code, content_type,
280 content_length, extra_headers));
281 return;
282 }
283
284 if (!ValidateServer())
285 return;
286
287 ConnectionInfo* info = GetConnectionInfo(connection_id);
288 if (!info)
289 return;
290
291 if (info->is_websocket) {
292 LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
293 << connection_id;
294 return;
295 }
296
297 net::HttpServerResponseInfo response(
298 static_cast<net::HttpStatusCode>(response_code));
299
300 HeaderMap::const_iterator it = extra_headers.begin();
301 for (; it != extra_headers.end(); ++it)
302 response.AddHeader(it->first, it->second);
303
304 response.AddHeader(net::HttpRequestHeaders::kContentType, content_type);
305 if (content_length >= 0) {
306 response.AddHeader(
307 net::HttpRequestHeaders::kContentLength,
308 base::StringPrintf("%" PRIuS, static_cast<size_t>(content_length)));
309 }
310
311 server_->SendResponse(connection_id, response, MISSING_TRAFFIC_ANNOTATION);
312 if (content_length == 0) {
313 server_->Close(connection_id);
314 }
315 }
316
SendRawData(int connection_id,const void * data,size_t data_size)317 void CefServerImpl::SendRawData(int connection_id,
318 const void* data,
319 size_t data_size) {
320 if (!data || data_size == 0)
321 return;
322 SendRawDataInternal(connection_id, CreateUniqueString(data, data_size));
323 }
324
CloseConnection(int connection_id)325 void CefServerImpl::CloseConnection(int connection_id) {
326 if (!CEF_CURRENTLY_ON_HT()) {
327 CEF_POST_TASK_HT(
328 base::BindOnce(&CefServerImpl::CloseConnection, this, connection_id));
329 return;
330 }
331
332 if (ValidateServer() && GetConnectionInfo(connection_id)) {
333 server_->Close(connection_id);
334 }
335 }
336
SendWebSocketMessage(int connection_id,const void * data,size_t data_size)337 void CefServerImpl::SendWebSocketMessage(int connection_id,
338 const void* data,
339 size_t data_size) {
340 if (!data || data_size == 0)
341 return;
342 SendWebSocketMessageInternal(connection_id,
343 CreateUniqueString(data, data_size));
344 }
345
ContinueWebSocketRequest(int connection_id,const net::HttpServerRequestInfo & request_info,bool allow)346 void CefServerImpl::ContinueWebSocketRequest(
347 int connection_id,
348 const net::HttpServerRequestInfo& request_info,
349 bool allow) {
350 if (!CEF_CURRENTLY_ON_HT()) {
351 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::ContinueWebSocketRequest,
352 this, connection_id, request_info, allow));
353 return;
354 }
355
356 if (!ValidateServer())
357 return;
358
359 ConnectionInfo* info = GetConnectionInfo(connection_id);
360 DCHECK(info);
361 if (!info)
362 return;
363
364 DCHECK(info->is_websocket);
365 DCHECK(info->is_websocket_pending);
366 if (!info->is_websocket || !info->is_websocket_pending)
367 return;
368
369 info->is_websocket_pending = false;
370
371 if (allow) {
372 server_->AcceptWebSocket(connection_id, request_info,
373 MISSING_TRAFFIC_ANNOTATION);
374 handler_->OnWebSocketConnected(this, connection_id);
375 } else {
376 server_->Close(connection_id);
377 }
378 }
379
SendHttp200ResponseInternal(int connection_id,const CefString & content_type,std::unique_ptr<std::string> data)380 void CefServerImpl::SendHttp200ResponseInternal(
381 int connection_id,
382 const CefString& content_type,
383 std::unique_ptr<std::string> data) {
384 if (!CEF_CURRENTLY_ON_HT()) {
385 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendHttp200ResponseInternal,
386 this, connection_id, content_type,
387 std::move(data)));
388 return;
389 }
390
391 if (!ValidateServer())
392 return;
393
394 ConnectionInfo* info = GetConnectionInfo(connection_id);
395 if (!info)
396 return;
397
398 if (info->is_websocket) {
399 LOG(ERROR) << "Invalid attempt to send HTTP response for connection_id "
400 << connection_id;
401 return;
402 }
403
404 server_->Send200(connection_id, *data, content_type,
405 MISSING_TRAFFIC_ANNOTATION);
406 server_->Close(connection_id);
407 }
408
SendRawDataInternal(int connection_id,std::unique_ptr<std::string> data)409 void CefServerImpl::SendRawDataInternal(int connection_id,
410 std::unique_ptr<std::string> data) {
411 if (!CEF_CURRENTLY_ON_HT()) {
412 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::SendRawDataInternal, this,
413 connection_id, std::move(data)));
414 return;
415 }
416
417 if (!ValidateServer())
418 return;
419
420 if (!GetConnectionInfo(connection_id))
421 return;
422
423 server_->SendRaw(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
424 }
425
SendWebSocketMessageInternal(int connection_id,std::unique_ptr<std::string> data)426 void CefServerImpl::SendWebSocketMessageInternal(
427 int connection_id,
428 std::unique_ptr<std::string> data) {
429 if (!CEF_CURRENTLY_ON_HT()) {
430 CEF_POST_TASK_HT(
431 base::BindOnce(&CefServerImpl::SendWebSocketMessageInternal, this,
432 connection_id, std::move(data)));
433 return;
434 }
435
436 if (!ValidateServer())
437 return;
438
439 ConnectionInfo* info = GetConnectionInfo(connection_id);
440 if (!info)
441 return;
442
443 if (!info->is_websocket || info->is_websocket_pending) {
444 LOG(ERROR) << "Invalid attempt to send WebSocket message for connection_id "
445 << connection_id;
446 return;
447 }
448
449 server_->SendOverWebSocket(connection_id, *data, MISSING_TRAFFIC_ANNOTATION);
450 }
451
OnConnect(int connection_id)452 void CefServerImpl::OnConnect(int connection_id) {
453 CEF_REQUIRE_HT();
454
455 CreateConnectionInfo(connection_id);
456 handler_->OnClientConnected(this, connection_id);
457 }
458
OnHttpRequest(int connection_id,const net::HttpServerRequestInfo & request_info)459 void CefServerImpl::OnHttpRequest(
460 int connection_id,
461 const net::HttpServerRequestInfo& request_info) {
462 CEF_REQUIRE_HT();
463
464 ConnectionInfo* info = GetConnectionInfo(connection_id);
465 DCHECK(info);
466 if (!info)
467 return;
468
469 DCHECK(!info->is_websocket);
470
471 handler_->OnHttpRequest(this, connection_id, request_info.peer.ToString(),
472 CreateRequest(address_, request_info, false));
473 }
474
OnWebSocketRequest(int connection_id,const net::HttpServerRequestInfo & request_info)475 void CefServerImpl::OnWebSocketRequest(
476 int connection_id,
477 const net::HttpServerRequestInfo& request_info) {
478 CEF_REQUIRE_HT();
479
480 ConnectionInfo* info = GetConnectionInfo(connection_id);
481 DCHECK(info);
482 if (!info)
483 return;
484
485 DCHECK(!info->is_websocket);
486 info->is_websocket = true;
487 info->is_websocket_pending = true;
488
489 // Will eventually result in a call to ContinueWebSocketRequest.
490 CefRefPtr<CefCallback> callback =
491 new AcceptWebSocketCallback(this, connection_id, request_info);
492 handler_->OnWebSocketRequest(
493 this, connection_id, request_info.peer.ToString(),
494 CreateRequest(address_, request_info, true), callback);
495 }
496
OnWebSocketMessage(int connection_id,std::string data)497 void CefServerImpl::OnWebSocketMessage(int connection_id, std::string data) {
498 CEF_REQUIRE_HT();
499
500 ConnectionInfo* info = GetConnectionInfo(connection_id);
501 if (!info)
502 return;
503
504 DCHECK(info->is_websocket);
505 DCHECK(!info->is_websocket_pending);
506
507 handler_->OnWebSocketMessage(this, connection_id, data.data(), data.size());
508 }
509
OnClose(int connection_id)510 void CefServerImpl::OnClose(int connection_id) {
511 CEF_REQUIRE_HT();
512
513 RemoveConnectionInfo(connection_id);
514 handler_->OnClientDisconnected(this, connection_id);
515 }
516
StartOnUIThread(const std::string & address,uint16 port,int backlog)517 void CefServerImpl::StartOnUIThread(const std::string& address,
518 uint16 port,
519 int backlog) {
520 CEF_REQUIRE_UIT();
521 DCHECK(!thread_);
522
523 std::unique_ptr<base::Thread> thread(
524 new base::Thread(base::StringPrintf("%s:%d", address.c_str(), port)));
525 base::Thread::Options options;
526 options.message_pump_type = base::MessagePumpType::IO;
527 if (thread->StartWithOptions(std::move(options))) {
528 // Add a reference that will be released in ShutdownOnUIThread().
529 AddRef();
530
531 thread_ = std::move(thread);
532 task_runner_ = thread_->task_runner();
533
534 CEF_POST_TASK_HT(base::BindOnce(&CefServerImpl::StartOnHandlerThread, this,
535 address, port, backlog));
536 }
537 }
538
StartOnHandlerThread(const std::string & address,uint16 port,int backlog)539 void CefServerImpl::StartOnHandlerThread(const std::string& address,
540 uint16 port,
541 int backlog) {
542 CEF_REQUIRE_HT();
543
544 std::unique_ptr<net::ServerSocket> socket(
545 new net::TCPServerSocket(nullptr, net::NetLogSource()));
546 if (socket->ListenWithAddressAndPort(address, port, backlog) == net::OK) {
547 server_.reset(new net::HttpServer(std::move(socket), this));
548
549 net::IPEndPoint address;
550 if (server_->GetLocalAddress(&address) == net::OK)
551 address_ = address.ToString();
552 }
553
554 handler_->OnServerCreated(this);
555
556 if (!server_) {
557 // Server failed to start.
558 handler_->OnServerDestroyed(this);
559
560 CEF_POST_TASK(CEF_UIT,
561 base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
562 }
563 }
564
ShutdownOnHandlerThread()565 void CefServerImpl::ShutdownOnHandlerThread() {
566 CEF_REQUIRE_HT();
567
568 if (server_) {
569 // Stop the server.
570 server_.reset();
571
572 if (!connection_info_map_.empty()) {
573 // Clear |connection_info_map_| first so any calls from
574 // OnClientDisconnected will fail as expected.
575 ConnectionInfoMap temp_map;
576 temp_map.swap(connection_info_map_);
577
578 // OnClose won't be called for clients that are connected when the server
579 // shuts down, so send the disconnected notification here.
580 ConnectionInfoMap::const_iterator it = temp_map.begin();
581 for (; it != temp_map.end(); ++it) {
582 handler_->OnClientDisconnected(this, it->first);
583 }
584 }
585
586 handler_->OnServerDestroyed(this);
587 }
588
589 CEF_POST_TASK(CEF_UIT,
590 base::BindOnce(&CefServerImpl::ShutdownOnUIThread, this));
591 }
592
ShutdownOnUIThread()593 void CefServerImpl::ShutdownOnUIThread() {
594 CEF_REQUIRE_UIT();
595
596 handler_ = nullptr;
597
598 if (thread_) {
599 // Stop the handler thread as a background task so the UI thread isn't
600 // blocked.
601 auto task = base::BindOnce(
602 [](std::unique_ptr<base::Thread> thread) {
603 // Calling PlatformThread::Join() on the UI thread is otherwise
604 // disallowed.
605 base::ScopedAllowBaseSyncPrimitivesForTesting
606 scoped_allow_sync_primitives;
607 thread.reset();
608 },
609 std::move(thread_));
610
611 // Make sure the task is executed on shutdown. Otherwise, |thread| might
612 // be released outside of the correct scope.
613 base::ThreadPool::PostTask(
614 FROM_HERE,
615 {base::TaskPriority::BEST_EFFORT,
616 base::TaskShutdownBehavior::BLOCK_SHUTDOWN, base::MayBlock()},
617 std::move(task));
618
619 // Release the reference that was added in StartupOnUIThread().
620 Release();
621 }
622 }
623
ValidateServer() const624 bool CefServerImpl::ValidateServer() const {
625 CEF_REQUIRE_HT();
626 if (!server_) {
627 LOG(ERROR) << "Server is not running";
628 return false;
629 }
630 return true;
631 }
632
CreateConnectionInfo(int connection_id)633 CefServerImpl::ConnectionInfo* CefServerImpl::CreateConnectionInfo(
634 int connection_id) {
635 CEF_REQUIRE_HT();
636
637 #if DCHECK_IS_ON()
638 ConnectionInfoMap::const_iterator it =
639 connection_info_map_.find(connection_id);
640 DCHECK(it == connection_info_map_.end());
641 #endif
642
643 ConnectionInfo* info = new ConnectionInfo();
644 connection_info_map_.insert(
645 std::make_pair(connection_id, base::WrapUnique(info)));
646 return info;
647 }
648
GetConnectionInfo(int connection_id) const649 CefServerImpl::ConnectionInfo* CefServerImpl::GetConnectionInfo(
650 int connection_id) const {
651 CEF_REQUIRE_HT();
652 ConnectionInfoMap::const_iterator it =
653 connection_info_map_.find(connection_id);
654 if (it != connection_info_map_.end())
655 return it->second.get();
656
657 LOG(ERROR) << "Invalid connection_id " << connection_id;
658 return nullptr;
659 }
660
RemoveConnectionInfo(int connection_id)661 void CefServerImpl::RemoveConnectionInfo(int connection_id) {
662 CEF_REQUIRE_HT();
663 ConnectionInfoMap::iterator it = connection_info_map_.find(connection_id);
664 DCHECK(it != connection_info_map_.end());
665 if (it != connection_info_map_.end())
666 connection_info_map_.erase(it);
667 }
668
CurrentlyOnHandlerThread() const669 bool CefServerImpl::CurrentlyOnHandlerThread() const {
670 return task_runner_ && task_runner_->BelongsToCurrentThread();
671 }
672