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 virtual ~ServerWrapper() {}
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", false)) {
364 } else if (base::EndsWith(filename
, ".css", false)) {
366 } else if (base::EndsWith(filename
, ".js", false)) {
367 return "application/javascript";
368 } else if (base::EndsWith(filename
, ".png", false)) {
370 } else if (base::EndsWith(filename
, ".gif", false)) {
372 } else if (base::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_
->task_runner(),
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(
817 *value
, base::JSONWriter::OPTIONS_PRETTY_PRINT
, &json_value
);
819 std::string json_message
;
820 base::JSONWriter::Write(base::StringValue(message
), &json_message
);
822 net::HttpServerResponseInfo
response(status_code
);
823 response
.SetBody(json_value
+ message
, "application/json; charset=UTF-8");
825 thread_
->task_runner()->PostTask(
827 base::Bind(&ServerWrapper::SendResponse
,
828 base::Unretained(server_wrapper_
), connection_id
, response
));
831 void DevToolsHttpHandler::Send200(int connection_id
,
832 const std::string
& data
,
833 const std::string
& mime_type
) {
836 thread_
->task_runner()->PostTask(
838 base::Bind(&ServerWrapper::Send200
, base::Unretained(server_wrapper_
),
839 connection_id
, data
, mime_type
));
842 void DevToolsHttpHandler::Send404(int connection_id
) {
845 thread_
->task_runner()->PostTask(
846 FROM_HERE
, base::Bind(&ServerWrapper::Send404
,
847 base::Unretained(server_wrapper_
), connection_id
));
850 void DevToolsHttpHandler::Send500(int connection_id
,
851 const std::string
& message
) {
854 thread_
->task_runner()->PostTask(
856 base::Bind(&ServerWrapper::Send500
, base::Unretained(server_wrapper_
),
857 connection_id
, message
));
860 void DevToolsHttpHandler::AcceptWebSocket(
862 const net::HttpServerRequestInfo
& request
) {
865 thread_
->task_runner()->PostTask(
867 base::Bind(&ServerWrapper::AcceptWebSocket
,
868 base::Unretained(server_wrapper_
), connection_id
, request
));
871 base::DictionaryValue
* DevToolsHttpHandler::SerializeDescriptor(
872 const DevToolsTargetDescriptor
& descriptor
,
873 const std::string
& host
) {
874 base::DictionaryValue
* dictionary
= new base::DictionaryValue
;
876 std::string id
= descriptor
.GetId();
877 dictionary
->SetString(kTargetIdField
, id
);
878 std::string parent_id
= descriptor
.GetParentId();
879 if (!parent_id
.empty())
880 dictionary
->SetString(kTargetParentIdField
, parent_id
);
881 dictionary
->SetString(kTargetTypeField
, descriptor
.GetType());
882 dictionary
->SetString(kTargetTitleField
,
883 net::EscapeForHTML(descriptor
.GetTitle()));
884 dictionary
->SetString(kTargetDescriptionField
, descriptor
.GetDescription());
886 GURL url
= descriptor
.GetURL();
887 dictionary
->SetString(kTargetUrlField
, url
.spec());
889 GURL favicon_url
= descriptor
.GetFaviconURL();
890 if (favicon_url
.is_valid())
891 dictionary
->SetString(kTargetFaviconUrlField
, favicon_url
.spec());
893 if (!delegate_
->GetPageThumbnailData(url
).empty()) {
894 dictionary
->SetString(kTargetThumbnailUrlField
,
895 std::string(kThumbUrlPrefix
) + id
);
898 if (!descriptor
.IsAttached()) {
899 dictionary
->SetString(kTargetWebSocketDebuggerUrlField
,
900 base::StringPrintf("ws://%s%s%s",
904 std::string devtools_frontend_url
= GetFrontendURLInternal(
907 dictionary
->SetString(
908 kTargetDevtoolsFrontendUrlField
, devtools_frontend_url
);
914 } // namespace devtools_http_handler