Change next_proto member type.
[chromium-blink-merge.git] / content / browser / devtools / devtools_http_handler_impl.cc
blob5bd8887562eeadfc3dcd909d3610c4e9c29e1cbe
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.
5 #include <algorithm>
6 #include <utility>
8 #include "base/bind.h"
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/threading/thread.h"
17 #include "base/values.h"
18 #include "content/browser/devtools/devtools_manager.h"
19 #include "content/browser/devtools/protocol/devtools_protocol_handler.h"
20 #include "content/browser/devtools/protocol/system_info_handler.h"
21 #include "content/browser/devtools/protocol/tethering_handler.h"
22 #include "content/browser/devtools/protocol/tracing_handler.h"
23 #include "content/common/devtools_messages.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/devtools_agent_host.h"
26 #include "content/public/browser/devtools_http_handler.h"
27 #include "content/public/browser/devtools_http_handler_delegate.h"
28 #include "content/public/browser/devtools_target.h"
29 #include "content/public/common/content_client.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/common/user_agent.h"
32 #include "grit/devtools_resources_map.h"
33 #include "net/base/escape.h"
34 #include "net/base/io_buffer.h"
35 #include "net/base/ip_endpoint.h"
36 #include "net/base/net_errors.h"
37 #include "net/server/http_server.h"
38 #include "net/server/http_server_request_info.h"
39 #include "net/server/http_server_response_info.h"
40 #include "net/socket/server_socket.h"
42 #if defined(OS_ANDROID)
43 #include "base/android/build_info.h"
44 #endif
46 namespace content {
48 namespace {
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 connectinos.
70 const int32 kSendBufferSizeForDevTools = 100 * 1024 * 1024; // 100Mb
72 class BrowserTarget;
73 class DevToolsAgentHostClientImpl;
74 class ServerWrapper;
76 // DevToolsHttpHandlerImpl ---------------------------------------------------
78 class DevToolsHttpHandlerImpl : public DevToolsHttpHandler {
79 public:
80 DevToolsHttpHandlerImpl(scoped_ptr<ServerSocketFactory> server_socket_factory,
81 const std::string& frontend_url,
82 DevToolsHttpHandlerDelegate* delegate,
83 const base::FilePath& output_directory);
84 ~DevToolsHttpHandlerImpl() override;
86 void OnJsonRequest(int connection_id,
87 const net::HttpServerRequestInfo& info);
88 void OnThumbnailRequest(int connection_id, const std::string& target_id);
89 void OnDiscoveryPageRequest(int connection_id);
91 void OnWebSocketRequest(int connection_id,
92 const net::HttpServerRequestInfo& info);
93 void OnWebSocketMessage(int connection_id, const std::string& data);
94 void OnClose(int connection_id);
96 void ServerStarted(base::Thread* thread,
97 ServerWrapper* server_wrapper,
98 scoped_ptr<net::IPEndPoint> ip_address);
100 private:
101 // DevToolsHttpHandler implementation.
102 GURL GetFrontendURL(const std::string& path) override;
104 static void OnTargetListReceivedWeak(
105 base::WeakPtr<DevToolsHttpHandlerImpl> handler,
106 int connection_id,
107 const std::string& host,
108 const DevToolsManagerDelegate::TargetList& targets);
109 void OnTargetListReceived(
110 int connection_id,
111 const std::string& host,
112 const DevToolsManagerDelegate::TargetList& targets);
114 DevToolsTarget* GetTarget(const std::string& id);
116 void SendJson(int connection_id,
117 net::HttpStatusCode status_code,
118 base::Value* value,
119 const std::string& message);
120 void Send200(int connection_id,
121 const std::string& data,
122 const std::string& mime_type);
123 void Send404(int connection_id);
124 void Send500(int connection_id,
125 const std::string& message);
126 void AcceptWebSocket(int connection_id,
127 const net::HttpServerRequestInfo& request);
129 // Returns the front end url without the host at the beginning.
130 std::string GetFrontendURLInternal(const std::string target_id,
131 const std::string& host);
133 base::DictionaryValue* SerializeTarget(const DevToolsTarget& target,
134 const std::string& host);
136 // The thread used by the devtools handler to run server socket.
137 base::Thread* thread_;
139 std::string frontend_url_;
140 ServerWrapper* server_wrapper_;
141 scoped_ptr<net::IPEndPoint> server_ip_address_;
142 typedef std::map<int, DevToolsAgentHostClientImpl*> ConnectionToClientMap;
143 ConnectionToClientMap connection_to_client_;
144 const scoped_ptr<DevToolsHttpHandlerDelegate> delegate_;
145 typedef std::map<std::string, DevToolsTarget*> TargetMap;
146 TargetMap target_map_;
147 typedef std::map<int, BrowserTarget*> BrowserTargets;
148 BrowserTargets browser_targets_;
149 base::WeakPtrFactory<DevToolsHttpHandlerImpl> weak_factory_;
151 DISALLOW_COPY_AND_ASSIGN(DevToolsHttpHandlerImpl);
154 // ServerWrapper -------------------------------------------------------------
155 // All methods in this class are only called on handler thread.
156 class ServerWrapper : net::HttpServer::Delegate {
157 public:
158 ServerWrapper(base::WeakPtr<DevToolsHttpHandlerImpl> handler,
159 scoped_ptr<net::ServerSocket> socket,
160 const base::FilePath& frontend_dir,
161 bool bundles_resources);
163 int GetLocalAddress(net::IPEndPoint* address);
165 void AcceptWebSocket(int connection_id,
166 const net::HttpServerRequestInfo& request);
167 void SendOverWebSocket(int connection_id, const std::string& message);
168 void SendResponse(int connection_id,
169 const net::HttpServerResponseInfo& response);
170 void Send200(int connection_id,
171 const std::string& data,
172 const std::string& mime_type);
173 void Send404(int connection_id);
174 void Send500(int connection_id, const std::string& message);
175 void Close(int connection_id);
177 void WriteActivePortToUserProfile(const base::FilePath& output_directory);
179 virtual ~ServerWrapper() {}
181 private:
182 // net::HttpServer::Delegate implementation.
183 void OnConnect(int connection_id) override {}
184 void OnHttpRequest(int connection_id,
185 const net::HttpServerRequestInfo& info) override;
186 void OnWebSocketRequest(int connection_id,
187 const net::HttpServerRequestInfo& info) override;
188 void OnWebSocketMessage(int connection_id,
189 const std::string& data) override;
190 void OnClose(int connection_id) override;
192 base::WeakPtr<DevToolsHttpHandlerImpl> handler_;
193 scoped_ptr<net::HttpServer> server_;
194 base::FilePath frontend_dir_;
195 bool bundles_resources_;
198 ServerWrapper::ServerWrapper(base::WeakPtr<DevToolsHttpHandlerImpl> handler,
199 scoped_ptr<net::ServerSocket> socket,
200 const base::FilePath& frontend_dir,
201 bool bundles_resources)
202 : handler_(handler),
203 server_(new net::HttpServer(socket.Pass(), this)),
204 frontend_dir_(frontend_dir),
205 bundles_resources_(bundles_resources) {
208 int ServerWrapper::GetLocalAddress(net::IPEndPoint* address) {
209 return server_->GetLocalAddress(address);
212 void ServerWrapper::AcceptWebSocket(int connection_id,
213 const net::HttpServerRequestInfo& request) {
214 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
215 server_->AcceptWebSocket(connection_id, request);
218 void ServerWrapper::SendOverWebSocket(int connection_id,
219 const std::string& message) {
220 server_->SendOverWebSocket(connection_id, message);
223 void ServerWrapper::SendResponse(int connection_id,
224 const net::HttpServerResponseInfo& response) {
225 server_->SendResponse(connection_id, response);
228 void ServerWrapper::Send200(int connection_id,
229 const std::string& data,
230 const std::string& mime_type) {
231 server_->Send200(connection_id, data, mime_type);
234 void ServerWrapper::Send404(int connection_id) {
235 server_->Send404(connection_id);
238 void ServerWrapper::Send500(int connection_id,
239 const std::string& message) {
240 server_->Send500(connection_id, message);
243 void ServerWrapper::Close(int connection_id) {
244 server_->Close(connection_id);
247 // Thread and ServerWrapper lifetime management ------------------------------
249 void TerminateOnUI(base::Thread* thread, ServerWrapper* server_wrapper) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251 if (server_wrapper) {
252 DCHECK(thread);
253 thread->message_loop()->DeleteSoon(FROM_HERE, server_wrapper);
255 if (thread) {
256 BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, thread);
260 void ServerStartedOnUI(
261 base::WeakPtr<DevToolsHttpHandlerImpl> handler,
262 base::Thread* thread,
263 ServerWrapper* server_wrapper,
264 scoped_ptr<net::IPEndPoint> ip_address) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266 if (handler && thread && server_wrapper) {
267 handler->ServerStarted(thread, server_wrapper, ip_address.Pass());
268 } else {
269 TerminateOnUI(thread, server_wrapper);
273 void StartServerOnHandlerThread(
274 base::WeakPtr<DevToolsHttpHandlerImpl> handler,
275 base::Thread* thread,
276 scoped_ptr<DevToolsHttpHandler::ServerSocketFactory> server_socket_factory,
277 const base::FilePath& output_directory,
278 const base::FilePath& frontend_dir,
279 bool bundles_resources) {
280 DCHECK_EQ(thread->message_loop(), base::MessageLoop::current());
281 ServerWrapper* server_wrapper = nullptr;
282 scoped_ptr<net::ServerSocket> server_socket =
283 server_socket_factory->CreateAndListen();
284 scoped_ptr<net::IPEndPoint> ip_address(new net::IPEndPoint);
285 if (server_socket) {
286 server_wrapper = new ServerWrapper(handler, server_socket.Pass(),
287 frontend_dir, bundles_resources);
288 if (!output_directory.empty())
289 server_wrapper->WriteActivePortToUserProfile(output_directory);
291 if (server_wrapper->GetLocalAddress(ip_address.get()) != net::OK)
292 ip_address.reset();
293 } else {
294 ip_address.reset();
295 LOG(ERROR) << "Cannot start http server for devtools. Stop devtools.";
297 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
298 base::Bind(&ServerStartedOnUI,
299 handler, thread, server_wrapper, base::Passed(&ip_address)));
302 void StartServerOnFile(
303 base::WeakPtr<DevToolsHttpHandlerImpl> handler,
304 scoped_ptr<DevToolsHttpHandler::ServerSocketFactory> server_socket_factory,
305 const base::FilePath& output_directory,
306 const base::FilePath& frontend_dir,
307 bool bundles_resources) {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
309 scoped_ptr<base::Thread> thread(new base::Thread(kDevToolsHandlerThreadName));
310 base::Thread::Options options;
311 options.message_loop_type = base::MessageLoop::TYPE_IO;
312 if (thread->StartWithOptions(options)) {
313 base::MessageLoop* message_loop = thread->message_loop();
314 message_loop->PostTask(FROM_HERE,
315 base::Bind(&StartServerOnHandlerThread,
316 handler,
317 base::Unretained(thread.release()),
318 base::Passed(&server_socket_factory),
319 output_directory,
320 frontend_dir,
321 bundles_resources));
325 // DevToolsAgentHostClientImpl -----------------------------------------------
326 // An internal implementation of DevToolsAgentHostClient that delegates
327 // messages sent to a DebuggerShell instance.
328 class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
329 public:
330 DevToolsAgentHostClientImpl(base::MessageLoop* message_loop,
331 ServerWrapper* server_wrapper,
332 int connection_id,
333 DevToolsAgentHost* agent_host)
334 : message_loop_(message_loop),
335 server_wrapper_(server_wrapper),
336 connection_id_(connection_id),
337 agent_host_(agent_host) {
338 agent_host_->AttachClient(this);
341 ~DevToolsAgentHostClientImpl() override {
342 if (agent_host_.get())
343 agent_host_->DetachClient();
346 void AgentHostClosed(DevToolsAgentHost* agent_host,
347 bool replaced_with_another_client) override {
348 DCHECK(agent_host == agent_host_.get());
350 base::Callback<void(const std::string&)> raw_message_callback(
351 base::Bind(&DevToolsAgentHostClientImpl::DispatchProtocolMessage,
352 base::Unretained(this), base::Unretained(agent_host)));
353 devtools::inspector::Client inspector(raw_message_callback);
354 inspector.Detached(devtools::inspector::DetachedParams::Create()
355 ->set_reason(replaced_with_another_client ?
356 "replaced_with_devtools" : "target_closed"));
358 agent_host_ = nullptr;
359 message_loop_->PostTask(
360 FROM_HERE,
361 base::Bind(&ServerWrapper::Close,
362 base::Unretained(server_wrapper_),
363 connection_id_));
366 void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
367 const std::string& message) override {
368 DCHECK(agent_host == agent_host_.get());
369 message_loop_->PostTask(
370 FROM_HERE,
371 base::Bind(&ServerWrapper::SendOverWebSocket,
372 base::Unretained(server_wrapper_),
373 connection_id_,
374 message));
377 void OnMessage(const std::string& message) {
378 if (agent_host_.get())
379 agent_host_->DispatchProtocolMessage(message);
382 private:
383 base::MessageLoop* const message_loop_;
384 ServerWrapper* const server_wrapper_;
385 const int connection_id_;
386 scoped_refptr<DevToolsAgentHost> agent_host_;
389 static bool TimeComparator(const DevToolsTarget* target1,
390 const DevToolsTarget* target2) {
391 return target1->GetLastActivityTime() > target2->GetLastActivityTime();
394 // BrowserTarget -------------------------------------------------------------
396 class BrowserTarget {
397 public:
398 BrowserTarget(base::MessageLoop* message_loop,
399 ServerWrapper* server_wrapper,
400 DevToolsHttpHandlerDelegate* delegate,
401 int connection_id)
402 : message_loop_(message_loop),
403 server_wrapper_(server_wrapper),
404 connection_id_(connection_id),
405 system_info_handler_(new devtools::system_info::SystemInfoHandler()),
406 tethering_handler_(new devtools::tethering::TetheringHandler(
407 delegate, message_loop->message_loop_proxy())),
408 tracing_handler_(new devtools::tracing::TracingHandler(
409 devtools::tracing::TracingHandler::Browser)),
410 protocol_handler_(new DevToolsProtocolHandler(
411 true /* handle_generic_errors */,
412 base::Bind(&BrowserTarget::Respond, base::Unretained(this)))) {
413 DevToolsProtocolDispatcher* dispatcher = protocol_handler_->dispatcher();
414 dispatcher->SetSystemInfoHandler(system_info_handler_.get());
415 dispatcher->SetTetheringHandler(tethering_handler_.get());
416 dispatcher->SetTracingHandler(tracing_handler_.get());
419 void HandleMessage(const std::string& message) {
420 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
421 std::string error_response;
422 scoped_ptr<base::DictionaryValue> command =
423 protocol_handler_->ParseCommand(message);
424 if (command) {
425 bool result = protocol_handler_->HandleCommand(command.Pass());
426 DCHECK(result);
430 void Respond(const std::string& message) {
431 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432 message_loop_->PostTask(
433 FROM_HERE,
434 base::Bind(&ServerWrapper::SendOverWebSocket,
435 base::Unretained(server_wrapper_),
436 connection_id_,
437 message));
440 private:
441 base::MessageLoop* const message_loop_;
442 ServerWrapper* const server_wrapper_;
443 const int connection_id_;
444 scoped_ptr<devtools::system_info::SystemInfoHandler> system_info_handler_;
445 scoped_ptr<devtools::tethering::TetheringHandler> tethering_handler_;
446 scoped_ptr<devtools::tracing::TracingHandler> tracing_handler_;
447 scoped_ptr<DevToolsProtocolHandler> protocol_handler_;
450 } // namespace
452 // DevToolsHttpHandler -------------------------------------------------------
454 // static
455 bool DevToolsHttpHandler::IsSupportedProtocolVersion(
456 const std::string& version) {
457 return devtools::IsSupportedProtocolVersion(version);
460 // static
461 int DevToolsHttpHandler::GetFrontendResourceId(const std::string& name) {
462 for (size_t i = 0; i < kDevtoolsResourcesSize; ++i) {
463 if (name == kDevtoolsResources[i].name)
464 return kDevtoolsResources[i].value;
466 return -1;
469 // static
470 DevToolsHttpHandler* DevToolsHttpHandler::Start(
471 scoped_ptr<ServerSocketFactory> server_socket_factory,
472 const std::string& frontend_url,
473 DevToolsHttpHandlerDelegate* delegate,
474 const base::FilePath& active_port_output_directory) {
475 DevToolsHttpHandlerImpl* http_handler =
476 new DevToolsHttpHandlerImpl(server_socket_factory.Pass(),
477 frontend_url,
478 delegate,
479 active_port_output_directory);
480 return http_handler;
483 // DevToolsHttpHandler::ServerSocketFactory ----------------------------------
485 DevToolsHttpHandler::ServerSocketFactory::ServerSocketFactory(
486 const std::string& address,
487 uint16 port,
488 int backlog)
489 : address_(address),
490 port_(port),
491 backlog_(backlog) {
494 DevToolsHttpHandler::ServerSocketFactory::~ServerSocketFactory() {
497 scoped_ptr<net::ServerSocket>
498 DevToolsHttpHandler::ServerSocketFactory::CreateAndListen() const {
499 scoped_ptr<net::ServerSocket> socket = Create();
500 if (socket &&
501 socket->ListenWithAddressAndPort(address_, port_, backlog_) == net::OK) {
502 return socket.Pass();
504 return scoped_ptr<net::ServerSocket>();
507 // DevToolsHttpHandlerImpl ---------------------------------------------------
509 DevToolsHttpHandlerImpl::~DevToolsHttpHandlerImpl() {
510 TerminateOnUI(thread_, server_wrapper_);
511 STLDeleteValues(&target_map_);
512 STLDeleteValues(&browser_targets_);
513 STLDeleteValues(&connection_to_client_);
516 GURL DevToolsHttpHandlerImpl::GetFrontendURL(const std::string& path) {
517 if (!server_ip_address_)
518 return GURL();
519 return GURL(std::string("http://") + server_ip_address_->ToString() +
520 (path.empty() ? frontend_url_ : path));
523 static std::string PathWithoutParams(const std::string& path) {
524 size_t query_position = path.find("?");
525 if (query_position != std::string::npos)
526 return path.substr(0, query_position);
527 return path;
530 static std::string GetMimeType(const std::string& filename) {
531 if (EndsWith(filename, ".html", false)) {
532 return "text/html";
533 } else if (EndsWith(filename, ".css", false)) {
534 return "text/css";
535 } else if (EndsWith(filename, ".js", false)) {
536 return "application/javascript";
537 } else if (EndsWith(filename, ".png", false)) {
538 return "image/png";
539 } else if (EndsWith(filename, ".gif", false)) {
540 return "image/gif";
541 } else if (EndsWith(filename, ".json", false)) {
542 return "application/json";
544 LOG(ERROR) << "GetMimeType doesn't know mime type for: "
545 << filename
546 << " text/plain will be returned";
547 NOTREACHED();
548 return "text/plain";
551 void ServerWrapper::OnHttpRequest(int connection_id,
552 const net::HttpServerRequestInfo& info) {
553 server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
555 if (info.path.find("/json") == 0) {
556 BrowserThread::PostTask(
557 BrowserThread::UI,
558 FROM_HERE,
559 base::Bind(&DevToolsHttpHandlerImpl::OnJsonRequest,
560 handler_,
561 connection_id,
562 info));
563 return;
566 if (info.path.find(kThumbUrlPrefix) == 0) {
567 // Thumbnail request.
568 const std::string target_id = info.path.substr(strlen(kThumbUrlPrefix));
569 BrowserThread::PostTask(
570 BrowserThread::UI,
571 FROM_HERE,
572 base::Bind(&DevToolsHttpHandlerImpl::OnThumbnailRequest,
573 handler_,
574 connection_id,
575 target_id));
576 return;
579 if (info.path == "" || info.path == "/") {
580 // Discovery page request.
581 BrowserThread::PostTask(
582 BrowserThread::UI,
583 FROM_HERE,
584 base::Bind(&DevToolsHttpHandlerImpl::OnDiscoveryPageRequest,
585 handler_,
586 connection_id));
587 return;
590 if (info.path.find("/devtools/") != 0) {
591 server_->Send404(connection_id);
592 return;
595 std::string filename = PathWithoutParams(info.path.substr(10));
596 std::string mime_type = GetMimeType(filename);
598 if (!frontend_dir_.empty()) {
599 base::FilePath path = frontend_dir_.AppendASCII(filename);
600 std::string data;
601 base::ReadFileToString(path, &data);
602 server_->Send200(connection_id, data, mime_type);
603 return;
605 if (bundles_resources_) {
606 int resource_id = DevToolsHttpHandler::GetFrontendResourceId(filename);
607 if (resource_id != -1) {
608 base::StringPiece data = GetContentClient()->GetDataResource(
609 resource_id, ui::SCALE_FACTOR_NONE);
610 server_->Send200(connection_id, data.as_string(), mime_type);
611 return;
614 server_->Send404(connection_id);
617 void ServerWrapper::OnWebSocketRequest(
618 int connection_id,
619 const net::HttpServerRequestInfo& request) {
620 BrowserThread::PostTask(
621 BrowserThread::UI,
622 FROM_HERE,
623 base::Bind(
624 &DevToolsHttpHandlerImpl::OnWebSocketRequest,
625 handler_,
626 connection_id,
627 request));
630 void ServerWrapper::OnWebSocketMessage(int connection_id,
631 const std::string& data) {
632 BrowserThread::PostTask(
633 BrowserThread::UI,
634 FROM_HERE,
635 base::Bind(
636 &DevToolsHttpHandlerImpl::OnWebSocketMessage,
637 handler_,
638 connection_id,
639 data));
642 void ServerWrapper::OnClose(int connection_id) {
643 BrowserThread::PostTask(
644 BrowserThread::UI,
645 FROM_HERE,
646 base::Bind(
647 &DevToolsHttpHandlerImpl::OnClose,
648 handler_,
649 connection_id));
652 std::string DevToolsHttpHandlerImpl::GetFrontendURLInternal(
653 const std::string id,
654 const std::string& host) {
655 return base::StringPrintf(
656 "%s%sws=%s%s%s",
657 frontend_url_.c_str(),
658 frontend_url_.find("?") == std::string::npos ? "?" : "&",
659 host.c_str(),
660 kPageUrlPrefix,
661 id.c_str());
664 static bool ParseJsonPath(
665 const std::string& path,
666 std::string* command,
667 std::string* target_id) {
669 // Fall back to list in case of empty query.
670 if (path.empty()) {
671 *command = "list";
672 return true;
675 if (path.find("/") != 0) {
676 // Malformed command.
677 return false;
679 *command = path.substr(1);
681 size_t separator_pos = command->find("/");
682 if (separator_pos != std::string::npos) {
683 *target_id = command->substr(separator_pos + 1);
684 *command = command->substr(0, separator_pos);
686 return true;
689 void DevToolsHttpHandlerImpl::OnJsonRequest(
690 int connection_id,
691 const net::HttpServerRequestInfo& info) {
692 // Trim /json
693 std::string path = info.path.substr(5);
695 // Trim fragment and query
696 std::string query;
697 size_t query_pos = path.find("?");
698 if (query_pos != std::string::npos) {
699 query = path.substr(query_pos + 1);
700 path = path.substr(0, query_pos);
703 size_t fragment_pos = path.find("#");
704 if (fragment_pos != std::string::npos)
705 path = path.substr(0, fragment_pos);
707 std::string command;
708 std::string target_id;
709 if (!ParseJsonPath(path, &command, &target_id)) {
710 SendJson(connection_id,
711 net::HTTP_NOT_FOUND,
712 NULL,
713 "Malformed query: " + info.path);
714 return;
717 if (command == "version") {
718 base::DictionaryValue version;
719 version.SetString("Protocol-Version", devtools::kProtocolVersion);
720 version.SetString("WebKit-Version", GetWebKitVersion());
721 version.SetString("Browser", GetContentClient()->GetProduct());
722 version.SetString("User-Agent", GetContentClient()->GetUserAgent());
723 #if defined(OS_ANDROID)
724 version.SetString("Android-Package",
725 base::android::BuildInfo::GetInstance()->package_name());
726 #endif
727 SendJson(connection_id, net::HTTP_OK, &version, std::string());
728 return;
731 if (command == "list") {
732 std::string host = info.headers["host"];
733 DevToolsManagerDelegate* manager_delegate =
734 DevToolsManager::GetInstance()->delegate();
735 if (manager_delegate) {
736 manager_delegate->EnumerateTargets(
737 base::Bind(&DevToolsHttpHandlerImpl::OnTargetListReceivedWeak,
738 weak_factory_.GetWeakPtr(), connection_id, host));
739 } else {
740 DevToolsManagerDelegate::TargetList empty_list;
741 OnTargetListReceived(connection_id, host, empty_list);
743 return;
746 if (command == "new") {
747 GURL url(net::UnescapeURLComponent(
748 query, net::UnescapeRule::URL_SPECIAL_CHARS));
749 if (!url.is_valid())
750 url = GURL(url::kAboutBlankURL);
751 scoped_ptr<DevToolsTarget> target;
752 DevToolsManagerDelegate* manager_delegate =
753 DevToolsManager::GetInstance()->delegate();
754 if (manager_delegate)
755 target = manager_delegate->CreateNewTarget(url);
756 if (!target) {
757 SendJson(connection_id,
758 net::HTTP_INTERNAL_SERVER_ERROR,
759 NULL,
760 "Could not create new page");
761 return;
763 std::string host = info.headers["host"];
764 scoped_ptr<base::DictionaryValue> dictionary(
765 SerializeTarget(*target.get(), host));
766 SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
767 const std::string target_id = target->GetId();
768 target_map_[target_id] = target.release();
769 return;
772 if (command == "activate" || command == "close") {
773 DevToolsTarget* target = GetTarget(target_id);
774 if (!target) {
775 SendJson(connection_id,
776 net::HTTP_NOT_FOUND,
777 NULL,
778 "No such target id: " + target_id);
779 return;
782 if (command == "activate") {
783 if (target->Activate()) {
784 SendJson(connection_id, net::HTTP_OK, NULL, "Target activated");
785 } else {
786 SendJson(connection_id,
787 net::HTTP_INTERNAL_SERVER_ERROR,
788 NULL,
789 "Could not activate target id: " + target_id);
791 return;
794 if (command == "close") {
795 if (target->Close()) {
796 SendJson(connection_id, net::HTTP_OK, NULL, "Target is closing");
797 } else {
798 SendJson(connection_id,
799 net::HTTP_INTERNAL_SERVER_ERROR,
800 NULL,
801 "Could not close target id: " + target_id);
803 return;
806 SendJson(connection_id,
807 net::HTTP_NOT_FOUND,
808 NULL,
809 "Unknown command: " + command);
810 return;
813 // static
814 void DevToolsHttpHandlerImpl::OnTargetListReceivedWeak(
815 base::WeakPtr<DevToolsHttpHandlerImpl> handler,
816 int connection_id,
817 const std::string& host,
818 const DevToolsManagerDelegate::TargetList& targets) {
819 if (handler) {
820 handler->OnTargetListReceived(connection_id, host, targets);
821 } else {
822 STLDeleteContainerPointers(targets.begin(), targets.end());
826 void DevToolsHttpHandlerImpl::OnTargetListReceived(
827 int connection_id,
828 const std::string& host,
829 const DevToolsManagerDelegate::TargetList& targets) {
830 DevToolsManagerDelegate::TargetList sorted_targets = targets;
831 std::sort(sorted_targets.begin(), sorted_targets.end(), TimeComparator);
833 STLDeleteValues(&target_map_);
834 base::ListValue list_value;
835 for (DevToolsManagerDelegate::TargetList::const_iterator it =
836 sorted_targets.begin(); it != sorted_targets.end(); ++it) {
837 DevToolsTarget* target = *it;
838 target_map_[target->GetId()] = target;
839 list_value.Append(SerializeTarget(*target, host));
841 SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
844 DevToolsTarget* DevToolsHttpHandlerImpl::GetTarget(const std::string& id) {
845 TargetMap::const_iterator it = target_map_.find(id);
846 if (it == target_map_.end())
847 return NULL;
848 return it->second;
851 void DevToolsHttpHandlerImpl::OnThumbnailRequest(
852 int connection_id, const std::string& target_id) {
853 DevToolsTarget* target = GetTarget(target_id);
854 GURL page_url;
855 if (target)
856 page_url = target->GetURL();
857 DevToolsManagerDelegate* manager_delegate =
858 DevToolsManager::GetInstance()->delegate();
859 std::string data =
860 manager_delegate ? manager_delegate->GetPageThumbnailData(page_url) : "";
861 if (!data.empty())
862 Send200(connection_id, data, "image/png");
863 else
864 Send404(connection_id);
867 void DevToolsHttpHandlerImpl::OnDiscoveryPageRequest(int connection_id) {
868 std::string response = delegate_->GetDiscoveryPageHTML();
869 Send200(connection_id, response, "text/html; charset=UTF-8");
872 void DevToolsHttpHandlerImpl::OnWebSocketRequest(
873 int connection_id,
874 const net::HttpServerRequestInfo& request) {
875 if (!thread_)
876 return;
878 std::string browser_prefix = "/devtools/browser";
879 size_t browser_pos = request.path.find(browser_prefix);
880 if (browser_pos == 0) {
881 browser_targets_[connection_id] = new BrowserTarget(thread_->message_loop(),
882 server_wrapper_,
883 delegate_.get(),
884 connection_id);
885 AcceptWebSocket(connection_id, request);
886 return;
889 size_t pos = request.path.find(kPageUrlPrefix);
890 if (pos != 0) {
891 Send404(connection_id);
892 return;
895 std::string page_id = request.path.substr(strlen(kPageUrlPrefix));
896 DevToolsTarget* target = GetTarget(page_id);
897 scoped_refptr<DevToolsAgentHost> agent =
898 target ? target->GetAgentHost() : NULL;
899 if (!agent.get()) {
900 Send500(connection_id, "No such target id: " + page_id);
901 return;
904 if (agent->IsAttached()) {
905 Send500(connection_id,
906 "Target with given id is being inspected: " + page_id);
907 return;
910 DevToolsAgentHostClientImpl* client_host = new DevToolsAgentHostClientImpl(
911 thread_->message_loop(), server_wrapper_, connection_id, agent.get());
912 connection_to_client_[connection_id] = client_host;
914 AcceptWebSocket(connection_id, request);
917 void DevToolsHttpHandlerImpl::OnWebSocketMessage(
918 int connection_id,
919 const std::string& data) {
920 BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
921 if (browser_it != browser_targets_.end()) {
922 browser_it->second->HandleMessage(data);
923 return;
926 ConnectionToClientMap::iterator it =
927 connection_to_client_.find(connection_id);
928 if (it != connection_to_client_.end())
929 it->second->OnMessage(data);
932 void DevToolsHttpHandlerImpl::OnClose(int connection_id) {
933 BrowserTargets::iterator browser_it = browser_targets_.find(connection_id);
934 if (browser_it != browser_targets_.end()) {
935 delete browser_it->second;
936 browser_targets_.erase(connection_id);
937 return;
940 ConnectionToClientMap::iterator it =
941 connection_to_client_.find(connection_id);
942 if (it != connection_to_client_.end()) {
943 delete it->second;
944 connection_to_client_.erase(connection_id);
948 DevToolsHttpHandlerImpl::DevToolsHttpHandlerImpl(
949 scoped_ptr<ServerSocketFactory> server_socket_factory,
950 const std::string& frontend_url,
951 DevToolsHttpHandlerDelegate* delegate,
952 const base::FilePath& output_directory)
953 : thread_(nullptr),
954 frontend_url_(frontend_url),
955 server_wrapper_(nullptr),
956 delegate_(delegate),
957 weak_factory_(this) {
958 if (frontend_url_.empty())
959 frontend_url_ = "/devtools/inspector.html";
961 BrowserThread::PostTask(
962 BrowserThread::FILE, FROM_HERE,
963 base::Bind(&StartServerOnFile,
964 weak_factory_.GetWeakPtr(),
965 base::Passed(&server_socket_factory),
966 output_directory,
967 delegate_->GetDebugFrontendDir(),
968 delegate_->BundlesFrontendResources()));
971 void DevToolsHttpHandlerImpl::ServerStarted(
972 base::Thread* thread,
973 ServerWrapper* server_wrapper,
974 scoped_ptr<net::IPEndPoint> ip_address) {
975 thread_ = thread;
976 server_wrapper_ = server_wrapper;
977 server_ip_address_.swap(ip_address);
980 void ServerWrapper::WriteActivePortToUserProfile(
981 const base::FilePath& output_directory) {
982 DCHECK(!output_directory.empty());
983 net::IPEndPoint endpoint;
984 int err;
985 if ((err = server_->GetLocalAddress(&endpoint)) != net::OK) {
986 LOG(ERROR) << "Error " << err << " getting local address";
987 return;
990 // Write this port to a well-known file in the profile directory
991 // so Telemetry can pick it up.
992 base::FilePath path = output_directory.Append(kDevToolsActivePortFileName);
993 std::string port_string = base::IntToString(endpoint.port());
994 if (base::WriteFile(path, port_string.c_str(), port_string.length()) < 0) {
995 LOG(ERROR) << "Error writing DevTools active port to file";
999 void DevToolsHttpHandlerImpl::SendJson(int connection_id,
1000 net::HttpStatusCode status_code,
1001 base::Value* value,
1002 const std::string& message) {
1003 if (!thread_)
1004 return;
1006 // Serialize value and message.
1007 std::string json_value;
1008 if (value) {
1009 base::JSONWriter::WriteWithOptions(value,
1010 base::JSONWriter::OPTIONS_PRETTY_PRINT,
1011 &json_value);
1013 std::string json_message;
1014 scoped_ptr<base::Value> message_object(new base::StringValue(message));
1015 base::JSONWriter::Write(message_object.get(), &json_message);
1017 net::HttpServerResponseInfo response(status_code);
1018 response.SetBody(json_value + message, "application/json; charset=UTF-8");
1020 thread_->message_loop()->PostTask(
1021 FROM_HERE,
1022 base::Bind(&ServerWrapper::SendResponse,
1023 base::Unretained(server_wrapper_),
1024 connection_id,
1025 response));
1028 void DevToolsHttpHandlerImpl::Send200(int connection_id,
1029 const std::string& data,
1030 const std::string& mime_type) {
1031 if (!thread_)
1032 return;
1033 thread_->message_loop()->PostTask(
1034 FROM_HERE,
1035 base::Bind(&ServerWrapper::Send200,
1036 base::Unretained(server_wrapper_),
1037 connection_id,
1038 data,
1039 mime_type));
1042 void DevToolsHttpHandlerImpl::Send404(int connection_id) {
1043 if (!thread_)
1044 return;
1045 thread_->message_loop()->PostTask(
1046 FROM_HERE,
1047 base::Bind(&ServerWrapper::Send404,
1048 base::Unretained(server_wrapper_),
1049 connection_id));
1052 void DevToolsHttpHandlerImpl::Send500(int connection_id,
1053 const std::string& message) {
1054 if (!thread_)
1055 return;
1056 thread_->message_loop()->PostTask(
1057 FROM_HERE,
1058 base::Bind(&ServerWrapper::Send500,
1059 base::Unretained(server_wrapper_),
1060 connection_id,
1061 message));
1064 void DevToolsHttpHandlerImpl::AcceptWebSocket(
1065 int connection_id,
1066 const net::HttpServerRequestInfo& request) {
1067 if (!thread_)
1068 return;
1069 thread_->message_loop()->PostTask(
1070 FROM_HERE,
1071 base::Bind(&ServerWrapper::AcceptWebSocket,
1072 base::Unretained(server_wrapper_),
1073 connection_id,
1074 request));
1077 base::DictionaryValue* DevToolsHttpHandlerImpl::SerializeTarget(
1078 const DevToolsTarget& target,
1079 const std::string& host) {
1080 base::DictionaryValue* dictionary = new base::DictionaryValue;
1082 std::string id = target.GetId();
1083 dictionary->SetString(kTargetIdField, id);
1084 std::string parent_id = target.GetParentId();
1085 if (!parent_id.empty())
1086 dictionary->SetString(kTargetParentIdField, parent_id);
1087 dictionary->SetString(kTargetTypeField, target.GetType());
1088 dictionary->SetString(kTargetTitleField,
1089 net::EscapeForHTML(target.GetTitle()));
1090 dictionary->SetString(kTargetDescriptionField, target.GetDescription());
1092 GURL url = target.GetURL();
1093 dictionary->SetString(kTargetUrlField, url.spec());
1095 GURL favicon_url = target.GetFaviconURL();
1096 if (favicon_url.is_valid())
1097 dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
1099 DevToolsManagerDelegate* manager_delegate =
1100 DevToolsManager::GetInstance()->delegate();
1101 if (manager_delegate &&
1102 !manager_delegate->GetPageThumbnailData(url).empty()) {
1103 dictionary->SetString(kTargetThumbnailUrlField,
1104 std::string(kThumbUrlPrefix) + id);
1107 if (!target.IsAttached()) {
1108 dictionary->SetString(kTargetWebSocketDebuggerUrlField,
1109 base::StringPrintf("ws://%s%s%s",
1110 host.c_str(),
1111 kPageUrlPrefix,
1112 id.c_str()));
1113 std::string devtools_frontend_url = GetFrontendURLInternal(
1114 id.c_str(),
1115 host);
1116 dictionary->SetString(
1117 kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
1120 return dictionary;
1123 } // namespace content