1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include "base/compiler_specific.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_writer.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/threading/thread.h"
20 #include "base/values.h"
21 #include "components/devtools_discovery/devtools_discovery_manager.h"
22 #include "components/devtools_http_handler/devtools_http_handler.h"
23 #include "components/devtools_http_handler/devtools_http_handler_delegate.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/devtools_agent_host.h"
26 #include "content/public/common/url_constants.h"
27 #include "content/public/common/user_agent.h"
28 #include "net/base/escape.h"
29 #include "net/base/io_buffer.h"
30 #include "net/base/ip_endpoint.h"
31 #include "net/base/net_errors.h"
32 #include "net/server/http_server.h"
33 #include "net/server/http_server_request_info.h"
34 #include "net/server/http_server_response_info.h"
35 #include "net/socket/server_socket.h"
37 #if defined(OS_ANDROID)
38 #include "base/android/build_info.h"
41 using content::BrowserThread
;
42 using content::DevToolsAgentHost
;
43 using content::DevToolsAgentHostClient
;
44 using devtools_discovery::DevToolsTargetDescriptor
;
46 namespace devtools_http_handler
{
50 const base::FilePath::CharType kDevToolsActivePortFileName
[] =
51 FILE_PATH_LITERAL("DevToolsActivePort");
53 const char kDevToolsHandlerThreadName
[] = "Chrome_DevToolsHandlerThread";
55 const char kThumbUrlPrefix
[] = "/thumb/";
56 const char kPageUrlPrefix
[] = "/devtools/page/";
58 const char kTargetIdField
[] = "id";
59 const char kTargetParentIdField
[] = "parentId";
60 const char kTargetTypeField
[] = "type";
61 const char kTargetTitleField
[] = "title";
62 const char kTargetDescriptionField
[] = "description";
63 const char kTargetUrlField
[] = "url";
64 const char kTargetThumbnailUrlField
[] = "thumbnailUrl";
65 const char kTargetFaviconUrlField
[] = "faviconUrl";
66 const char kTargetWebSocketDebuggerUrlField
[] = "webSocketDebuggerUrl";
67 const char kTargetDevtoolsFrontendUrlField
[] = "devtoolsFrontendUrl";
69 // Maximum write buffer size of devtools http/websocket connections.
70 // TODO(rmcilroy/pfieldman): Reduce this back to 100Mb when we have
71 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
72 const int32 kSendBufferSizeForDevTools
= 256 * 1024 * 1024; // 256Mb
76 // ServerWrapper -------------------------------------------------------------
77 // All methods in this class are only called on handler thread.
78 class ServerWrapper
: net::HttpServer::Delegate
{
80 ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
81 scoped_ptr
<net::ServerSocket
> socket
,
82 const base::FilePath
& frontend_dir
,
83 bool bundles_resources
);
85 int GetLocalAddress(net::IPEndPoint
* address
);
87 void AcceptWebSocket(int connection_id
,
88 const net::HttpServerRequestInfo
& request
);
89 void SendOverWebSocket(int connection_id
, const std::string
& message
);
90 void SendResponse(int connection_id
,
91 const net::HttpServerResponseInfo
& response
);
92 void Send200(int connection_id
,
93 const std::string
& data
,
94 const std::string
& mime_type
);
95 void Send404(int connection_id
);
96 void Send500(int connection_id
, const std::string
& message
);
97 void Close(int connection_id
);
99 void WriteActivePortToUserProfile(const base::FilePath
& output_directory
);
101 ~ServerWrapper() override
{}
104 // net::HttpServer::Delegate implementation.
105 void OnConnect(int connection_id
) override
{}
106 void OnHttpRequest(int connection_id
,
107 const net::HttpServerRequestInfo
& info
) override
;
108 void OnWebSocketRequest(int connection_id
,
109 const net::HttpServerRequestInfo
& info
) override
;
110 void OnWebSocketMessage(int connection_id
,
111 const std::string
& data
) override
;
112 void OnClose(int connection_id
) override
;
114 base::WeakPtr
<DevToolsHttpHandler
> handler_
;
115 scoped_ptr
<net::HttpServer
> server_
;
116 base::FilePath frontend_dir_
;
117 bool bundles_resources_
;
120 ServerWrapper::ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
121 scoped_ptr
<net::ServerSocket
> socket
,
122 const base::FilePath
& frontend_dir
,
123 bool bundles_resources
)
125 server_(new net::HttpServer(socket
.Pass(), this)),
126 frontend_dir_(frontend_dir
),
127 bundles_resources_(bundles_resources
) {
130 int ServerWrapper::GetLocalAddress(net::IPEndPoint
* address
) {
131 return server_
->GetLocalAddress(address
);
134 void ServerWrapper::AcceptWebSocket(int connection_id
,
135 const net::HttpServerRequestInfo
& request
) {
136 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
137 server_
->AcceptWebSocket(connection_id
, request
);
140 void ServerWrapper::SendOverWebSocket(int connection_id
,
141 const std::string
& message
) {
142 server_
->SendOverWebSocket(connection_id
, message
);
145 void ServerWrapper::SendResponse(int connection_id
,
146 const net::HttpServerResponseInfo
& response
) {
147 server_
->SendResponse(connection_id
, response
);
150 void ServerWrapper::Send200(int connection_id
,
151 const std::string
& data
,
152 const std::string
& mime_type
) {
153 server_
->Send200(connection_id
, data
, mime_type
);
156 void ServerWrapper::Send404(int connection_id
) {
157 server_
->Send404(connection_id
);
160 void ServerWrapper::Send500(int connection_id
,
161 const std::string
& message
) {
162 server_
->Send500(connection_id
, message
);
165 void ServerWrapper::Close(int connection_id
) {
166 server_
->Close(connection_id
);
169 // Thread and ServerWrapper lifetime management ------------------------------
171 void TerminateOnUI(base::Thread
* thread
,
172 ServerWrapper
* server_wrapper
,
173 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
) {
174 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
175 if (server_wrapper
) {
177 thread
->message_loop()->DeleteSoon(FROM_HERE
, server_wrapper
);
179 if (socket_factory
) {
181 thread
->message_loop()->DeleteSoon(FROM_HERE
, socket_factory
);
184 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, thread
);
188 void ServerStartedOnUI(
189 base::WeakPtr
<DevToolsHttpHandler
> handler
,
190 base::Thread
* thread
,
191 ServerWrapper
* server_wrapper
,
192 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
,
193 scoped_ptr
<net::IPEndPoint
> ip_address
) {
194 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
195 if (handler
&& thread
&& server_wrapper
) {
196 handler
->ServerStarted(thread
, server_wrapper
, socket_factory
,
199 TerminateOnUI(thread
, server_wrapper
, socket_factory
);
203 void StartServerOnHandlerThread(
204 base::WeakPtr
<DevToolsHttpHandler
> handler
,
205 base::Thread
* thread
,
206 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
207 const base::FilePath
& output_directory
,
208 const base::FilePath
& frontend_dir
,
209 bool bundles_resources
) {
210 DCHECK_EQ(thread
->message_loop(), base::MessageLoop::current());
211 ServerWrapper
* server_wrapper
= nullptr;
212 scoped_ptr
<net::ServerSocket
> server_socket
=
213 server_socket_factory
->CreateForHttpServer();
214 scoped_ptr
<net::IPEndPoint
> ip_address(new net::IPEndPoint
);
216 server_wrapper
= new ServerWrapper(handler
, server_socket
.Pass(),
217 frontend_dir
, bundles_resources
);
218 if (!output_directory
.empty())
219 server_wrapper
->WriteActivePortToUserProfile(output_directory
);
221 if (server_wrapper
->GetLocalAddress(ip_address
.get()) != net::OK
)
225 LOG(ERROR
) << "Cannot start http server for devtools. Stop devtools.";
227 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
228 base::Bind(&ServerStartedOnUI
,
232 server_socket_factory
,
233 base::Passed(&ip_address
)));
236 void StartServerOnFile(
237 base::WeakPtr
<DevToolsHttpHandler
> handler
,
238 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
239 const base::FilePath
& output_directory
,
240 const base::FilePath
& frontend_dir
,
241 bool bundles_resources
) {
242 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
243 scoped_ptr
<base::Thread
> thread(new base::Thread(kDevToolsHandlerThreadName
));
244 base::Thread::Options options
;
245 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
246 if (thread
->StartWithOptions(options
)) {
247 base::MessageLoop
* message_loop
= thread
->message_loop();
248 message_loop
->task_runner()->PostTask(
250 base::Bind(&StartServerOnHandlerThread
, handler
,
251 base::Unretained(thread
.release()), server_socket_factory
,
252 output_directory
, frontend_dir
, bundles_resources
));
256 // DevToolsAgentHostClientImpl -----------------------------------------------
257 // An internal implementation of DevToolsAgentHostClient that delegates
258 // messages sent to a DebuggerShell instance.
259 class DevToolsAgentHostClientImpl
: public DevToolsAgentHostClient
{
261 DevToolsAgentHostClientImpl(base::MessageLoop
* message_loop
,
262 ServerWrapper
* server_wrapper
,
264 scoped_refptr
<DevToolsAgentHost
> agent_host
)
265 : message_loop_(message_loop
),
266 server_wrapper_(server_wrapper
),
267 connection_id_(connection_id
),
268 agent_host_(agent_host
) {
269 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
270 agent_host_
->AttachClient(this);
273 ~DevToolsAgentHostClientImpl() override
{
274 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
275 if (agent_host_
.get())
276 agent_host_
->DetachClient();
279 void AgentHostClosed(DevToolsAgentHost
* agent_host
,
280 bool replaced_with_another_client
) override
{
281 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
282 DCHECK(agent_host
== agent_host_
.get());
284 std::string message
= base::StringPrintf(
285 "{ \"method\": \"Inspector.detached\", "
286 "\"params\": { \"reason\": \"%s\"} }",
287 replaced_with_another_client
?
288 "replaced_with_devtools" : "target_closed");
289 DispatchProtocolMessage(agent_host
, message
);
291 agent_host_
= nullptr;
292 message_loop_
->task_runner()->PostTask(
294 base::Bind(&ServerWrapper::Close
, base::Unretained(server_wrapper_
),
298 void DispatchProtocolMessage(DevToolsAgentHost
* agent_host
,
299 const std::string
& message
) override
{
300 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
301 DCHECK(agent_host
== agent_host_
.get());
302 message_loop_
->task_runner()->PostTask(
304 base::Bind(&ServerWrapper::SendOverWebSocket
,
305 base::Unretained(server_wrapper_
), connection_id_
, message
));
308 void OnMessage(const std::string
& message
) {
309 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
310 if (agent_host_
.get())
311 agent_host_
->DispatchProtocolMessage(message
);
315 base::MessageLoop
* const message_loop_
;
316 ServerWrapper
* const server_wrapper_
;
317 const int connection_id_
;
318 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
321 static bool TimeComparator(const DevToolsTargetDescriptor
* desc1
,
322 const DevToolsTargetDescriptor
* desc2
) {
323 return desc1
->GetLastActivityTime() > desc2
->GetLastActivityTime();
326 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
328 scoped_ptr
<net::ServerSocket
>
329 DevToolsHttpHandler::ServerSocketFactory::CreateForHttpServer() {
330 return scoped_ptr
<net::ServerSocket
>();
333 scoped_ptr
<net::ServerSocket
>
334 DevToolsHttpHandler::ServerSocketFactory::CreateForTethering(
336 return scoped_ptr
<net::ServerSocket
>();
339 // DevToolsHttpHandler -------------------------------------------------------
341 DevToolsHttpHandler::~DevToolsHttpHandler() {
342 TerminateOnUI(thread_
, server_wrapper_
, socket_factory_
);
343 STLDeleteValues(&descriptor_map_
);
344 STLDeleteValues(&connection_to_client_
);
347 GURL
DevToolsHttpHandler::GetFrontendURL(const std::string
& path
) {
348 if (!server_ip_address_
)
350 return GURL(std::string("http://") + server_ip_address_
->ToString() +
351 (path
.empty() ? frontend_url_
: path
));
354 static std::string
PathWithoutParams(const std::string
& path
) {
355 size_t query_position
= path
.find("?");
356 if (query_position
!= std::string::npos
)
357 return path
.substr(0, query_position
);
361 static std::string
GetMimeType(const std::string
& filename
) {
362 if (base::EndsWith(filename
, ".html", base::CompareCase::INSENSITIVE_ASCII
)) {
364 } else if (base::EndsWith(filename
, ".css",
365 base::CompareCase::INSENSITIVE_ASCII
)) {
367 } else if (base::EndsWith(filename
, ".js",
368 base::CompareCase::INSENSITIVE_ASCII
)) {
369 return "application/javascript";
370 } else if (base::EndsWith(filename
, ".png",
371 base::CompareCase::INSENSITIVE_ASCII
)) {
373 } else if (base::EndsWith(filename
, ".gif",
374 base::CompareCase::INSENSITIVE_ASCII
)) {
376 } else if (base::EndsWith(filename
, ".json",
377 base::CompareCase::INSENSITIVE_ASCII
)) {
378 return "application/json";
380 LOG(ERROR
) << "GetMimeType doesn't know mime type for: "
382 << " text/plain will be returned";
387 void ServerWrapper::OnHttpRequest(int connection_id
,
388 const net::HttpServerRequestInfo
& info
) {
389 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
391 if (info
.path
.find("/json") == 0) {
392 BrowserThread::PostTask(
395 base::Bind(&DevToolsHttpHandler::OnJsonRequest
,
402 if (info
.path
.find(kThumbUrlPrefix
) == 0) {
403 // Thumbnail request.
404 const std::string target_id
= info
.path
.substr(strlen(kThumbUrlPrefix
));
405 BrowserThread::PostTask(
408 base::Bind(&DevToolsHttpHandler::OnThumbnailRequest
,
415 if (info
.path
.empty() || info
.path
== "/") {
416 // Discovery page request.
417 BrowserThread::PostTask(
420 base::Bind(&DevToolsHttpHandler::OnDiscoveryPageRequest
,
426 if (info
.path
.find("/devtools/") != 0) {
427 server_
->Send404(connection_id
);
431 std::string filename
= PathWithoutParams(info
.path
.substr(10));
432 std::string mime_type
= GetMimeType(filename
);
434 if (!frontend_dir_
.empty()) {
435 base::FilePath path
= frontend_dir_
.AppendASCII(filename
);
437 base::ReadFileToString(path
, &data
);
438 server_
->Send200(connection_id
, data
, mime_type
);
442 if (bundles_resources_
) {
443 BrowserThread::PostTask(
446 base::Bind(&DevToolsHttpHandler::OnFrontendResourceRequest
,
452 server_
->Send404(connection_id
);
455 void ServerWrapper::OnWebSocketRequest(
457 const net::HttpServerRequestInfo
& request
) {
458 BrowserThread::PostTask(
462 &DevToolsHttpHandler::OnWebSocketRequest
,
468 void ServerWrapper::OnWebSocketMessage(int connection_id
,
469 const std::string
& data
) {
470 BrowserThread::PostTask(
474 &DevToolsHttpHandler::OnWebSocketMessage
,
480 void ServerWrapper::OnClose(int connection_id
) {
481 BrowserThread::PostTask(
485 &DevToolsHttpHandler::OnClose
,
490 std::string
DevToolsHttpHandler::GetFrontendURLInternal(
491 const std::string id
,
492 const std::string
& host
) {
493 return base::StringPrintf(
495 frontend_url_
.c_str(),
496 frontend_url_
.find("?") == std::string::npos
? "?" : "&",
502 static bool ParseJsonPath(
503 const std::string
& path
,
504 std::string
* command
,
505 std::string
* target_id
) {
507 // Fall back to list in case of empty query.
513 if (path
.find("/") != 0) {
514 // Malformed command.
517 *command
= path
.substr(1);
519 size_t separator_pos
= command
->find("/");
520 if (separator_pos
!= std::string::npos
) {
521 *target_id
= command
->substr(separator_pos
+ 1);
522 *command
= command
->substr(0, separator_pos
);
527 void DevToolsHttpHandler::OnJsonRequest(
529 const net::HttpServerRequestInfo
& info
) {
531 std::string path
= info
.path
.substr(5);
533 // Trim fragment and query
535 size_t query_pos
= path
.find("?");
536 if (query_pos
!= std::string::npos
) {
537 query
= path
.substr(query_pos
+ 1);
538 path
= path
.substr(0, query_pos
);
541 size_t fragment_pos
= path
.find("#");
542 if (fragment_pos
!= std::string::npos
)
543 path
= path
.substr(0, fragment_pos
);
546 std::string target_id
;
547 if (!ParseJsonPath(path
, &command
, &target_id
)) {
548 SendJson(connection_id
,
551 "Malformed query: " + info
.path
);
555 if (command
== "version") {
556 base::DictionaryValue version
;
557 version
.SetString("Protocol-Version",
558 DevToolsAgentHost::GetProtocolVersion().c_str());
559 version
.SetString("WebKit-Version", content::GetWebKitVersion());
560 version
.SetString("Browser", product_name_
);
561 version
.SetString("User-Agent", user_agent_
);
562 #if defined(OS_ANDROID)
563 version
.SetString("Android-Package",
564 base::android::BuildInfo::GetInstance()->package_name());
566 SendJson(connection_id
, net::HTTP_OK
, &version
, std::string());
570 if (command
== "list") {
571 std::string host
= info
.headers
["host"];
572 DevToolsTargetDescriptor::List descriptors
=
573 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
575 std::sort(descriptors
.begin(), descriptors
.end(), TimeComparator
);
576 STLDeleteValues(&descriptor_map_
);
577 base::ListValue list_value
;
578 for (DevToolsTargetDescriptor
* descriptor
: descriptors
) {
579 descriptor_map_
[descriptor
->GetId()] = descriptor
;
580 list_value
.Append(SerializeDescriptor(*descriptor
, host
));
582 SendJson(connection_id
, net::HTTP_OK
, &list_value
, std::string());
586 if (command
== "new") {
587 GURL
url(net::UnescapeURLComponent(
588 query
, net::UnescapeRule::URL_SPECIAL_CHARS
));
590 url
= GURL(url::kAboutBlankURL
);
591 scoped_ptr
<DevToolsTargetDescriptor
> descriptor
=
592 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
595 SendJson(connection_id
,
596 net::HTTP_INTERNAL_SERVER_ERROR
,
598 "Could not create new page");
601 std::string host
= info
.headers
["host"];
602 scoped_ptr
<base::DictionaryValue
> dictionary(
603 SerializeDescriptor(*descriptor
.get(), host
));
604 SendJson(connection_id
, net::HTTP_OK
, dictionary
.get(), std::string());
605 const std::string target_id
= descriptor
->GetId();
606 descriptor_map_
[target_id
] = descriptor
.release();
610 if (command
== "activate" || command
== "close") {
611 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
613 SendJson(connection_id
,
616 "No such target id: " + target_id
);
620 if (command
== "activate") {
621 if (descriptor
->Activate()) {
622 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target activated");
624 SendJson(connection_id
,
625 net::HTTP_INTERNAL_SERVER_ERROR
,
627 "Could not activate target id: " + target_id
);
632 if (command
== "close") {
633 if (descriptor
->Close()) {
634 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target is closing");
636 SendJson(connection_id
,
637 net::HTTP_INTERNAL_SERVER_ERROR
,
639 "Could not close target id: " + target_id
);
644 SendJson(connection_id
,
647 "Unknown command: " + command
);
651 DevToolsTargetDescriptor
* DevToolsHttpHandler::GetDescriptor(
652 const std::string
& target_id
) {
653 DescriptorMap::const_iterator it
= descriptor_map_
.find(target_id
);
654 if (it
== descriptor_map_
.end())
659 void DevToolsHttpHandler::OnThumbnailRequest(
660 int connection_id
, const std::string
& target_id
) {
661 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
664 page_url
= descriptor
->GetURL();
665 std::string data
= delegate_
->GetPageThumbnailData(page_url
);
667 Send200(connection_id
, data
, "image/png");
669 Send404(connection_id
);
672 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id
) {
673 std::string response
= delegate_
->GetDiscoveryPageHTML();
674 Send200(connection_id
, response
, "text/html; charset=UTF-8");
677 void DevToolsHttpHandler::OnFrontendResourceRequest(
678 int connection_id
, const std::string
& path
) {
679 Send200(connection_id
,
680 delegate_
->GetFrontendResource(path
),
684 void DevToolsHttpHandler::OnWebSocketRequest(
686 const net::HttpServerRequestInfo
& request
) {
690 std::string browser_prefix
= "/devtools/browser";
691 size_t browser_pos
= request
.path
.find(browser_prefix
);
692 if (browser_pos
== 0) {
693 scoped_refptr
<DevToolsAgentHost
> browser_agent
=
694 DevToolsAgentHost::CreateForBrowser(
695 thread_
->task_runner(),
696 base::Bind(&ServerSocketFactory::CreateForTethering
,
697 base::Unretained(socket_factory_
)));
698 connection_to_client_
[connection_id
] = new DevToolsAgentHostClientImpl(
699 thread_
->message_loop(), server_wrapper_
, connection_id
, browser_agent
);
700 AcceptWebSocket(connection_id
, request
);
704 size_t pos
= request
.path
.find(kPageUrlPrefix
);
706 Send404(connection_id
);
710 std::string target_id
= request
.path
.substr(strlen(kPageUrlPrefix
));
711 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
712 scoped_refptr
<DevToolsAgentHost
> agent
=
713 descriptor
? descriptor
->GetAgentHost() : nullptr;
715 Send500(connection_id
, "No such target id: " + target_id
);
719 if (agent
->IsAttached()) {
720 Send500(connection_id
,
721 "Target with given id is being inspected: " + target_id
);
725 DevToolsAgentHostClientImpl
* client_host
= new DevToolsAgentHostClientImpl(
726 thread_
->message_loop(), server_wrapper_
, connection_id
, agent
);
727 connection_to_client_
[connection_id
] = client_host
;
729 AcceptWebSocket(connection_id
, request
);
732 void DevToolsHttpHandler::OnWebSocketMessage(
734 const std::string
& data
) {
735 ConnectionToClientMap::iterator it
=
736 connection_to_client_
.find(connection_id
);
737 if (it
!= connection_to_client_
.end())
738 it
->second
->OnMessage(data
);
741 void DevToolsHttpHandler::OnClose(int connection_id
) {
742 ConnectionToClientMap::iterator it
=
743 connection_to_client_
.find(connection_id
);
744 if (it
!= connection_to_client_
.end()) {
746 connection_to_client_
.erase(connection_id
);
750 DevToolsHttpHandler::DevToolsHttpHandler(
751 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
752 const std::string
& frontend_url
,
753 DevToolsHttpHandlerDelegate
* delegate
,
754 const base::FilePath
& output_directory
,
755 const base::FilePath
& debug_frontend_dir
,
756 const std::string
& product_name
,
757 const std::string
& user_agent
)
759 frontend_url_(frontend_url
),
760 product_name_(product_name
),
761 user_agent_(user_agent
),
762 server_wrapper_(nullptr),
764 socket_factory_(nullptr),
765 weak_factory_(this) {
766 bool bundles_resources
= frontend_url_
.empty();
767 if (frontend_url_
.empty())
768 frontend_url_
= "/devtools/inspector.html";
770 BrowserThread::PostTask(
771 BrowserThread::FILE, FROM_HERE
,
772 base::Bind(&StartServerOnFile
,
773 weak_factory_
.GetWeakPtr(),
774 server_socket_factory
.release(),
780 void DevToolsHttpHandler::ServerStarted(
781 base::Thread
* thread
,
782 ServerWrapper
* server_wrapper
,
783 ServerSocketFactory
* socket_factory
,
784 scoped_ptr
<net::IPEndPoint
> ip_address
) {
786 server_wrapper_
= server_wrapper
;
787 socket_factory_
= socket_factory
;
788 server_ip_address_
.swap(ip_address
);
791 void ServerWrapper::WriteActivePortToUserProfile(
792 const base::FilePath
& output_directory
) {
793 DCHECK(!output_directory
.empty());
794 net::IPEndPoint endpoint
;
796 if ((err
= server_
->GetLocalAddress(&endpoint
)) != net::OK
) {
797 LOG(ERROR
) << "Error " << err
<< " getting local address";
801 // Write this port to a well-known file in the profile directory
802 // so Telemetry can pick it up.
803 base::FilePath path
= output_directory
.Append(kDevToolsActivePortFileName
);
804 std::string port_string
= base::IntToString(endpoint
.port());
805 if (base::WriteFile(path
, port_string
.c_str(),
806 static_cast<int>(port_string
.length())) < 0) {
807 LOG(ERROR
) << "Error writing DevTools active port to file";
811 void DevToolsHttpHandler::SendJson(int connection_id
,
812 net::HttpStatusCode status_code
,
814 const std::string
& message
) {
818 // Serialize value and message.
819 std::string json_value
;
821 base::JSONWriter::WriteWithOptions(
822 *value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &json_value
);
824 std::string json_message
;
825 base::JSONWriter::Write(base::StringValue(message
), &json_message
);
827 net::HttpServerResponseInfo
response(status_code
);
828 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
830 thread_
->task_runner()->PostTask(
832 base::Bind(&ServerWrapper::SendResponse
,
833 base::Unretained(server_wrapper_
), connection_id
, response
));
836 void DevToolsHttpHandler::Send200(int connection_id
,
837 const std::string
& data
,
838 const std::string
& mime_type
) {
841 thread_
->task_runner()->PostTask(
843 base::Bind(&ServerWrapper::Send200
, base::Unretained(server_wrapper_
),
844 connection_id
, data
, mime_type
));
847 void DevToolsHttpHandler::Send404(int connection_id
) {
850 thread_
->task_runner()->PostTask(
851 FROM_HERE
, base::Bind(&ServerWrapper::Send404
,
852 base::Unretained(server_wrapper_
), connection_id
));
855 void DevToolsHttpHandler::Send500(int connection_id
,
856 const std::string
& message
) {
859 thread_
->task_runner()->PostTask(
861 base::Bind(&ServerWrapper::Send500
, base::Unretained(server_wrapper_
),
862 connection_id
, message
));
865 void DevToolsHttpHandler::AcceptWebSocket(
867 const net::HttpServerRequestInfo
& request
) {
870 thread_
->task_runner()->PostTask(
872 base::Bind(&ServerWrapper::AcceptWebSocket
,
873 base::Unretained(server_wrapper_
), connection_id
, request
));
876 base::DictionaryValue
* DevToolsHttpHandler::SerializeDescriptor(
877 const DevToolsTargetDescriptor
& descriptor
,
878 const std::string
& host
) {
879 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
881 std::string id
= descriptor
.GetId();
882 dictionary
->SetString(kTargetIdField
, id
);
883 std::string parent_id
= descriptor
.GetParentId();
884 if (!parent_id
.empty())
885 dictionary
->SetString(kTargetParentIdField
, parent_id
);
886 dictionary
->SetString(kTargetTypeField
, descriptor
.GetType());
887 dictionary
->SetString(kTargetTitleField
,
888 net::EscapeForHTML(descriptor
.GetTitle()));
889 dictionary
->SetString(kTargetDescriptionField
, descriptor
.GetDescription());
891 GURL url
= descriptor
.GetURL();
892 dictionary
->SetString(kTargetUrlField
, url
.spec());
894 GURL favicon_url
= descriptor
.GetFaviconURL();
895 if (favicon_url
.is_valid())
896 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
898 if (!delegate_
->GetPageThumbnailData(url
).empty()) {
899 dictionary
->SetString(kTargetThumbnailUrlField
,
900 std::string(kThumbUrlPrefix
) + id
);
903 if (!descriptor
.IsAttached()) {
904 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
905 base::StringPrintf("ws://%s%s%s",
909 std::string devtools_frontend_url
= GetFrontendURLInternal(
912 dictionary
->SetString(
913 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
919 } // namespace devtools_http_handler