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/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/threading/thread.h"
19 #include "base/values.h"
20 #include "components/devtools_discovery/devtools_discovery_manager.h"
21 #include "components/devtools_http_handler/devtools_http_handler.h"
22 #include "components/devtools_http_handler/devtools_http_handler_delegate.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/devtools_agent_host.h"
25 #include "content/public/common/url_constants.h"
26 #include "content/public/common/user_agent.h"
27 #include "net/base/escape.h"
28 #include "net/base/io_buffer.h"
29 #include "net/base/ip_endpoint.h"
30 #include "net/base/net_errors.h"
31 #include "net/server/http_server.h"
32 #include "net/server/http_server_request_info.h"
33 #include "net/server/http_server_response_info.h"
34 #include "net/socket/server_socket.h"
36 #if defined(OS_ANDROID)
37 #include "base/android/build_info.h"
40 using content::BrowserThread
;
41 using content::DevToolsAgentHost
;
42 using content::DevToolsAgentHostClient
;
43 using devtools_discovery::DevToolsTargetDescriptor
;
45 namespace devtools_http_handler
{
49 const base::FilePath::CharType kDevToolsActivePortFileName
[] =
50 FILE_PATH_LITERAL("DevToolsActivePort");
52 const char kDevToolsHandlerThreadName
[] = "Chrome_DevToolsHandlerThread";
54 const char kThumbUrlPrefix
[] = "/thumb/";
55 const char kPageUrlPrefix
[] = "/devtools/page/";
57 const char kTargetIdField
[] = "id";
58 const char kTargetParentIdField
[] = "parentId";
59 const char kTargetTypeField
[] = "type";
60 const char kTargetTitleField
[] = "title";
61 const char kTargetDescriptionField
[] = "description";
62 const char kTargetUrlField
[] = "url";
63 const char kTargetThumbnailUrlField
[] = "thumbnailUrl";
64 const char kTargetFaviconUrlField
[] = "faviconUrl";
65 const char kTargetWebSocketDebuggerUrlField
[] = "webSocketDebuggerUrl";
66 const char kTargetDevtoolsFrontendUrlField
[] = "devtoolsFrontendUrl";
68 // Maximum write buffer size of devtools http/websocket connections.
69 // TODO(rmcilroy/pfieldman): Reduce this back to 100Mb when we have
70 // added back pressure on the TraceComplete message protocol - crbug.com/456845.
71 const int32 kSendBufferSizeForDevTools
= 256 * 1024 * 1024; // 256Mb
75 // ServerWrapper -------------------------------------------------------------
76 // All methods in this class are only called on handler thread.
77 class ServerWrapper
: net::HttpServer::Delegate
{
79 ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
80 scoped_ptr
<net::ServerSocket
> socket
,
81 const base::FilePath
& frontend_dir
,
82 bool bundles_resources
);
84 int GetLocalAddress(net::IPEndPoint
* address
);
86 void AcceptWebSocket(int connection_id
,
87 const net::HttpServerRequestInfo
& request
);
88 void SendOverWebSocket(int connection_id
, const std::string
& message
);
89 void SendResponse(int connection_id
,
90 const net::HttpServerResponseInfo
& response
);
91 void Send200(int connection_id
,
92 const std::string
& data
,
93 const std::string
& mime_type
);
94 void Send404(int connection_id
);
95 void Send500(int connection_id
, const std::string
& message
);
96 void Close(int connection_id
);
98 void WriteActivePortToUserProfile(const base::FilePath
& output_directory
);
100 virtual ~ServerWrapper() {}
103 // net::HttpServer::Delegate implementation.
104 void OnConnect(int connection_id
) override
{}
105 void OnHttpRequest(int connection_id
,
106 const net::HttpServerRequestInfo
& info
) override
;
107 void OnWebSocketRequest(int connection_id
,
108 const net::HttpServerRequestInfo
& info
) override
;
109 void OnWebSocketMessage(int connection_id
,
110 const std::string
& data
) override
;
111 void OnClose(int connection_id
) override
;
113 base::WeakPtr
<DevToolsHttpHandler
> handler_
;
114 scoped_ptr
<net::HttpServer
> server_
;
115 base::FilePath frontend_dir_
;
116 bool bundles_resources_
;
119 ServerWrapper::ServerWrapper(base::WeakPtr
<DevToolsHttpHandler
> handler
,
120 scoped_ptr
<net::ServerSocket
> socket
,
121 const base::FilePath
& frontend_dir
,
122 bool bundles_resources
)
124 server_(new net::HttpServer(socket
.Pass(), this)),
125 frontend_dir_(frontend_dir
),
126 bundles_resources_(bundles_resources
) {
129 int ServerWrapper::GetLocalAddress(net::IPEndPoint
* address
) {
130 return server_
->GetLocalAddress(address
);
133 void ServerWrapper::AcceptWebSocket(int connection_id
,
134 const net::HttpServerRequestInfo
& request
) {
135 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
136 server_
->AcceptWebSocket(connection_id
, request
);
139 void ServerWrapper::SendOverWebSocket(int connection_id
,
140 const std::string
& message
) {
141 server_
->SendOverWebSocket(connection_id
, message
);
144 void ServerWrapper::SendResponse(int connection_id
,
145 const net::HttpServerResponseInfo
& response
) {
146 server_
->SendResponse(connection_id
, response
);
149 void ServerWrapper::Send200(int connection_id
,
150 const std::string
& data
,
151 const std::string
& mime_type
) {
152 server_
->Send200(connection_id
, data
, mime_type
);
155 void ServerWrapper::Send404(int connection_id
) {
156 server_
->Send404(connection_id
);
159 void ServerWrapper::Send500(int connection_id
,
160 const std::string
& message
) {
161 server_
->Send500(connection_id
, message
);
164 void ServerWrapper::Close(int connection_id
) {
165 server_
->Close(connection_id
);
168 // Thread and ServerWrapper lifetime management ------------------------------
170 void TerminateOnUI(base::Thread
* thread
,
171 ServerWrapper
* server_wrapper
,
172 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
) {
173 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
174 if (server_wrapper
) {
176 thread
->message_loop()->DeleteSoon(FROM_HERE
, server_wrapper
);
178 if (socket_factory
) {
180 thread
->message_loop()->DeleteSoon(FROM_HERE
, socket_factory
);
183 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE
, thread
);
187 void ServerStartedOnUI(
188 base::WeakPtr
<DevToolsHttpHandler
> handler
,
189 base::Thread
* thread
,
190 ServerWrapper
* server_wrapper
,
191 DevToolsHttpHandler::ServerSocketFactory
* socket_factory
,
192 scoped_ptr
<net::IPEndPoint
> ip_address
) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
194 if (handler
&& thread
&& server_wrapper
) {
195 handler
->ServerStarted(thread
, server_wrapper
, socket_factory
,
198 TerminateOnUI(thread
, server_wrapper
, socket_factory
);
202 void StartServerOnHandlerThread(
203 base::WeakPtr
<DevToolsHttpHandler
> handler
,
204 base::Thread
* thread
,
205 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
206 const base::FilePath
& output_directory
,
207 const base::FilePath
& frontend_dir
,
208 bool bundles_resources
) {
209 DCHECK_EQ(thread
->message_loop(), base::MessageLoop::current());
210 ServerWrapper
* server_wrapper
= nullptr;
211 scoped_ptr
<net::ServerSocket
> server_socket
=
212 server_socket_factory
->CreateForHttpServer();
213 scoped_ptr
<net::IPEndPoint
> ip_address(new net::IPEndPoint
);
215 server_wrapper
= new ServerWrapper(handler
, server_socket
.Pass(),
216 frontend_dir
, bundles_resources
);
217 if (!output_directory
.empty())
218 server_wrapper
->WriteActivePortToUserProfile(output_directory
);
220 if (server_wrapper
->GetLocalAddress(ip_address
.get()) != net::OK
)
224 LOG(ERROR
) << "Cannot start http server for devtools. Stop devtools.";
226 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
227 base::Bind(&ServerStartedOnUI
,
231 server_socket_factory
,
232 base::Passed(&ip_address
)));
235 void StartServerOnFile(
236 base::WeakPtr
<DevToolsHttpHandler
> handler
,
237 DevToolsHttpHandler::ServerSocketFactory
* server_socket_factory
,
238 const base::FilePath
& output_directory
,
239 const base::FilePath
& frontend_dir
,
240 bool bundles_resources
) {
241 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
242 scoped_ptr
<base::Thread
> thread(new base::Thread(kDevToolsHandlerThreadName
));
243 base::Thread::Options options
;
244 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
245 if (thread
->StartWithOptions(options
)) {
246 base::MessageLoop
* message_loop
= thread
->message_loop();
247 message_loop
->PostTask(FROM_HERE
,
248 base::Bind(&StartServerOnHandlerThread
,
250 base::Unretained(thread
.release()),
251 server_socket_factory
,
258 // DevToolsAgentHostClientImpl -----------------------------------------------
259 // An internal implementation of DevToolsAgentHostClient that delegates
260 // messages sent to a DebuggerShell instance.
261 class DevToolsAgentHostClientImpl
: public DevToolsAgentHostClient
{
263 DevToolsAgentHostClientImpl(base::MessageLoop
* message_loop
,
264 ServerWrapper
* server_wrapper
,
266 scoped_refptr
<DevToolsAgentHost
> agent_host
)
267 : message_loop_(message_loop
),
268 server_wrapper_(server_wrapper
),
269 connection_id_(connection_id
),
270 agent_host_(agent_host
) {
271 agent_host_
->AttachClient(this);
274 ~DevToolsAgentHostClientImpl() override
{
275 if (agent_host_
.get())
276 agent_host_
->DetachClient();
279 void AgentHostClosed(DevToolsAgentHost
* agent_host
,
280 bool replaced_with_another_client
) override
{
281 DCHECK(agent_host
== agent_host_
.get());
283 std::string message
= base::StringPrintf(
284 "{ \"method\": \"Inspector.detached\", "
285 "\"params\": { \"reason\": \"%s\"} }",
286 replaced_with_another_client
?
287 "replaced_with_devtools" : "target_closed");
288 DispatchProtocolMessage(agent_host
, message
);
290 agent_host_
= nullptr;
291 message_loop_
->PostTask(
293 base::Bind(&ServerWrapper::Close
,
294 base::Unretained(server_wrapper_
),
298 void DispatchProtocolMessage(DevToolsAgentHost
* agent_host
,
299 const std::string
& message
) override
{
300 DCHECK(agent_host
== agent_host_
.get());
301 message_loop_
->PostTask(
303 base::Bind(&ServerWrapper::SendOverWebSocket
,
304 base::Unretained(server_wrapper_
),
309 void OnMessage(const std::string
& message
) {
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 (EndsWith(filename
, ".html", false)) {
364 } else if (EndsWith(filename
, ".css", false)) {
366 } else if (EndsWith(filename
, ".js", false)) {
367 return "application/javascript";
368 } else if (EndsWith(filename
, ".png", false)) {
370 } else if (EndsWith(filename
, ".gif", false)) {
372 } else if (EndsWith(filename
, ".json", false)) {
373 return "application/json";
375 LOG(ERROR
) << "GetMimeType doesn't know mime type for: "
377 << " text/plain will be returned";
382 void ServerWrapper::OnHttpRequest(int connection_id
,
383 const net::HttpServerRequestInfo
& info
) {
384 server_
->SetSendBufferSize(connection_id
, kSendBufferSizeForDevTools
);
386 if (info
.path
.find("/json") == 0) {
387 BrowserThread::PostTask(
390 base::Bind(&DevToolsHttpHandler::OnJsonRequest
,
397 if (info
.path
.find(kThumbUrlPrefix
) == 0) {
398 // Thumbnail request.
399 const std::string target_id
= info
.path
.substr(strlen(kThumbUrlPrefix
));
400 BrowserThread::PostTask(
403 base::Bind(&DevToolsHttpHandler::OnThumbnailRequest
,
410 if (info
.path
.empty() || info
.path
== "/") {
411 // Discovery page request.
412 BrowserThread::PostTask(
415 base::Bind(&DevToolsHttpHandler::OnDiscoveryPageRequest
,
421 if (info
.path
.find("/devtools/") != 0) {
422 server_
->Send404(connection_id
);
426 std::string filename
= PathWithoutParams(info
.path
.substr(10));
427 std::string mime_type
= GetMimeType(filename
);
429 if (!frontend_dir_
.empty()) {
430 base::FilePath path
= frontend_dir_
.AppendASCII(filename
);
432 base::ReadFileToString(path
, &data
);
433 server_
->Send200(connection_id
, data
, mime_type
);
437 if (bundles_resources_
) {
438 BrowserThread::PostTask(
441 base::Bind(&DevToolsHttpHandler::OnFrontendResourceRequest
,
447 server_
->Send404(connection_id
);
450 void ServerWrapper::OnWebSocketRequest(
452 const net::HttpServerRequestInfo
& request
) {
453 BrowserThread::PostTask(
457 &DevToolsHttpHandler::OnWebSocketRequest
,
463 void ServerWrapper::OnWebSocketMessage(int connection_id
,
464 const std::string
& data
) {
465 BrowserThread::PostTask(
469 &DevToolsHttpHandler::OnWebSocketMessage
,
475 void ServerWrapper::OnClose(int connection_id
) {
476 BrowserThread::PostTask(
480 &DevToolsHttpHandler::OnClose
,
485 std::string
DevToolsHttpHandler::GetFrontendURLInternal(
486 const std::string id
,
487 const std::string
& host
) {
488 return base::StringPrintf(
490 frontend_url_
.c_str(),
491 frontend_url_
.find("?") == std::string::npos
? "?" : "&",
497 static bool ParseJsonPath(
498 const std::string
& path
,
499 std::string
* command
,
500 std::string
* target_id
) {
502 // Fall back to list in case of empty query.
508 if (path
.find("/") != 0) {
509 // Malformed command.
512 *command
= path
.substr(1);
514 size_t separator_pos
= command
->find("/");
515 if (separator_pos
!= std::string::npos
) {
516 *target_id
= command
->substr(separator_pos
+ 1);
517 *command
= command
->substr(0, separator_pos
);
522 void DevToolsHttpHandler::OnJsonRequest(
524 const net::HttpServerRequestInfo
& info
) {
526 std::string path
= info
.path
.substr(5);
528 // Trim fragment and query
530 size_t query_pos
= path
.find("?");
531 if (query_pos
!= std::string::npos
) {
532 query
= path
.substr(query_pos
+ 1);
533 path
= path
.substr(0, query_pos
);
536 size_t fragment_pos
= path
.find("#");
537 if (fragment_pos
!= std::string::npos
)
538 path
= path
.substr(0, fragment_pos
);
541 std::string target_id
;
542 if (!ParseJsonPath(path
, &command
, &target_id
)) {
543 SendJson(connection_id
,
546 "Malformed query: " + info
.path
);
550 if (command
== "version") {
551 base::DictionaryValue version
;
552 version
.SetString("Protocol-Version",
553 DevToolsAgentHost::GetProtocolVersion().c_str());
554 version
.SetString("WebKit-Version", content::GetWebKitVersion());
555 version
.SetString("Browser", product_name_
);
556 version
.SetString("User-Agent", user_agent_
);
557 #if defined(OS_ANDROID)
558 version
.SetString("Android-Package",
559 base::android::BuildInfo::GetInstance()->package_name());
561 SendJson(connection_id
, net::HTTP_OK
, &version
, std::string());
565 if (command
== "list") {
566 std::string host
= info
.headers
["host"];
567 DevToolsTargetDescriptor::List descriptors
=
568 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
570 std::sort(descriptors
.begin(), descriptors
.end(), TimeComparator
);
571 STLDeleteValues(&descriptor_map_
);
572 base::ListValue list_value
;
573 for (DevToolsTargetDescriptor
* descriptor
: descriptors
) {
574 descriptor_map_
[descriptor
->GetId()] = descriptor
;
575 list_value
.Append(SerializeDescriptor(*descriptor
, host
));
577 SendJson(connection_id
, net::HTTP_OK
, &list_value
, std::string());
581 if (command
== "new") {
582 GURL
url(net::UnescapeURLComponent(
583 query
, net::UnescapeRule::URL_SPECIAL_CHARS
));
585 url
= GURL(url::kAboutBlankURL
);
586 scoped_ptr
<DevToolsTargetDescriptor
> descriptor
=
587 devtools_discovery::DevToolsDiscoveryManager::GetInstance()->
590 SendJson(connection_id
,
591 net::HTTP_INTERNAL_SERVER_ERROR
,
593 "Could not create new page");
596 std::string host
= info
.headers
["host"];
597 scoped_ptr
<base::DictionaryValue
> dictionary(
598 SerializeDescriptor(*descriptor
.get(), host
));
599 SendJson(connection_id
, net::HTTP_OK
, dictionary
.get(), std::string());
600 const std::string target_id
= descriptor
->GetId();
601 descriptor_map_
[target_id
] = descriptor
.release();
605 if (command
== "activate" || command
== "close") {
606 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
608 SendJson(connection_id
,
611 "No such target id: " + target_id
);
615 if (command
== "activate") {
616 if (descriptor
->Activate()) {
617 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target activated");
619 SendJson(connection_id
,
620 net::HTTP_INTERNAL_SERVER_ERROR
,
622 "Could not activate target id: " + target_id
);
627 if (command
== "close") {
628 if (descriptor
->Close()) {
629 SendJson(connection_id
, net::HTTP_OK
, NULL
, "Target is closing");
631 SendJson(connection_id
,
632 net::HTTP_INTERNAL_SERVER_ERROR
,
634 "Could not close target id: " + target_id
);
639 SendJson(connection_id
,
642 "Unknown command: " + command
);
646 DevToolsTargetDescriptor
* DevToolsHttpHandler::GetDescriptor(
647 const std::string
& target_id
) {
648 DescriptorMap::const_iterator it
= descriptor_map_
.find(target_id
);
649 if (it
== descriptor_map_
.end())
654 void DevToolsHttpHandler::OnThumbnailRequest(
655 int connection_id
, const std::string
& target_id
) {
656 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
659 page_url
= descriptor
->GetURL();
660 std::string data
= delegate_
->GetPageThumbnailData(page_url
);
662 Send200(connection_id
, data
, "image/png");
664 Send404(connection_id
);
667 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id
) {
668 std::string response
= delegate_
->GetDiscoveryPageHTML();
669 Send200(connection_id
, response
, "text/html; charset=UTF-8");
672 void DevToolsHttpHandler::OnFrontendResourceRequest(
673 int connection_id
, const std::string
& path
) {
674 Send200(connection_id
,
675 delegate_
->GetFrontendResource(path
),
679 void DevToolsHttpHandler::OnWebSocketRequest(
681 const net::HttpServerRequestInfo
& request
) {
685 std::string browser_prefix
= "/devtools/browser";
686 size_t browser_pos
= request
.path
.find(browser_prefix
);
687 if (browser_pos
== 0) {
688 scoped_refptr
<DevToolsAgentHost
> browser_agent
=
689 DevToolsAgentHost::CreateForBrowser(
690 thread_
->message_loop_proxy(),
691 base::Bind(&ServerSocketFactory::CreateForTethering
,
692 base::Unretained(socket_factory_
)));
693 connection_to_client_
[connection_id
] = new DevToolsAgentHostClientImpl(
694 thread_
->message_loop(), server_wrapper_
, connection_id
, browser_agent
);
695 AcceptWebSocket(connection_id
, request
);
699 size_t pos
= request
.path
.find(kPageUrlPrefix
);
701 Send404(connection_id
);
705 std::string target_id
= request
.path
.substr(strlen(kPageUrlPrefix
));
706 DevToolsTargetDescriptor
* descriptor
= GetDescriptor(target_id
);
707 scoped_refptr
<DevToolsAgentHost
> agent
=
708 descriptor
? descriptor
->GetAgentHost() : nullptr;
710 Send500(connection_id
, "No such target id: " + target_id
);
714 if (agent
->IsAttached()) {
715 Send500(connection_id
,
716 "Target with given id is being inspected: " + target_id
);
720 DevToolsAgentHostClientImpl
* client_host
= new DevToolsAgentHostClientImpl(
721 thread_
->message_loop(), server_wrapper_
, connection_id
, agent
);
722 connection_to_client_
[connection_id
] = client_host
;
724 AcceptWebSocket(connection_id
, request
);
727 void DevToolsHttpHandler::OnWebSocketMessage(
729 const std::string
& data
) {
730 ConnectionToClientMap::iterator it
=
731 connection_to_client_
.find(connection_id
);
732 if (it
!= connection_to_client_
.end())
733 it
->second
->OnMessage(data
);
736 void DevToolsHttpHandler::OnClose(int connection_id
) {
737 ConnectionToClientMap::iterator it
=
738 connection_to_client_
.find(connection_id
);
739 if (it
!= connection_to_client_
.end()) {
741 connection_to_client_
.erase(connection_id
);
745 DevToolsHttpHandler::DevToolsHttpHandler(
746 scoped_ptr
<ServerSocketFactory
> server_socket_factory
,
747 const std::string
& frontend_url
,
748 DevToolsHttpHandlerDelegate
* delegate
,
749 const base::FilePath
& output_directory
,
750 const base::FilePath
& debug_frontend_dir
,
751 const std::string
& product_name
,
752 const std::string
& user_agent
)
754 frontend_url_(frontend_url
),
755 product_name_(product_name
),
756 user_agent_(user_agent
),
757 server_wrapper_(nullptr),
759 socket_factory_(nullptr),
760 weak_factory_(this) {
761 bool bundles_resources
= frontend_url_
.empty();
762 if (frontend_url_
.empty())
763 frontend_url_
= "/devtools/inspector.html";
765 BrowserThread::PostTask(
766 BrowserThread::FILE, FROM_HERE
,
767 base::Bind(&StartServerOnFile
,
768 weak_factory_
.GetWeakPtr(),
769 server_socket_factory
.release(),
775 void DevToolsHttpHandler::ServerStarted(
776 base::Thread
* thread
,
777 ServerWrapper
* server_wrapper
,
778 ServerSocketFactory
* socket_factory
,
779 scoped_ptr
<net::IPEndPoint
> ip_address
) {
781 server_wrapper_
= server_wrapper
;
782 socket_factory_
= socket_factory
;
783 server_ip_address_
.swap(ip_address
);
786 void ServerWrapper::WriteActivePortToUserProfile(
787 const base::FilePath
& output_directory
) {
788 DCHECK(!output_directory
.empty());
789 net::IPEndPoint endpoint
;
791 if ((err
= server_
->GetLocalAddress(&endpoint
)) != net::OK
) {
792 LOG(ERROR
) << "Error " << err
<< " getting local address";
796 // Write this port to a well-known file in the profile directory
797 // so Telemetry can pick it up.
798 base::FilePath path
= output_directory
.Append(kDevToolsActivePortFileName
);
799 std::string port_string
= base::IntToString(endpoint
.port());
800 if (base::WriteFile(path
, port_string
.c_str(),
801 static_cast<int>(port_string
.length())) < 0) {
802 LOG(ERROR
) << "Error writing DevTools active port to file";
806 void DevToolsHttpHandler::SendJson(int connection_id
,
807 net::HttpStatusCode status_code
,
809 const std::string
& message
) {
813 // Serialize value and message.
814 std::string json_value
;
816 base::JSONWriter::WriteWithOptions(value
,
817 base::JSONWriter::OPTIONS_PRETTY_PRINT
,
820 std::string json_message
;
821 scoped_ptr
<base::Value
> message_object(new base::StringValue(message
));
822 base::JSONWriter::Write(message_object
.get(), &json_message
);
824 net::HttpServerResponseInfo
response(status_code
);
825 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
827 thread_
->message_loop()->PostTask(
829 base::Bind(&ServerWrapper::SendResponse
,
830 base::Unretained(server_wrapper_
),
835 void DevToolsHttpHandler::Send200(int connection_id
,
836 const std::string
& data
,
837 const std::string
& mime_type
) {
840 thread_
->message_loop()->PostTask(
842 base::Bind(&ServerWrapper::Send200
,
843 base::Unretained(server_wrapper_
),
849 void DevToolsHttpHandler::Send404(int connection_id
) {
852 thread_
->message_loop()->PostTask(
854 base::Bind(&ServerWrapper::Send404
,
855 base::Unretained(server_wrapper_
),
859 void DevToolsHttpHandler::Send500(int connection_id
,
860 const std::string
& message
) {
863 thread_
->message_loop()->PostTask(
865 base::Bind(&ServerWrapper::Send500
,
866 base::Unretained(server_wrapper_
),
871 void DevToolsHttpHandler::AcceptWebSocket(
873 const net::HttpServerRequestInfo
& request
) {
876 thread_
->message_loop()->PostTask(
878 base::Bind(&ServerWrapper::AcceptWebSocket
,
879 base::Unretained(server_wrapper_
),
884 base::DictionaryValue
* DevToolsHttpHandler::SerializeDescriptor(
885 const DevToolsTargetDescriptor
& descriptor
,
886 const std::string
& host
) {
887 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
889 std::string id
= descriptor
.GetId();
890 dictionary
->SetString(kTargetIdField
, id
);
891 std::string parent_id
= descriptor
.GetParentId();
892 if (!parent_id
.empty())
893 dictionary
->SetString(kTargetParentIdField
, parent_id
);
894 dictionary
->SetString(kTargetTypeField
, descriptor
.GetType());
895 dictionary
->SetString(kTargetTitleField
,
896 net::EscapeForHTML(descriptor
.GetTitle()));
897 dictionary
->SetString(kTargetDescriptionField
, descriptor
.GetDescription());
899 GURL url
= descriptor
.GetURL();
900 dictionary
->SetString(kTargetUrlField
, url
.spec());
902 GURL favicon_url
= descriptor
.GetFaviconURL();
903 if (favicon_url
.is_valid())
904 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
906 if (!delegate_
->GetPageThumbnailData(url
).empty()) {
907 dictionary
->SetString(kTargetThumbnailUrlField
,
908 std::string(kThumbUrlPrefix
) + id
);
911 if (!descriptor
.IsAttached()) {
912 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
913 base::StringPrintf("ws://%s%s%s",
917 std::string devtools_frontend_url
= GetFrontendURLInternal(
920 dictionary
->SetString(
921 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
927 } // namespace devtools_http_handler