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.
4 
5 #include <stddef.h>
6 #include <stdint.h>
7 
8 #include <algorithm>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_util.h"
14 #include "base/guid.h"
15 #include "base/json/json_writer.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "base/memory/ref_counted_memory.h"
20 #include "base/message_loop/message_pump_type.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/strings/string_number_conversions.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/task/thread_pool.h"
26 #include "base/threading/thread.h"
27 #include "base/values.h"
28 #include "build/build_config.h"
29 #include "content/browser/devtools/devtools_http_handler.h"
30 #include "content/browser/devtools/devtools_manager.h"
31 #include "content/public/browser/browser_task_traits.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "content/public/browser/content_browser_client.h"
34 #include "content/public/browser/devtools_external_agent_proxy_delegate.h"
35 #include "content/public/browser/devtools_frontend_host.h"
36 #include "content/public/browser/devtools_manager_delegate.h"
37 #include "content/public/browser/devtools_socket_factory.h"
38 #include "content/public/common/content_client.h"
39 #include "content/public/common/url_constants.h"
40 #include "content/public/common/user_agent.h"
41 #include "net/base/escape.h"
42 #include "net/base/io_buffer.h"
43 #include "net/base/ip_endpoint.h"
44 #include "net/base/net_errors.h"
45 #include "net/base/url_util.h"
46 #include "net/server/http_server.h"
47 #include "net/server/http_server_request_info.h"
48 #include "net/server/http_server_response_info.h"
49 #include "net/socket/server_socket.h"
50 #include "net/traffic_annotation/network_traffic_annotation.h"
51 #include "v8/include/v8-version-string.h"
52 
53 #if defined(OS_ANDROID)
54 #include "base/android/build_info.h"
55 #endif
56 
57 #if !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
58 #include "content/browser/devtools/grit/devtools_resources.h"  // nogncheck
59 #endif
60 
61 namespace content {
62 
63 namespace {
64 
65 const base::FilePath::CharType kDevToolsActivePortFileName[] =
66     FILE_PATH_LITERAL("DevToolsActivePort");
67 
68 const char kDevToolsHandlerThreadName[] = "Chrome_DevToolsHandlerThread";
69 
70 const char kPageUrlPrefix[] = "/devtools/page/";
71 const char kBrowserUrlPrefix[] = "/devtools/browser";
72 
73 const char kTargetIdField[] = "id";
74 const char kTargetParentIdField[] = "parentId";
75 const char kTargetTypeField[] = "type";
76 const char kTargetTitleField[] = "title";
77 const char kTargetDescriptionField[] = "description";
78 const char kTargetUrlField[] = "url";
79 const char kTargetFaviconUrlField[] = "faviconUrl";
80 const char kTargetWebSocketDebuggerUrlField[] = "webSocketDebuggerUrl";
81 const char kTargetDevtoolsFrontendUrlField[] = "devtoolsFrontendUrl";
82 
83 const int32_t kSendBufferSizeForDevTools = 256 * 1024 * 1024;  // 256Mb
84 const int32_t kReceiveBufferSizeForDevTools = 100 * 1024 * 1024;  // 100Mb
85 
86 constexpr net::NetworkTrafficAnnotationTag
87     kDevtoolsHttpHandlerTrafficAnnotation =
88         net::DefineNetworkTrafficAnnotation("devtools_http_handler", R"(
89       semantics {
90         sender: "Devtools Http Handler"
91         description:
92           "This is a remote debugging server, only enabled by "
93           "'--remote-debugging-port' switch. It exposes debugging protocol "
94           "over websockets."
95         trigger: "Run with '--remote-debugging-port' switch."
96         data: "Debugging data, including any data on the open pages."
97         destination: OTHER
98         destination_other: "The data can be sent to any destination."
99       }
100       policy {
101         cookies_allowed: NO
102         setting:
103           "This request cannot be disabled in settings. However it will never "
104           "be made if user does not run with '--remote-debugging-port' switch."
105         policy_exception_justification:
106           "Not implemented, only used in Devtools and is behind a switch."
107       })");
108 
RequestIsSafeToServe(const net::HttpServerRequestInfo & info)109 bool RequestIsSafeToServe(const net::HttpServerRequestInfo& info) {
110   // For browser-originating requests, serve only those that are coming from
111   // pages loaded off localhost or fixed IPs.
112   std::string header = info.GetHeaderValue("host");
113   if (header.empty())
114     return true;
115   GURL url = GURL("https://" + header);
116   return url.HostIsIPAddress() || net::IsLocalHostname(url.host(), nullptr);
117 }
118 
119 }  // namespace
120 
121 // ServerWrapper -------------------------------------------------------------
122 // All methods in this class are only called on handler thread.
123 class ServerWrapper : net::HttpServer::Delegate {
124  public:
125   ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
126                 std::unique_ptr<net::ServerSocket> socket,
127                 const base::FilePath& debug_frontend_dir,
128                 bool bundles_resources);
129 
130   int GetLocalAddress(net::IPEndPoint* address);
131 
132   void AcceptWebSocket(int connection_id,
133                        const net::HttpServerRequestInfo& request);
134   void SendOverWebSocket(int connection_id, std::string message);
135   void SendResponse(int connection_id,
136                     const net::HttpServerResponseInfo& response);
137   void Send200(int connection_id,
138                const std::string& data,
139                const std::string& mime_type);
140   void Send404(int connection_id);
141   void Send500(int connection_id, const std::string& message);
142   void Close(int connection_id);
143 
~ServerWrapper()144   ~ServerWrapper() override {}
145 
146  private:
147   // net::HttpServer::Delegate implementation.
OnConnect(int connection_id)148   void OnConnect(int connection_id) override {}
149   void OnHttpRequest(int connection_id,
150                      const net::HttpServerRequestInfo& info) override;
151   void OnWebSocketRequest(int connection_id,
152                           const net::HttpServerRequestInfo& info) override;
153   void OnWebSocketMessage(int connection_id, std::string data) override;
154   void OnClose(int connection_id) override;
155 
156   base::WeakPtr<DevToolsHttpHandler> handler_;
157   std::unique_ptr<net::HttpServer> server_;
158   base::FilePath debug_frontend_dir_;
159   bool bundles_resources_;
160 };
161 
ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,std::unique_ptr<net::ServerSocket> socket,const base::FilePath & debug_frontend_dir,bool bundles_resources)162 ServerWrapper::ServerWrapper(base::WeakPtr<DevToolsHttpHandler> handler,
163                              std::unique_ptr<net::ServerSocket> socket,
164                              const base::FilePath& debug_frontend_dir,
165                              bool bundles_resources)
166     : handler_(handler),
167       server_(new net::HttpServer(std::move(socket), this)),
168       debug_frontend_dir_(debug_frontend_dir),
169       bundles_resources_(bundles_resources) {}
170 
GetLocalAddress(net::IPEndPoint * address)171 int ServerWrapper::GetLocalAddress(net::IPEndPoint* address) {
172   return server_->GetLocalAddress(address);
173 }
174 
AcceptWebSocket(int connection_id,const net::HttpServerRequestInfo & request)175 void ServerWrapper::AcceptWebSocket(int connection_id,
176                                     const net::HttpServerRequestInfo& request) {
177   server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
178   server_->SetReceiveBufferSize(connection_id, kReceiveBufferSizeForDevTools);
179   server_->AcceptWebSocket(connection_id, request,
180                            kDevtoolsHttpHandlerTrafficAnnotation);
181 }
182 
SendOverWebSocket(int connection_id,std::string message)183 void ServerWrapper::SendOverWebSocket(int connection_id, std::string message) {
184   server_->SendOverWebSocket(connection_id, std::move(message),
185                              kDevtoolsHttpHandlerTrafficAnnotation);
186 }
187 
SendResponse(int connection_id,const net::HttpServerResponseInfo & response)188 void ServerWrapper::SendResponse(int connection_id,
189                                  const net::HttpServerResponseInfo& response) {
190   server_->SendResponse(connection_id, response,
191                         kDevtoolsHttpHandlerTrafficAnnotation);
192 }
193 
Send200(int connection_id,const std::string & data,const std::string & mime_type)194 void ServerWrapper::Send200(int connection_id,
195                             const std::string& data,
196                             const std::string& mime_type) {
197   server_->Send200(connection_id, data, mime_type,
198                    kDevtoolsHttpHandlerTrafficAnnotation);
199 }
200 
Send404(int connection_id)201 void ServerWrapper::Send404(int connection_id) {
202   server_->Send404(connection_id, kDevtoolsHttpHandlerTrafficAnnotation);
203 }
204 
Send500(int connection_id,const std::string & message)205 void ServerWrapper::Send500(int connection_id,
206                             const std::string& message) {
207   server_->Send500(connection_id, message,
208                    kDevtoolsHttpHandlerTrafficAnnotation);
209 }
210 
Close(int connection_id)211 void ServerWrapper::Close(int connection_id) {
212   server_->Close(connection_id);
213 }
214 
215 // Thread and ServerWrapper lifetime management ------------------------------
216 
TerminateOnUI(std::unique_ptr<base::Thread> thread,std::unique_ptr<ServerWrapper> server_wrapper,std::unique_ptr<DevToolsSocketFactory> socket_factory)217 void TerminateOnUI(std::unique_ptr<base::Thread> thread,
218                    std::unique_ptr<ServerWrapper> server_wrapper,
219                    std::unique_ptr<DevToolsSocketFactory> socket_factory) {
220   DCHECK_CURRENTLY_ON(BrowserThread::UI);
221   if (server_wrapper)
222     thread->task_runner()->DeleteSoon(FROM_HERE, std::move(server_wrapper));
223   if (socket_factory)
224     thread->task_runner()->DeleteSoon(FROM_HERE, std::move(socket_factory));
225   if (thread) {
226     base::ThreadPool::PostTask(
227         FROM_HERE,
228         {base::WithBaseSyncPrimitives(), base::TaskPriority::BEST_EFFORT},
229         BindOnce([](std::unique_ptr<base::Thread>) {}, std::move(thread)));
230   }
231 }
232 
ServerStartedOnUI(base::WeakPtr<DevToolsHttpHandler> handler,base::Thread * thread,ServerWrapper * server_wrapper,DevToolsSocketFactory * socket_factory,std::unique_ptr<net::IPEndPoint> ip_address)233 void ServerStartedOnUI(base::WeakPtr<DevToolsHttpHandler> handler,
234                        base::Thread* thread,
235                        ServerWrapper* server_wrapper,
236                        DevToolsSocketFactory* socket_factory,
237                        std::unique_ptr<net::IPEndPoint> ip_address) {
238   DCHECK_CURRENTLY_ON(BrowserThread::UI);
239   if (handler && thread && server_wrapper) {
240     handler->ServerStarted(
241         std::unique_ptr<base::Thread>(thread),
242         std::unique_ptr<ServerWrapper>(server_wrapper),
243         std::unique_ptr<DevToolsSocketFactory>(socket_factory),
244         std::move(ip_address));
245   } else {
246     TerminateOnUI(std::unique_ptr<base::Thread>(thread),
247                   std::unique_ptr<ServerWrapper>(server_wrapper),
248                   std::unique_ptr<DevToolsSocketFactory>(socket_factory));
249   }
250 }
251 
StartServerOnHandlerThread(base::WeakPtr<DevToolsHttpHandler> handler,std::unique_ptr<base::Thread> thread,std::unique_ptr<DevToolsSocketFactory> socket_factory,const base::FilePath & output_directory,const base::FilePath & debug_frontend_dir,const std::string & browser_guid,bool bundles_resources)252 void StartServerOnHandlerThread(
253     base::WeakPtr<DevToolsHttpHandler> handler,
254     std::unique_ptr<base::Thread> thread,
255     std::unique_ptr<DevToolsSocketFactory> socket_factory,
256     const base::FilePath& output_directory,
257     const base::FilePath& debug_frontend_dir,
258     const std::string& browser_guid,
259     bool bundles_resources) {
260   DCHECK(thread->task_runner()->BelongsToCurrentThread());
261   std::unique_ptr<ServerWrapper> server_wrapper;
262   std::unique_ptr<net::ServerSocket> server_socket =
263       socket_factory->CreateForHttpServer();
264   std::unique_ptr<net::IPEndPoint> ip_address(new net::IPEndPoint);
265   if (server_socket) {
266     server_wrapper.reset(new ServerWrapper(handler, std::move(server_socket),
267                                            debug_frontend_dir,
268                                            bundles_resources));
269     if (server_wrapper->GetLocalAddress(ip_address.get()) != net::OK)
270       ip_address.reset();
271   } else {
272     ip_address.reset();
273   }
274 
275   if (ip_address) {
276     std::string message = base::StringPrintf(
277         "\nDevTools listening on ws://%s%s\n", ip_address->ToString().c_str(),
278         browser_guid.c_str());
279     fprintf(stderr, "%s", message.c_str());
280     fflush(stderr);
281 
282     // Write this port to a well-known file in the profile directory
283     // so Telemetry, ChromeDriver, etc. can pick it up.
284     if (!output_directory.empty()) {
285       base::FilePath path =
286           output_directory.Append(kDevToolsActivePortFileName);
287       std::string port_target_string = base::StringPrintf(
288           "%d\n%s", ip_address->port(), browser_guid.c_str());
289       if (base::WriteFile(path, port_target_string.c_str(),
290                           static_cast<int>(port_target_string.length())) < 0) {
291         LOG(ERROR) << "Error writing DevTools active port to file";
292       }
293     }
294   } else {
295 #if !defined(OS_ANDROID)
296     // Android uses UNIX domain sockets which don't have an IP address.
297     LOG(ERROR) << "Cannot start http server for devtools.";
298 #endif
299   }
300 
301   GetUIThreadTaskRunner({})->PostTask(
302       FROM_HERE,
303       base::BindOnce(&ServerStartedOnUI, std::move(handler), thread.release(),
304                      server_wrapper.release(), socket_factory.release(),
305                      std::move(ip_address)));
306 }
307 
308 // DevToolsAgentHostClientImpl -----------------------------------------------
309 // An internal implementation of DevToolsAgentHostClient that delegates
310 // messages sent to a DebuggerShell instance.
311 class DevToolsAgentHostClientImpl : public DevToolsAgentHostClient {
312  public:
DevToolsAgentHostClientImpl(scoped_refptr<base::SingleThreadTaskRunner> task_runner,ServerWrapper * server_wrapper,int connection_id,scoped_refptr<DevToolsAgentHost> agent_host)313   DevToolsAgentHostClientImpl(
314       scoped_refptr<base::SingleThreadTaskRunner> task_runner,
315       ServerWrapper* server_wrapper,
316       int connection_id,
317       scoped_refptr<DevToolsAgentHost> agent_host)
318       : task_runner_(std::move(task_runner)),
319         server_wrapper_(server_wrapper),
320         connection_id_(connection_id),
321         agent_host_(agent_host) {
322     DCHECK_CURRENTLY_ON(BrowserThread::UI);
323     // TODO(dgozman): handle return value of AttachClient.
324     agent_host_->AttachClient(this);
325   }
326 
~DevToolsAgentHostClientImpl()327   ~DevToolsAgentHostClientImpl() override {
328     DCHECK_CURRENTLY_ON(BrowserThread::UI);
329     if (agent_host_)
330       agent_host_->DetachClient(this);
331   }
332 
AgentHostClosed(DevToolsAgentHost * agent_host)333   void AgentHostClosed(DevToolsAgentHost* agent_host) override {
334     DCHECK_CURRENTLY_ON(BrowserThread::UI);
335     DCHECK(agent_host == agent_host_.get());
336 
337     constexpr char kMsg[] =
338         "{\"method\":\"Inspector.detached\","
339         "\"params\":{\"reason\":\"target_closed\"}}";
340     DispatchProtocolMessage(
341         agent_host, base::as_bytes(base::make_span(kMsg, strlen(kMsg))));
342 
343     agent_host_ = nullptr;
344     task_runner_->PostTask(
345         FROM_HERE,
346         base::BindOnce(&ServerWrapper::Close, base::Unretained(server_wrapper_),
347                        connection_id_));
348   }
349 
DispatchProtocolMessage(DevToolsAgentHost * agent_host,base::span<const uint8_t> message)350   void DispatchProtocolMessage(DevToolsAgentHost* agent_host,
351                                base::span<const uint8_t> message) override {
352     DCHECK_CURRENTLY_ON(BrowserThread::UI);
353     DCHECK(agent_host == agent_host_.get());
354     task_runner_->PostTask(
355         FROM_HERE,
356         base::BindOnce(&ServerWrapper::SendOverWebSocket,
357                        base::Unretained(server_wrapper_), connection_id_,
358                        std::string(message.begin(), message.end())));
359   }
360 
OnMessage(base::span<const uint8_t> message)361   void OnMessage(base::span<const uint8_t> message) {
362     DCHECK_CURRENTLY_ON(BrowserThread::UI);
363     if (agent_host_)
364       agent_host_->DispatchProtocolMessage(this, message);
365   }
366 
367  private:
368   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
369   ServerWrapper* const server_wrapper_;
370   const int connection_id_;
371   scoped_refptr<DevToolsAgentHost> agent_host_;
372 };
373 
TimeComparator(scoped_refptr<DevToolsAgentHost> host1,scoped_refptr<DevToolsAgentHost> host2)374 static bool TimeComparator(scoped_refptr<DevToolsAgentHost> host1,
375                            scoped_refptr<DevToolsAgentHost> host2) {
376   return host1->GetLastActivityTime() > host2->GetLastActivityTime();
377 }
378 
379 // DevToolsHttpHandler -------------------------------------------------------
380 
~DevToolsHttpHandler()381 DevToolsHttpHandler::~DevToolsHttpHandler() {
382   // Disconnecting sessions might lead to the last minute messages generated
383   // by the targets. It is essential that this happens before we issue delete
384   // soon for the server wrapper.
385   connection_to_client_.clear();
386   TerminateOnUI(std::move(thread_), std::move(server_wrapper_),
387                 std::move(socket_factory_));
388 }
389 
PathWithoutParams(const std::string & path)390 static std::string PathWithoutParams(const std::string& path) {
391   size_t query_position = path.find('?');
392   if (query_position != std::string::npos)
393     return path.substr(0, query_position);
394   return path;
395 }
396 
GetMimeType(const std::string & filename)397 static std::string GetMimeType(const std::string& filename) {
398   if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) {
399     return "text/html";
400   } else if (base::EndsWith(filename, ".css",
401                             base::CompareCase::INSENSITIVE_ASCII)) {
402     return "text/css";
403   } else if (base::EndsWith(filename, ".js",
404                             base::CompareCase::INSENSITIVE_ASCII)) {
405     return "application/javascript";
406   } else if (base::EndsWith(filename, ".png",
407                             base::CompareCase::INSENSITIVE_ASCII)) {
408     return "image/png";
409   } else if (base::EndsWith(filename, ".gif",
410                             base::CompareCase::INSENSITIVE_ASCII)) {
411     return "image/gif";
412   } else if (base::EndsWith(filename, ".json",
413                             base::CompareCase::INSENSITIVE_ASCII)) {
414     return "application/json";
415   } else if (base::EndsWith(filename, ".svg",
416                             base::CompareCase::INSENSITIVE_ASCII)) {
417     return "image/svg+xml";
418   }
419   LOG(ERROR) << "GetMimeType doesn't know mime type for: "
420              << filename
421              << " text/plain will be returned";
422   return "text/plain";
423 }
424 
OnHttpRequest(int connection_id,const net::HttpServerRequestInfo & info)425 void ServerWrapper::OnHttpRequest(int connection_id,
426                                   const net::HttpServerRequestInfo& info) {
427   if (!RequestIsSafeToServe(info)) {
428     Send500(connection_id,
429             "Host header is specified and is not an IP address or localhost.");
430     return;
431   }
432 
433   server_->SetSendBufferSize(connection_id, kSendBufferSizeForDevTools);
434 
435   if (base::StartsWith(info.path, "/json", base::CompareCase::SENSITIVE)) {
436     GetUIThreadTaskRunner({})->PostTask(
437         FROM_HERE, base::BindOnce(&DevToolsHttpHandler::OnJsonRequest, handler_,
438                                   connection_id, info));
439     return;
440   }
441 
442   if (info.path.empty() || info.path == "/") {
443     // Discovery page request.
444     GetUIThreadTaskRunner({})->PostTask(
445         FROM_HERE, base::BindOnce(&DevToolsHttpHandler::OnDiscoveryPageRequest,
446                                   handler_, connection_id));
447     return;
448   }
449 
450   if (!base::StartsWith(info.path, "/devtools/",
451                         base::CompareCase::SENSITIVE)) {
452     server_->Send404(connection_id, kDevtoolsHttpHandlerTrafficAnnotation);
453     return;
454   }
455 
456   std::string filename = PathWithoutParams(info.path.substr(10));
457   std::string mime_type = GetMimeType(filename);
458 
459   if (!debug_frontend_dir_.empty()) {
460     base::FilePath path = debug_frontend_dir_.AppendASCII(filename);
461     std::string data;
462     base::ReadFileToString(path, &data);
463     server_->Send200(connection_id, data, mime_type,
464                      kDevtoolsHttpHandlerTrafficAnnotation);
465     return;
466   }
467 
468   if (bundles_resources_) {
469     GetUIThreadTaskRunner({})->PostTask(
470         FROM_HERE,
471         base::BindOnce(&DevToolsHttpHandler::OnFrontendResourceRequest,
472                        handler_, connection_id, filename));
473     return;
474   }
475   server_->Send404(connection_id, kDevtoolsHttpHandlerTrafficAnnotation);
476 }
477 
OnWebSocketRequest(int connection_id,const net::HttpServerRequestInfo & request)478 void ServerWrapper::OnWebSocketRequest(
479     int connection_id,
480     const net::HttpServerRequestInfo& request) {
481   GetUIThreadTaskRunner({})->PostTask(
482       FROM_HERE, base::BindOnce(&DevToolsHttpHandler::OnWebSocketRequest,
483                                 handler_, connection_id, request));
484 }
485 
OnWebSocketMessage(int connection_id,std::string data)486 void ServerWrapper::OnWebSocketMessage(int connection_id, std::string data) {
487   GetUIThreadTaskRunner({})->PostTask(
488       FROM_HERE, base::BindOnce(&DevToolsHttpHandler::OnWebSocketMessage,
489                                 handler_, connection_id, std::move(data)));
490 }
491 
OnClose(int connection_id)492 void ServerWrapper::OnClose(int connection_id) {
493   GetUIThreadTaskRunner({})->PostTask(
494       FROM_HERE,
495       base::BindOnce(&DevToolsHttpHandler::OnClose, handler_, connection_id));
496 }
497 
GetFrontendURLInternal(scoped_refptr<DevToolsAgentHost> agent_host,const std::string & id,const std::string & host)498 std::string DevToolsHttpHandler::GetFrontendURLInternal(
499     scoped_refptr<DevToolsAgentHost> agent_host,
500     const std::string& id,
501     const std::string& host) {
502   std::string frontend_url;
503   if (delegate_->HasBundledFrontendResources()) {
504     frontend_url = "/devtools/inspector.html";
505   } else {
506     std::string type = agent_host->GetType();
507     bool is_worker = type == DevToolsAgentHost::kTypeServiceWorker ||
508                      type == DevToolsAgentHost::kTypeSharedWorker;
509     frontend_url = base::StringPrintf(
510         "https://chrome-devtools-frontend.appspot.com/serve_rev/%s/%s.html",
511         GetWebKitRevision().c_str(), is_worker ? "worker_app" : "inspector");
512   }
513   return base::StringPrintf("%s?ws=%s%s%s", frontend_url.c_str(), host.c_str(),
514                             kPageUrlPrefix, id.c_str());
515 }
516 
ParseJsonPath(const std::string & path,std::string * command,std::string * target_id)517 static bool ParseJsonPath(
518     const std::string& path,
519     std::string* command,
520     std::string* target_id) {
521 
522   // Fall back to list in case of empty query.
523   if (path.empty()) {
524     *command = "list";
525     return true;
526   }
527 
528   if (!base::StartsWith(path, "/", base::CompareCase::SENSITIVE)) {
529     // Malformed command.
530     return false;
531   }
532   *command = path.substr(1);
533 
534   size_t separator_pos = command->find("/");
535   if (separator_pos != std::string::npos) {
536     *target_id = command->substr(separator_pos + 1);
537     *command = command->substr(0, separator_pos);
538   }
539   return true;
540 }
541 
OnJsonRequest(int connection_id,const net::HttpServerRequestInfo & info)542 void DevToolsHttpHandler::OnJsonRequest(
543     int connection_id,
544     const net::HttpServerRequestInfo& info) {
545   // Trim /json
546   std::string path = info.path.substr(5);
547 
548   // Trim fragment and query
549   std::string query;
550   size_t query_pos = path.find('?');
551   if (query_pos != std::string::npos) {
552     query = path.substr(query_pos + 1);
553     path = path.substr(0, query_pos);
554   }
555 
556   size_t fragment_pos = path.find('#');
557   if (fragment_pos != std::string::npos)
558     path = path.substr(0, fragment_pos);
559 
560   std::string command;
561   std::string target_id;
562   if (!ParseJsonPath(path, &command, &target_id)) {
563     SendJson(connection_id, net::HTTP_NOT_FOUND, nullptr,
564              "Malformed query: " + info.path);
565     return;
566   }
567 
568   if (command == "version") {
569     base::DictionaryValue version;
570     version.SetString("Protocol-Version",
571                       DevToolsAgentHost::GetProtocolVersion());
572     version.SetString("WebKit-Version", GetWebKitVersion());
573     version.SetString("Browser", GetContentClient()->browser()->GetProduct());
574     version.SetString("User-Agent",
575                       GetContentClient()->browser()->GetUserAgent());
576     version.SetString("V8-Version", V8_VERSION_STRING);
577     std::string host = info.GetHeaderValue("host");
578     version.SetString(
579         kTargetWebSocketDebuggerUrlField,
580         base::StringPrintf("ws://%s%s", host.c_str(), browser_guid_.c_str()));
581 #if defined(OS_ANDROID)
582     version.SetString(
583         "Android-Package",
584         base::android::BuildInfo::GetInstance()->host_package_name());
585 #endif
586     SendJson(connection_id, net::HTTP_OK, &version, std::string());
587     return;
588   }
589 
590   if (command == "protocol") {
591     DecompressAndSendJsonProtocol(connection_id);
592     return;
593   }
594 
595   if (command == "list") {
596     DevToolsManager* manager = DevToolsManager::GetInstance();
597     DevToolsAgentHost::List list =
598         manager->delegate() ? manager->delegate()->RemoteDebuggingTargets()
599                             : DevToolsAgentHost::GetOrCreateAll();
600     RespondToJsonList(connection_id, info.GetHeaderValue("host"),
601                       std::move(list));
602     return;
603   }
604 
605   if (command == "new") {
606     GURL url(net::UnescapeBinaryURLComponent(query));
607     if (!url.is_valid())
608       url = GURL(url::kAboutBlankURL);
609     scoped_refptr<DevToolsAgentHost> agent_host = nullptr;
610     agent_host = delegate_->CreateNewTarget(url);
611     if (!agent_host) {
612       SendJson(connection_id, net::HTTP_INTERNAL_SERVER_ERROR, nullptr,
613                "Could not create new page");
614       return;
615     }
616     std::string host = info.GetHeaderValue("host");
617     std::unique_ptr<base::DictionaryValue> dictionary(
618         SerializeDescriptor(agent_host, host));
619     SendJson(connection_id, net::HTTP_OK, dictionary.get(), std::string());
620     return;
621   }
622 
623   if (command == "activate" || command == "close") {
624     scoped_refptr<DevToolsAgentHost> agent_host =
625         DevToolsAgentHost::GetForId(target_id);
626     if (!agent_host) {
627       SendJson(connection_id, net::HTTP_NOT_FOUND, nullptr,
628                "No such target id: " + target_id);
629       return;
630     }
631 
632     if (command == "activate") {
633       if (agent_host->Activate()) {
634         SendJson(connection_id, net::HTTP_OK, nullptr, "Target activated");
635       } else {
636         SendJson(connection_id, net::HTTP_INTERNAL_SERVER_ERROR, nullptr,
637                  "Could not activate target id: " + target_id);
638       }
639       return;
640     }
641 
642     if (command == "close") {
643       if (agent_host->Close()) {
644         SendJson(connection_id, net::HTTP_OK, nullptr, "Target is closing");
645       } else {
646         SendJson(connection_id, net::HTTP_INTERNAL_SERVER_ERROR, nullptr,
647                  "Could not close target id: " + target_id);
648       }
649       return;
650     }
651   }
652   SendJson(connection_id, net::HTTP_NOT_FOUND, nullptr,
653            "Unknown command: " + command);
654 }
655 
DecompressAndSendJsonProtocol(int connection_id)656 void DevToolsHttpHandler::DecompressAndSendJsonProtocol(int connection_id) {
657 #if defined(OS_ANDROID) || defined(OS_FUCHSIA)
658   NOTREACHED();
659 #else
660   scoped_refptr<base::RefCountedMemory> bytes =
661       GetContentClient()->GetDataResourceBytes(COMPRESSED_PROTOCOL_JSON);
662   std::string json_protocol(reinterpret_cast<const char*>(bytes->front()),
663                             bytes->size());
664 
665   net::HttpServerResponseInfo response(net::HTTP_OK);
666   response.SetBody(json_protocol, "application/json; charset=UTF-8");
667 
668   thread_->task_runner()->PostTask(
669       FROM_HERE, base::BindOnce(&ServerWrapper::SendResponse,
670                                 base::Unretained(server_wrapper_.get()),
671                                 connection_id, response));
672 #endif  // defined(OS_ANDROID) || defined(OS_FUCHSIA)
673 }
674 
RespondToJsonList(int connection_id,const std::string & host,DevToolsAgentHost::List hosts)675 void DevToolsHttpHandler::RespondToJsonList(
676     int connection_id,
677     const std::string& host,
678     DevToolsAgentHost::List hosts) {
679   DevToolsAgentHost::List agent_hosts = std::move(hosts);
680   std::sort(agent_hosts.begin(), agent_hosts.end(), TimeComparator);
681   base::ListValue list_value;
682   for (auto& agent_host : agent_hosts)
683     list_value.Append(SerializeDescriptor(agent_host, host));
684   SendJson(connection_id, net::HTTP_OK, &list_value, std::string());
685 }
686 
OnDiscoveryPageRequest(int connection_id)687 void DevToolsHttpHandler::OnDiscoveryPageRequest(int connection_id) {
688   std::string response = delegate_->GetDiscoveryPageHTML();
689   Send200(connection_id, response, "text/html; charset=UTF-8");
690 }
691 
OnFrontendResourceRequest(int connection_id,const std::string & path)692 void DevToolsHttpHandler::OnFrontendResourceRequest(
693     int connection_id, const std::string& path) {
694 #if defined(OS_ANDROID)
695   Send404(connection_id);
696 #else
697   Send200(connection_id,
698           content::DevToolsFrontendHost::GetFrontendResource(path),
699           GetMimeType(path));
700 #endif
701 }
702 
OnWebSocketRequest(int connection_id,const net::HttpServerRequestInfo & request)703 void DevToolsHttpHandler::OnWebSocketRequest(
704     int connection_id,
705     const net::HttpServerRequestInfo& request) {
706   if (!thread_)
707     return;
708 
709   if (base::StartsWith(request.path, browser_guid_,
710                        base::CompareCase::SENSITIVE)) {
711     scoped_refptr<DevToolsAgentHost> browser_agent =
712         DevToolsAgentHost::CreateForBrowser(
713             thread_->task_runner(),
714             base::BindRepeating(&DevToolsSocketFactory::CreateForTethering,
715                                 base::Unretained(socket_factory_.get())));
716     connection_to_client_[connection_id].reset(new DevToolsAgentHostClientImpl(
717         thread_->task_runner(), server_wrapper_.get(), connection_id,
718         browser_agent));
719     AcceptWebSocket(connection_id, request);
720     return;
721   }
722 
723   if (!base::StartsWith(request.path, kPageUrlPrefix,
724                         base::CompareCase::SENSITIVE)) {
725     Send404(connection_id);
726     return;
727   }
728 
729   std::string target_id = request.path.substr(strlen(kPageUrlPrefix));
730   scoped_refptr<DevToolsAgentHost> agent =
731       DevToolsAgentHost::GetForId(target_id);
732   if (!agent) {
733     Send500(connection_id, "No such target id: " + target_id);
734     return;
735   }
736 
737   connection_to_client_[connection_id].reset(new DevToolsAgentHostClientImpl(
738       thread_->task_runner(), server_wrapper_.get(), connection_id, agent));
739 
740   AcceptWebSocket(connection_id, request);
741 }
742 
OnWebSocketMessage(int connection_id,std::string data)743 void DevToolsHttpHandler::OnWebSocketMessage(int connection_id,
744                                              std::string data) {
745   auto it = connection_to_client_.find(connection_id);
746   if (it != connection_to_client_.end()) {
747     it->second->OnMessage(base::as_bytes(base::make_span(data)));
748   }
749 }
750 
OnClose(int connection_id)751 void DevToolsHttpHandler::OnClose(int connection_id) {
752   connection_to_client_.erase(connection_id);
753 }
754 
DevToolsHttpHandler(DevToolsManagerDelegate * delegate,std::unique_ptr<DevToolsSocketFactory> socket_factory,const base::FilePath & output_directory,const base::FilePath & debug_frontend_dir)755 DevToolsHttpHandler::DevToolsHttpHandler(
756     DevToolsManagerDelegate* delegate,
757     std::unique_ptr<DevToolsSocketFactory> socket_factory,
758     const base::FilePath& output_directory,
759     const base::FilePath& debug_frontend_dir)
760     : delegate_(delegate) {
761   browser_guid_ = delegate_->IsBrowserTargetDiscoverable()
762                       ? kBrowserUrlPrefix
763                       : base::StringPrintf("%s/%s", kBrowserUrlPrefix,
764                                            base::GenerateGUID().c_str());
765   std::unique_ptr<base::Thread> thread(
766       new base::Thread(kDevToolsHandlerThreadName));
767   base::Thread::Options options;
768   options.message_pump_type = base::MessagePumpType::IO;
769   if (thread->StartWithOptions(options)) {
770     auto task_runner = thread->task_runner();
771     task_runner->PostTask(
772         FROM_HERE,
773         base::BindOnce(&StartServerOnHandlerThread, weak_factory_.GetWeakPtr(),
774                        std::move(thread), std::move(socket_factory),
775                        output_directory, debug_frontend_dir, browser_guid_,
776                        delegate_->HasBundledFrontendResources()));
777   }
778 }
779 
ServerStarted(std::unique_ptr<base::Thread> thread,std::unique_ptr<ServerWrapper> server_wrapper,std::unique_ptr<DevToolsSocketFactory> socket_factory,std::unique_ptr<net::IPEndPoint> ip_address)780 void DevToolsHttpHandler::ServerStarted(
781     std::unique_ptr<base::Thread> thread,
782     std::unique_ptr<ServerWrapper> server_wrapper,
783     std::unique_ptr<DevToolsSocketFactory> socket_factory,
784     std::unique_ptr<net::IPEndPoint> ip_address) {
785   thread_ = std::move(thread);
786   server_wrapper_ = std::move(server_wrapper);
787   socket_factory_ = std::move(socket_factory);
788   server_ip_address_ = std::move(ip_address);
789 }
790 
SendJson(int connection_id,net::HttpStatusCode status_code,base::Value * value,const std::string & message)791 void DevToolsHttpHandler::SendJson(int connection_id,
792                                    net::HttpStatusCode status_code,
793                                    base::Value* value,
794                                    const std::string& message) {
795   if (!thread_)
796     return;
797 
798   // Serialize value and message.
799   std::string json_value;
800   if (value) {
801     base::JSONWriter::WriteWithOptions(
802         *value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &json_value);
803   }
804   std::string json_message;
805   base::JSONWriter::Write(base::Value(message), &json_message);
806 
807   net::HttpServerResponseInfo response(status_code);
808   response.SetBody(json_value + message, "application/json; charset=UTF-8");
809 
810   thread_->task_runner()->PostTask(
811       FROM_HERE, base::BindOnce(&ServerWrapper::SendResponse,
812                                 base::Unretained(server_wrapper_.get()),
813                                 connection_id, response));
814 }
815 
Send200(int connection_id,const std::string & data,const std::string & mime_type)816 void DevToolsHttpHandler::Send200(int connection_id,
817                                   const std::string& data,
818                                   const std::string& mime_type) {
819   if (!thread_)
820     return;
821   thread_->task_runner()->PostTask(
822       FROM_HERE, base::BindOnce(&ServerWrapper::Send200,
823                                 base::Unretained(server_wrapper_.get()),
824                                 connection_id, data, mime_type));
825 }
826 
Send404(int connection_id)827 void DevToolsHttpHandler::Send404(int connection_id) {
828   if (!thread_)
829     return;
830   thread_->task_runner()->PostTask(
831       FROM_HERE,
832       base::BindOnce(&ServerWrapper::Send404,
833                      base::Unretained(server_wrapper_.get()), connection_id));
834 }
835 
Send500(int connection_id,const std::string & message)836 void DevToolsHttpHandler::Send500(int connection_id,
837                                   const std::string& message) {
838   if (!thread_)
839     return;
840   thread_->task_runner()->PostTask(
841       FROM_HERE, base::BindOnce(&ServerWrapper::Send500,
842                                 base::Unretained(server_wrapper_.get()),
843                                 connection_id, message));
844 }
845 
AcceptWebSocket(int connection_id,const net::HttpServerRequestInfo & request)846 void DevToolsHttpHandler::AcceptWebSocket(
847     int connection_id,
848     const net::HttpServerRequestInfo& request) {
849   if (!thread_)
850     return;
851   thread_->task_runner()->PostTask(
852       FROM_HERE, base::BindOnce(&ServerWrapper::AcceptWebSocket,
853                                 base::Unretained(server_wrapper_.get()),
854                                 connection_id, request));
855 }
856 
SerializeDescriptor(scoped_refptr<DevToolsAgentHost> agent_host,const std::string & host)857 std::unique_ptr<base::DictionaryValue> DevToolsHttpHandler::SerializeDescriptor(
858     scoped_refptr<DevToolsAgentHost> agent_host,
859     const std::string& host) {
860   std::unique_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
861   std::string id = agent_host->GetId();
862   dictionary->SetString(kTargetIdField, id);
863   std::string parent_id = agent_host->GetParentId();
864   if (!parent_id.empty())
865     dictionary->SetString(kTargetParentIdField, parent_id);
866   dictionary->SetString(kTargetTypeField, agent_host->GetType());
867   dictionary->SetString(kTargetTitleField,
868                         net::EscapeForHTML(agent_host->GetTitle()));
869   dictionary->SetString(kTargetDescriptionField, agent_host->GetDescription());
870 
871   GURL url = agent_host->GetURL();
872   dictionary->SetString(kTargetUrlField, url.spec());
873 
874   GURL favicon_url = agent_host->GetFaviconURL();
875   if (favicon_url.is_valid())
876     dictionary->SetString(kTargetFaviconUrlField, favicon_url.spec());
877 
878   dictionary->SetString(kTargetWebSocketDebuggerUrlField,
879                         base::StringPrintf("ws://%s%s%s", host.c_str(),
880                                            kPageUrlPrefix, id.c_str()));
881   std::string devtools_frontend_url =
882       GetFrontendURLInternal(agent_host, id, host);
883   dictionary->SetString(kTargetDevtoolsFrontendUrlField, devtools_frontend_url);
884 
885   return dictionary;
886 }
887 
888 }  // namespace content
889