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/browser/devtools_external_agent_proxy_delegate.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/common/user_agent.h"
29 #include "net/base/escape.h"
30 #include "net/base/io_buffer.h"
31 #include "net/base/ip_endpoint.h"
32 #include "net/base/net_errors.h"
33 #include "net/server/http_server.h"
34 #include "net/server/http_server_request_info.h"
35 #include "net/server/http_server_response_info.h"
36 #include "net/socket/server_socket.h"
38 #if defined(OS_ANDROID)
39 #include "base/android/build_info.h"
42 using content::BrowserThread
;
43 using content::DevToolsAgentHost
;
44 using content::DevToolsAgentHostClient
;
45 using devtools_discovery::DevToolsTargetDescriptor
;
47 namespace devtools_http_handler
{
51 const base::FilePath::CharType kDevToolsActivePortFileName
[] =
52 FILE_PATH_LITERAL("DevToolsActivePort");
54 const char kDevToolsHandlerThreadName
[] = "Chrome_DevToolsHandlerThread";
56 const char kThumbUrlPrefix
[] = "/thumb/";
57 const char kPageUrlPrefix
[] = "/devtools/page/";
59 const char kTargetIdField
[] = "id";
60 const char kTargetParentIdField
[] = "parentId";
61 const char kTargetTypeField
[] = "type";
62 const char kTargetTitleField
[] = "title";
63 const char kTargetDescriptionField
[] = "description";
64 const char kTargetUrlField
[] = "url";
65 const char kTargetThumbnailUrlField
[] = "thumbnailUrl";
66 const char kTargetFaviconUrlField
[] = "faviconUrl";
67 const char kTargetWebSocketDebuggerUrlField
[] = "webSocketDebuggerUrl";
68 const char kTargetDevtoolsFrontendUrlField
[] = "devtoolsFrontendUrl";
70 // Maximum write buffer size of devtools http/websocket connections.
71 // TODO(rmcilroy/pfieldman): Reduce this back to 100Mb when we have
72 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
73 const int32 kSendBufferSizeForDevTools
= 256 * 1024 * 1024; // 256Mb
77 // ServerWrapper -------------------------------------------------------------
78 // All methods in this class are only called on handler thread.
79 class ServerWrapper
: net::HttpServer::Delegate
{
81 ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
82 scoped_ptr
<net::ServerSocket
> socket
,
83 const base::FilePath
& frontend_dir
,
84 bool bundles_resources
);
86 int GetLocalAddress(net::IPEndPoint
* address
);
88 void AcceptWebSocket(int connection_id
,
89 const net::HttpServerRequestInfo
& request
);
90 void SendOverWebSocket(int connection_id
, const std::string
& message
);
91 void SendResponse(int connection_id
,
92 const net::HttpServerResponseInfo
& response
);
93 void Send200(int connection_id
,
94 const std::string
& data
,
95 const std::string
& mime_type
);
96 void Send404(int connection_id
);
97 void Send500(int connection_id
, const std::string
& message
);
98 void Close(int connection_id
);
100 void WriteActivePortToUserProfile(const base::FilePath
& output_directory
);
102 ~ServerWrapper() override
{}
105 // net::HttpServer::Delegate implementation.
106 void OnConnect(int connection_id
) override
{}
107 void OnHttpRequest(int connection_id
,
108 const net::HttpServerRequestInfo
& info
) override
;
109 void OnWebSocketRequest(int connection_id
,
110 const net::HttpServerRequestInfo
& info
) override
;
111 void OnWebSocketMessage(int connection_id
,
112 const std::string
& data
) override
;
113 void OnClose(int connection_id
) override
;
115 base::WeakPtr
<DevToolsHttpHandler
> handler_
;
116 scoped_ptr
<net::HttpServer
> server_
;
117 base::FilePath frontend_dir_
;
118 bool bundles_resources_
;
121 ServerWrapper::ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
122 scoped_ptr
<net::ServerSocket
> socket
,
123 const base::FilePath
& frontend_dir
,
124 bool bundles_resources
)
126 server_(new net::HttpServer(socket
.Pass(), this)),
127 frontend_dir_(frontend_dir
),
128 bundles_resources_(bundles_resources
) {
131 int ServerWrapper::GetLocalAddress(net::IPEndPoint
* address
) {
132 return server_
->GetLocalAddress(address
);
135 void ServerWrapper::AcceptWebSocket(int connection_id
,
136 const net::HttpServerRequestInfo
& request
) {
137 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
138 server_
->AcceptWebSocket(connection_id
, request
);
141 void ServerWrapper::SendOverWebSocket(int connection_id
,
142 const std::string
& message
) {
143 server_
->SendOverWebSocket(connection_id
, message
);
146 void ServerWrapper::SendResponse(int connection_id
,
147 const net::HttpServerResponseInfo
& response
) {
148 server_
->SendResponse(connection_id
, response
);
151 void ServerWrapper::Send200(int connection_id
,
152 const std::string
& data
,
153 const std::string
& mime_type
) {
154 server_
->Send200(connection_id
, data
, mime_type
);
157 void ServerWrapper::Send404(int connection_id
) {
158 server_
->Send404(connection_id
);
161 void ServerWrapper::Send500(int connection_id
,
162 const std::string
& message
) {
163 server_
->Send500(connection_id
, message
);
166 void ServerWrapper::Close(int connection_id
) {
167 server_
->Close(connection_id
);
170 // Thread and ServerWrapper lifetime management ------------------------------
172 void TerminateOnUI(base::Thread
* thread
,
173 ServerWrapper
* server_wrapper
,
174 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
) {
175 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
176 if (server_wrapper
) {
178 thread
->message_loop()->DeleteSoon(FROM_HERE
, server_wrapper
);
180 if (socket_factory
) {
182 thread
->message_loop()->DeleteSoon(FROM_HERE
, socket_factory
);
185 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, thread
);
189 void ServerStartedOnUI(
190 base::WeakPtr
<DevToolsHttpHandler
> handler
,
191 base::Thread
* thread
,
192 ServerWrapper
* server_wrapper
,
193 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
,
194 scoped_ptr
<net::IPEndPoint
> ip_address
) {
195 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
196 if (handler
&& thread
&& server_wrapper
) {
197 handler
->ServerStarted(thread
, server_wrapper
, socket_factory
,
200 TerminateOnUI(thread
, server_wrapper
, socket_factory
);
204 void StartServerOnHandlerThread(
205 base::WeakPtr
<DevToolsHttpHandler
> handler
,
206 base::Thread
* thread
,
207 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
208 const base::FilePath
& output_directory
,
209 const base::FilePath
& frontend_dir
,
210 bool bundles_resources
) {
211 DCHECK_EQ(thread
->message_loop(), base::MessageLoop::current());
212 ServerWrapper
* server_wrapper
= nullptr;
213 scoped_ptr
<net::ServerSocket
> server_socket
=
214 server_socket_factory
->CreateForHttpServer();
215 scoped_ptr
<net::IPEndPoint
> ip_address(new net::IPEndPoint
);
217 server_wrapper
= new ServerWrapper(handler
, server_socket
.Pass(),
218 frontend_dir
, bundles_resources
);
219 if (!output_directory
.empty())
220 server_wrapper
->WriteActivePortToUserProfile(output_directory
);
222 if (server_wrapper
->GetLocalAddress(ip_address
.get()) != net::OK
)
226 LOG(ERROR
) << "Cannot start http server for devtools. Stop devtools.";
228 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
229 base::Bind(&ServerStartedOnUI
,
233 server_socket_factory
,
234 base::Passed(&ip_address
)));
237 void StartServerOnFile(
238 base::WeakPtr
<DevToolsHttpHandler
> handler
,
239 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
240 const base::FilePath
& output_directory
,
241 const base::FilePath
& frontend_dir
,
242 bool bundles_resources
) {
243 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
244 scoped_ptr
<base::Thread
> thread(new base::Thread(kDevToolsHandlerThreadName
));
245 base::Thread::Options options
;
246 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
247 if (thread
->StartWithOptions(options
)) {
248 base::MessageLoop
* message_loop
= thread
->message_loop();
249 message_loop
->task_runner()->PostTask(
251 base::Bind(&StartServerOnHandlerThread
, handler
,
252 base::Unretained(thread
.release()), server_socket_factory
,
253 output_directory
, frontend_dir
, bundles_resources
));
257 // DevToolsAgentHostClientImpl -----------------------------------------------
258 // An internal implementation of DevToolsAgentHostClient that delegates
259 // messages sent to a DebuggerShell instance.
260 class DevToolsAgentHostClientImpl
: public DevToolsAgentHostClient
{
262 DevToolsAgentHostClientImpl(base::MessageLoop
* message_loop
,
263 ServerWrapper
* server_wrapper
,
265 scoped_refptr
<DevToolsAgentHost
> agent_host
)
266 : message_loop_(message_loop
),
267 server_wrapper_(server_wrapper
),
268 connection_id_(connection_id
),
269 agent_host_(agent_host
) {
270 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
271 agent_host_
->AttachClient(this);
274 ~DevToolsAgentHostClientImpl() override
{
275 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
276 if (agent_host_
.get())
277 agent_host_
->DetachClient();
280 void AgentHostClosed(DevToolsAgentHost
* agent_host
,
281 bool replaced_with_another_client
) override
{
282 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
283 DCHECK(agent_host
== agent_host_
.get());
285 std::string message
= base::StringPrintf(
286 "{ \"method\": \"Inspector.detached\", "
287 "\"params\": { \"reason\": \"%s\"} }",
288 replaced_with_another_client
?
289 "replaced_with_devtools" : "target_closed");
290 DispatchProtocolMessage(agent_host
, message
);
292 agent_host_
= nullptr;
293 message_loop_
->task_runner()->PostTask(
295 base::Bind(&ServerWrapper::Close
, base::Unretained(server_wrapper_
),
299 void DispatchProtocolMessage(DevToolsAgentHost
* agent_host
,
300 const std::string
& message
) override
{
301 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
302 DCHECK(agent_host
== agent_host_
.get());
303 message_loop_
->task_runner()->PostTask(
305 base::Bind(&ServerWrapper::SendOverWebSocket
,
306 base::Unretained(server_wrapper_
), connection_id_
, message
));
309 void OnMessage(const std::string
& message
) {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
311 if (agent_host_
.get())
312 agent_host_
->DispatchProtocolMessage(message
);
316 base::MessageLoop
* const message_loop_
;
317 ServerWrapper
* const server_wrapper_
;
318 const int connection_id_
;
319 scoped_refptr
<DevToolsAgentHost
> agent_host_
;
322 static bool TimeComparator(const DevToolsTargetDescriptor
* desc1
,
323 const DevToolsTargetDescriptor
* desc2
) {
324 return desc1
->GetLastActivityTime() > desc2
->GetLastActivityTime();
327 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
329 scoped_ptr
<net::ServerSocket
>
330 DevToolsHttpHandler::ServerSocketFactory::CreateForHttpServer() {
331 return scoped_ptr
<net::ServerSocket
>();
334 scoped_ptr
<net::ServerSocket
>
335 DevToolsHttpHandler::ServerSocketFactory::CreateForTethering(
337 return scoped_ptr
<net::ServerSocket
>();
340 // DevToolsHttpHandler -------------------------------------------------------
342 DevToolsHttpHandler::~DevToolsHttpHandler() {
343 TerminateOnUI(thread_
, server_wrapper_
, socket_factory_
);
344 STLDeleteValues(&descriptor_map_
);
345 STLDeleteValues(&connection_to_client_
);
348 GURL
DevToolsHttpHandler::GetFrontendURL(const std::string
& path
) {
349 if (!server_ip_address_
)
351 return GURL(std::string("http://") + server_ip_address_
->ToString() +
352 (path
.empty() ? frontend_url_
: path
));
355 static std::string
PathWithoutParams(const std::string
& path
) {
356 size_t query_position
= path
.find("?");
357 if (query_position
!= std::string::npos
)
358 return path
.substr(0, query_position
);
362 static std::string
GetMimeType(const std::string
& filename
) {
363 if (base::EndsWith(filename
, ".html", base::CompareCase::INSENSITIVE_ASCII
)) {
365 } else if (base::EndsWith(filename
, ".css",
366 base::CompareCase::INSENSITIVE_ASCII
)) {
368 } else if (base::EndsWith(filename
, ".js",
369 base::CompareCase::INSENSITIVE_ASCII
)) {
370 return "application/javascript";
371 } else if (base::EndsWith(filename
, ".png",
372 base::CompareCase::INSENSITIVE_ASCII
)) {
374 } else if (base::EndsWith(filename
, ".gif",
375 base::CompareCase::INSENSITIVE_ASCII
)) {
377 } else if (base::EndsWith(filename
, ".json",
378 base::CompareCase::INSENSITIVE_ASCII
)) {
379 return "application/json";
381 LOG(ERROR
) << "GetMimeType doesn't know mime type for: "
383 << " text/plain will be returned";
388 void ServerWrapper::OnHttpRequest(int connection_id
,
389 const net::HttpServerRequestInfo
& info
) {
390 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
392 if (info
.path
.find("/json") == 0) {
393 BrowserThread::PostTask(
396 base::Bind(&DevToolsHttpHandler::OnJsonRequest
,
403 if (info
.path
.find(kThumbUrlPrefix
) == 0) {
404 // Thumbnail request.
405 const std::string target_id
= info
.path
.substr(strlen(kThumbUrlPrefix
));
406 BrowserThread::PostTask(
409 base::Bind(&DevToolsHttpHandler::OnThumbnailRequest
,
416 if (info
.path
.empty() || info
.path
== "/") {
417 // Discovery page request.
418 BrowserThread::PostTask(
421 base::Bind(&DevToolsHttpHandler::OnDiscoveryPageRequest
,
427 if (info
.path
.find("/devtools/") != 0) {
428 server_
->Send404(connection_id
);
432 std::string filename
= PathWithoutParams(info
.path
.substr(10));
433 std::string mime_type
= GetMimeType(filename
);
435 if (!frontend_dir_
.empty()) {
436 base::FilePath path
= frontend_dir_
.AppendASCII(filename
);
438 base::ReadFileToString(path
, &data
);
439 server_
->Send200(connection_id
, data
, mime_type
);
443 if (bundles_resources_
) {
444 BrowserThread::PostTask(
447 base::Bind(&DevToolsHttpHandler::OnFrontendResourceRequest
,
453 server_
->Send404(connection_id
);
456 void ServerWrapper::OnWebSocketRequest(
458 const net::HttpServerRequestInfo
& request
) {
459 BrowserThread::PostTask(
463 &DevToolsHttpHandler::OnWebSocketRequest
,
469 void ServerWrapper::OnWebSocketMessage(int connection_id
,
470 const std::string
& data
) {
471 BrowserThread::PostTask(
475 &DevToolsHttpHandler::OnWebSocketMessage
,
481 void ServerWrapper::OnClose(int connection_id
) {
482 BrowserThread::PostTask(
486 &DevToolsHttpHandler::OnClose
,
491 std::string
DevToolsHttpHandler::GetFrontendURLInternal(
492 const std::string id
,
493 const std::string
& host
) {
494 return base::StringPrintf(
496 frontend_url_
.c_str(),
497 frontend_url_
.find("?") == std::string::npos
? "?" : "&",
503 static bool ParseJsonPath(
504 const std::string
& path
,
505 std::string
* command
,
506 std::string
* target_id
) {
508 // Fall back to list in case of empty query.
514 if (path
.find("/") != 0) {
515 // Malformed command.
518 *command
= path
.substr(1);
520 size_t separator_pos
= command
->find("/");
521 if (separator_pos
!= std::string::npos
) {
522 *target_id
= command
->substr(separator_pos
+ 1);
523 *command
= command
->substr(0, separator_pos
);
528 void DevToolsHttpHandler::OnJsonRequest(
530 const net::HttpServerRequestInfo
& info
) {
532 std::string path
= info
.path
.substr(5);
534 // Trim fragment and query
536 size_t query_pos
= path
.find("?");
537 if (query_pos
!= std::string::npos
) {
538 query
= path
.substr(query_pos
+ 1);
539 path
= path
.substr(0, query_pos
);
542 size_t fragment_pos
= path
.find("#");
543 if (fragment_pos
!= std::string::npos
)
544 path
= path
.substr(0, fragment_pos
);
547 std::string target_id
;
548 if (!ParseJsonPath(path
, &command
, &target_id
)) {
549 SendJson(connection_id
,
552 "Malformed query: " + info
.path
);
556 if (command
== "version") {
557 base::DictionaryValue version
;
558 version
.SetString("Protocol-Version",
559 DevToolsAgentHost::GetProtocolVersion().c_str());
560 version
.SetString("WebKit-Version", content::GetWebKitVersion());
561 version
.SetString("Browser", product_name_
);
562 version
.SetString("User-Agent", user_agent_
);
563 #if defined(OS_ANDROID)
564 version
.SetString("Android-Package",
565 base::android::BuildInfo::GetInstance()->package_name());
567 SendJson(connection_id
, net::HTTP_OK
, &version
, std::string());
571 if (command
== "list") {
572 std::string host
= info
.headers
["host"];
573 DevToolsTargetDescriptor::List descriptors
=
574 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
576 std::sort(descriptors
.begin(), descriptors
.end(), TimeComparator
);
577 STLDeleteValues(&descriptor_map_
);
578 base::ListValue list_value
;
579 for (DevToolsTargetDescriptor
* descriptor
: descriptors
) {
580 descriptor_map_
[descriptor
->GetId()] = descriptor
;
581 list_value
.Append(SerializeDescriptor(*descriptor
, host
));
583 SendJson(connection_id
, net::HTTP_OK
, &list_value
, std::string());
587 if (command
== "new") {
588 GURL
url(net::UnescapeURLComponent(
589 query
, net::UnescapeRule::URL_SPECIAL_CHARS
));
591 url
= GURL(url::kAboutBlankURL
);
592 scoped_ptr
<DevToolsTargetDescriptor
> descriptor
=
593 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
596 SendJson(connection_id
,
597 net::HTTP_INTERNAL_SERVER_ERROR
,
599 "Could not create new page");
602 std::string host
= info
.headers
["host"];
603 scoped_ptr
<base::DictionaryValue
> dictionary(
604 SerializeDescriptor(*descriptor
.get(), host
));
605 SendJson(connection_id
, net::HTTP_OK
, dictionary
.get(), std::string());
606 const std::string target_id
= descriptor
->GetId();
607 descriptor_map_
[target_id
] = descriptor
.release();
611 if (command
== "activate" || command
== "close") {
612 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
614 SendJson(connection_id
,
617 "No such target id: " + target_id
);
621 if (command
== "activate") {
622 if (descriptor
->Activate()) {
623 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target activated");
625 SendJson(connection_id
,
626 net::HTTP_INTERNAL_SERVER_ERROR
,
628 "Could not activate target id: " + target_id
);
633 if (command
== "close") {
634 if (descriptor
->Close()) {
635 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target is closing");
637 SendJson(connection_id
,
638 net::HTTP_INTERNAL_SERVER_ERROR
,
640 "Could not close target id: " + target_id
);
645 SendJson(connection_id
,
648 "Unknown command: " + command
);
652 DevToolsTargetDescriptor
* DevToolsHttpHandler::GetDescriptor(
653 const std::string
& target_id
) {
654 DescriptorMap::const_iterator it
= descriptor_map_
.find(target_id
);
655 if (it
== descriptor_map_
.end())
660 void DevToolsHttpHandler::OnThumbnailRequest(
661 int connection_id
, const std::string
& target_id
) {
662 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
665 page_url
= descriptor
->GetURL();
666 std::string data
= delegate_
->GetPageThumbnailData(page_url
);
668 Send200(connection_id
, data
, "image/png");
670 Send404(connection_id
);
673 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id
) {
674 std::string response
= delegate_
->GetDiscoveryPageHTML();
675 Send200(connection_id
, response
, "text/html; charset=UTF-8");
678 void DevToolsHttpHandler::OnFrontendResourceRequest(
679 int connection_id
, const std::string
& path
) {
680 Send200(connection_id
,
681 delegate_
->GetFrontendResource(path
),
685 void DevToolsHttpHandler::OnWebSocketRequest(
687 const net::HttpServerRequestInfo
& request
) {
691 std::string browser_prefix
= "/devtools/browser";
692 size_t browser_pos
= request
.path
.find(browser_prefix
);
693 if (browser_pos
== 0) {
694 scoped_refptr
<DevToolsAgentHost
> browser_agent
=
695 DevToolsAgentHost::CreateForBrowser(
696 thread_
->task_runner(),
697 base::Bind(&ServerSocketFactory::CreateForTethering
,
698 base::Unretained(socket_factory_
)));
699 connection_to_client_
[connection_id
] = new DevToolsAgentHostClientImpl(
700 thread_
->message_loop(), server_wrapper_
, connection_id
, browser_agent
);
701 AcceptWebSocket(connection_id
, request
);
705 // Handle external connections (such as frontend api) on the embedder level.
706 content::DevToolsExternalAgentProxyDelegate
* external_delegate
=
707 delegate_
->HandleWebSocketConnection(request
.path
);
708 if (external_delegate
) {
709 scoped_refptr
<DevToolsAgentHost
> agent_host
=
710 DevToolsAgentHost::Create(external_delegate
);
711 connection_to_client_
[connection_id
] = new DevToolsAgentHostClientImpl(
712 thread_
->message_loop(), server_wrapper_
, connection_id
, agent_host
);
713 AcceptWebSocket(connection_id
, request
);
717 size_t pos
= request
.path
.find(kPageUrlPrefix
);
719 Send404(connection_id
);
723 std::string target_id
= request
.path
.substr(strlen(kPageUrlPrefix
));
724 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
725 scoped_refptr
<DevToolsAgentHost
> agent
=
726 descriptor
? descriptor
->GetAgentHost() : nullptr;
728 Send500(connection_id
, "No such target id: " + target_id
);
732 if (agent
->IsAttached()) {
733 Send500(connection_id
,
734 "Target with given id is being inspected: " + target_id
);
738 DevToolsAgentHostClientImpl
* client_host
= new DevToolsAgentHostClientImpl(
739 thread_
->message_loop(), server_wrapper_
, connection_id
, agent
);
740 connection_to_client_
[connection_id
] = client_host
;
742 AcceptWebSocket(connection_id
, request
);
745 void DevToolsHttpHandler::OnWebSocketMessage(
747 const std::string
& data
) {
748 ConnectionToClientMap::iterator it
=
749 connection_to_client_
.find(connection_id
);
750 if (it
!= connection_to_client_
.end())
751 it
->second
->OnMessage(data
);
754 void DevToolsHttpHandler::OnClose(int connection_id
) {
755 ConnectionToClientMap::iterator it
=
756 connection_to_client_
.find(connection_id
);
757 if (it
!= connection_to_client_
.end()) {
759 connection_to_client_
.erase(connection_id
);
763 DevToolsHttpHandler::DevToolsHttpHandler(
764 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
765 const std::string
& frontend_url
,
766 DevToolsHttpHandlerDelegate
* delegate
,
767 const base::FilePath
& output_directory
,
768 const base::FilePath
& debug_frontend_dir
,
769 const std::string
& product_name
,
770 const std::string
& user_agent
)
772 frontend_url_(frontend_url
),
773 product_name_(product_name
),
774 user_agent_(user_agent
),
775 server_wrapper_(nullptr),
777 socket_factory_(nullptr),
778 weak_factory_(this) {
779 bool bundles_resources
= frontend_url_
.empty();
780 if (frontend_url_
.empty())
781 frontend_url_
= "/devtools/inspector.html";
783 BrowserThread::PostTask(
784 BrowserThread::FILE, FROM_HERE
,
785 base::Bind(&StartServerOnFile
,
786 weak_factory_
.GetWeakPtr(),
787 server_socket_factory
.release(),
793 void DevToolsHttpHandler::ServerStarted(
794 base::Thread
* thread
,
795 ServerWrapper
* server_wrapper
,
796 ServerSocketFactory
* socket_factory
,
797 scoped_ptr
<net::IPEndPoint
> ip_address
) {
799 server_wrapper_
= server_wrapper
;
800 socket_factory_
= socket_factory
;
801 server_ip_address_
.swap(ip_address
);
804 void ServerWrapper::WriteActivePortToUserProfile(
805 const base::FilePath
& output_directory
) {
806 DCHECK(!output_directory
.empty());
807 net::IPEndPoint endpoint
;
809 if ((err
= server_
->GetLocalAddress(&endpoint
)) != net::OK
) {
810 LOG(ERROR
) << "Error " << err
<< " getting local address";
814 // Write this port to a well-known file in the profile directory
815 // so Telemetry can pick it up.
816 base::FilePath path
= output_directory
.Append(kDevToolsActivePortFileName
);
817 std::string port_string
= base::IntToString(endpoint
.port());
818 if (base::WriteFile(path
, port_string
.c_str(),
819 static_cast<int>(port_string
.length())) < 0) {
820 LOG(ERROR
) << "Error writing DevTools active port to file";
824 void DevToolsHttpHandler::SendJson(int connection_id
,
825 net::HttpStatusCode status_code
,
827 const std::string
& message
) {
831 // Serialize value and message.
832 std::string json_value
;
834 base::JSONWriter::WriteWithOptions(
835 *value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &json_value
);
837 std::string json_message
;
838 base::JSONWriter::Write(base::StringValue(message
), &json_message
);
840 net::HttpServerResponseInfo
response(status_code
);
841 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
843 thread_
->task_runner()->PostTask(
845 base::Bind(&ServerWrapper::SendResponse
,
846 base::Unretained(server_wrapper_
), connection_id
, response
));
849 void DevToolsHttpHandler::Send200(int connection_id
,
850 const std::string
& data
,
851 const std::string
& mime_type
) {
854 thread_
->task_runner()->PostTask(
856 base::Bind(&ServerWrapper::Send200
, base::Unretained(server_wrapper_
),
857 connection_id
, data
, mime_type
));
860 void DevToolsHttpHandler::Send404(int connection_id
) {
863 thread_
->task_runner()->PostTask(
864 FROM_HERE
, base::Bind(&ServerWrapper::Send404
,
865 base::Unretained(server_wrapper_
), connection_id
));
868 void DevToolsHttpHandler::Send500(int connection_id
,
869 const std::string
& message
) {
872 thread_
->task_runner()->PostTask(
874 base::Bind(&ServerWrapper::Send500
, base::Unretained(server_wrapper_
),
875 connection_id
, message
));
878 void DevToolsHttpHandler::AcceptWebSocket(
880 const net::HttpServerRequestInfo
& request
) {
883 thread_
->task_runner()->PostTask(
885 base::Bind(&ServerWrapper::AcceptWebSocket
,
886 base::Unretained(server_wrapper_
), connection_id
, request
));
889 base::DictionaryValue
* DevToolsHttpHandler::SerializeDescriptor(
890 const DevToolsTargetDescriptor
& descriptor
,
891 const std::string
& host
) {
892 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
894 std::string id
= descriptor
.GetId();
895 dictionary
->SetString(kTargetIdField
, id
);
896 std::string parent_id
= descriptor
.GetParentId();
897 if (!parent_id
.empty())
898 dictionary
->SetString(kTargetParentIdField
, parent_id
);
899 dictionary
->SetString(kTargetTypeField
, descriptor
.GetType());
900 dictionary
->SetString(kTargetTitleField
,
901 net::EscapeForHTML(descriptor
.GetTitle()));
902 dictionary
->SetString(kTargetDescriptionField
, descriptor
.GetDescription());
904 GURL url
= descriptor
.GetURL();
905 dictionary
->SetString(kTargetUrlField
, url
.spec());
907 GURL favicon_url
= descriptor
.GetFaviconURL();
908 if (favicon_url
.is_valid())
909 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
911 if (!delegate_
->GetPageThumbnailData(url
).empty()) {
912 dictionary
->SetString(kTargetThumbnailUrlField
,
913 std::string(kThumbUrlPrefix
) + id
);
916 if (!descriptor
.IsAttached()) {
917 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
918 base::StringPrintf("ws://%s%s%s",
922 std::string devtools_frontend_url
= GetFrontendURLInternal(
925 dictionary
->SetString(
926 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
932 } // namespace devtools_http_handler