Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: chrome/browser/debugger/devtools_http_protocol_handler.cc

Issue 2870062: DevTools: implement basic handshake for remote debugging. (Closed)
Patch Set: utf16 conversion Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/debugger/devtools_http_protocol_handler.h" 5 #include "chrome/browser/debugger/devtools_http_protocol_handler.h"
6 6
7 #include "base/compiler_specific.h" 7 #include "base/compiler_specific.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop_proxy.h" 9 #include "base/message_loop_proxy.h"
10 #include "base/string_util.h" 10 #include "base/string_util.h"
(...skipping 19 matching lines...) Expand all
30 30
31 // An internal implementation of DevToolsClientHost that delegates 31 // An internal implementation of DevToolsClientHost that delegates
32 // messages sent for DevToolsClient to a DebuggerShell instance. 32 // messages sent for DevToolsClient to a DebuggerShell instance.
33 class DevToolsClientHostImpl : public DevToolsClientHost { 33 class DevToolsClientHostImpl : public DevToolsClientHost {
34 public: 34 public:
35 explicit DevToolsClientHostImpl(HttpListenSocket* socket) 35 explicit DevToolsClientHostImpl(HttpListenSocket* socket)
36 : socket_(socket) {} 36 : socket_(socket) {}
37 ~DevToolsClientHostImpl() {} 37 ~DevToolsClientHostImpl() {}
38 38
39 // DevToolsClientHost interface 39 // DevToolsClientHost interface
40 virtual void InspectedTabClosing() {} 40 virtual void InspectedTabClosing() {
41 socket_->Close();
42 }
43
41 virtual void SendMessageToClient(const IPC::Message& msg) { 44 virtual void SendMessageToClient(const IPC::Message& msg) {
42 IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg) 45 IPC_BEGIN_MESSAGE_MAP(DevToolsClientHostImpl, msg)
43 IPC_MESSAGE_HANDLER(DevToolsClientMsg_RpcMessage, OnRpcMessage); 46 IPC_MESSAGE_HANDLER(DevToolsClientMsg_RpcMessage, OnRpcMessage);
44 IPC_MESSAGE_UNHANDLED_ERROR() 47 IPC_MESSAGE_UNHANDLED_ERROR()
45 IPC_END_MESSAGE_MAP() 48 IPC_END_MESSAGE_MAP()
46 } 49 }
47 50
51 void NotifyCloseListener() {
52 DevToolsClientHost::NotifyCloseListener();
53 }
48 private: 54 private:
49 // Message handling routines 55 // Message handling routines
50 void OnRpcMessage(const DevToolsMessageData& data) { 56 void OnRpcMessage(const DevToolsMessageData& data) {
51 std::string message; 57 std::string message;
52 message += "devtools$$dispatch(\"" + data.class_name + "\", \"" + 58 message += "devtools$$dispatch(\"" + data.class_name + "\", \"" +
53 data.method_name + "\""; 59 data.method_name + "\"";
54 for (std::vector<std::string>::const_iterator it = data.arguments.begin(); 60 for (std::vector<std::string>::const_iterator it = data.arguments.begin();
55 it != data.arguments.end(); ++it) { 61 it != data.arguments.end(); ++it) {
56 std::string param = *it; 62 std::string param = *it;
57 if (!param.empty()) 63 if (!param.empty())
(...skipping 17 matching lines...) Expand all
75 ChromeThread::IO, FROM_HERE, 81 ChromeThread::IO, FROM_HERE,
76 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init)); 82 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Init));
77 } 83 }
78 84
79 void DevToolsHttpProtocolHandler::Stop() { 85 void DevToolsHttpProtocolHandler::Stop() {
80 ChromeThread::PostTask( 86 ChromeThread::PostTask(
81 ChromeThread::IO, FROM_HERE, 87 ChromeThread::IO, FROM_HERE,
82 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown)); 88 NewRunnableMethod(this, &DevToolsHttpProtocolHandler::Teardown));
83 } 89 }
84 90
85 void DevToolsHttpProtocolHandler::OnHttpRequest(HttpListenSocket* socket, 91 void DevToolsHttpProtocolHandler::OnHttpRequest(
86 HttpServerRequestInfo* info) { 92 HttpListenSocket* socket,
87 URLRequest* request = new URLRequest(GURL("chrome:/" + info->path), this); 93 const HttpServerRequestInfo& info) {
94 size_t pos = info.path.find("/devtools/");
95 if (pos != 0) {
96 socket->Send404();
97 return;
98 }
99
100 if (info.path == "/devtools/pages") {
101 // Pages discovery request.
102 ChromeThread::PostTask(
103 ChromeThread::UI,
104 FROM_HERE,
105 NewRunnableMethod(this,
106 &DevToolsHttpProtocolHandler::OnHttpRequestUI,
107 socket,
108 info));
109 return;
110 }
111
112 // Proxy static files from chrome://devtools/*.
113 URLRequest* request = new URLRequest(GURL("chrome:/" + info.path), this);
88 Bind(request, socket); 114 Bind(request, socket);
89 request->set_context( 115 request->set_context(
90 Profile::GetDefaultRequestContext()->GetURLRequestContext()); 116 Profile::GetDefaultRequestContext()->GetURLRequestContext());
91 request->Start(); 117 request->Start();
92 } 118 }
93 119
94 void DevToolsHttpProtocolHandler::OnWebSocketRequest( 120 void DevToolsHttpProtocolHandler::OnWebSocketRequest(
95 HttpListenSocket* socket, 121 HttpListenSocket* socket,
96 HttpServerRequestInfo* request) { 122 const HttpServerRequestInfo& request) {
97 socket->AcceptWebSocket(request); 123 ChromeThread::PostTask(
124 ChromeThread::UI,
125 FROM_HERE,
126 NewRunnableMethod(
127 this,
128 &DevToolsHttpProtocolHandler::OnWebSocketRequestUI,
129 socket,
130 request));
98 } 131 }
99 132
100 void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket, 133 void DevToolsHttpProtocolHandler::OnWebSocketMessage(HttpListenSocket* socket,
101 const std::string& data) { 134 const std::string& data) {
102 ChromeThread::PostTask( 135 ChromeThread::PostTask(
103 ChromeThread::UI, 136 ChromeThread::UI,
104 FROM_HERE, 137 FROM_HERE,
105 NewRunnableMethod( 138 NewRunnableMethod(
106 this, 139 this,
107 &DevToolsHttpProtocolHandler::OnWebSocketMessageUI, 140 &DevToolsHttpProtocolHandler::OnWebSocketMessageUI,
108 socket, 141 socket,
109 data)); 142 data));
110 } 143 }
111 144
145 void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) {
146 SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
147 if (it != socket_to_requests_io_.end()) {
148 // Dispose delegating socket.
149 for (std::set<URLRequest*>::iterator it2 = it->second.begin();
150 it2 != it->second.end(); ++it2) {
151 URLRequest* request = *it2;
152 request->Cancel();
153 request_to_socket_io_.erase(request);
154 request_to_buffer_io_.erase(request);
155 delete request;
156 }
157 socket_to_requests_io_.erase(socket);
158 }
159
160 ChromeThread::PostTask(
161 ChromeThread::UI,
162 FROM_HERE,
163 NewRunnableMethod(
164 this,
165 &DevToolsHttpProtocolHandler::OnCloseUI,
166 socket));
167 }
168
169 void DevToolsHttpProtocolHandler::OnHttpRequestUI(
170 HttpListenSocket* socket,
171 const HttpServerRequestInfo& info) {
172 std::string response = "<html><body>";
173 for (BrowserList::const_iterator it = BrowserList::begin(),
174 end = BrowserList::end(); it != end; ++it) {
175 TabStripModel* model = (*it)->tabstrip_model();
176 for (int i = 0, size = model->count(); i < size; ++i) {
177 TabContents* tab_contents = model->GetTabContentsAt(i);
178 NavigationController& controller = tab_contents->controller();
179 NavigationEntry* entry = controller.GetActiveEntry();
180 if (entry == NULL)
181 continue;
182
183 if (!entry->url().is_valid())
184 continue;
185
186 DevToolsClientHost* client_host = DevToolsManager::GetInstance()->
187 GetDevToolsClientHostFor(tab_contents->render_view_host());
188 if (!client_host) {
189 response += StringPrintf(
190 "<a href='devtools.html?page=%d'>%s (%s)</a><br>",
yurys 2010/07/22 15:12:42 could you send raw data and format them on the cli
pfeldman 2010/07/26 12:10:57 So far I am using this handler for local testing.
191 controller.session_id().id(),
192 UTF16ToUTF8(entry->title()).c_str(),
193 entry->url().spec().c_str());
194 } else {
195 response += StringPrintf(
196 "%s (%s)<br>",
197 UTF16ToUTF8(entry->title()).c_str(),
198 entry->url().spec().c_str());
199 }
200 }
201 }
202 response += "</body></html>";
203 Send200(socket, response, "text/html");
204 }
205
206 void DevToolsHttpProtocolHandler::OnWebSocketRequestUI(
207 HttpListenSocket* socket,
208 const HttpServerRequestInfo& request) {
209 std::string prefix = "/devtools/page/";
210 size_t pos = request.path.find(prefix);
211 if (pos != 0) {
212 Send404(socket);
213 return;
214 }
215 std::string page_id = request.path.substr(prefix.length());
216 int id = 0;
217 if (!StringToInt(page_id, &id)) {
218 Send500(socket, "Invalid page id: " + page_id);
219 return;
220 }
221
222 TabContents* tab_contents = GetTabContents(id);
223 if (tab_contents == NULL) {
224 Send500(socket, "No such page id: " + page_id);
225 return;
226 }
227
228 DevToolsManager* manager = DevToolsManager::GetInstance();
229 if (manager->GetDevToolsClientHostFor(tab_contents->render_view_host())) {
230 Send500(socket, "Page with given id is being inspected: " + page_id);
231 return;
232 }
233
234 DevToolsClientHostImpl* client_host = new DevToolsClientHostImpl(socket);
235 socket_to_client_host_ui_[socket] = client_host;
236
237 manager->RegisterDevToolsClientHostFor(
238 tab_contents->render_view_host(),
239 client_host);
240 AcceptWebSocket(socket, request);
241 }
242
112 void DevToolsHttpProtocolHandler::OnWebSocketMessageUI( 243 void DevToolsHttpProtocolHandler::OnWebSocketMessageUI(
113 HttpListenSocket* socket, 244 HttpListenSocket* socket,
114 const std::string& d) { 245 const std::string& d) {
246 SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
247 if (it == socket_to_client_host_ui_.end())
248 return;
249
115 std::string data = d; 250 std::string data = d;
116 if (!client_host_.get() && data == "attach") { 251 // TODO(pfeldman): Replace with proper parsing / dispatching.
117 client_host_.reset(new DevToolsClientHostImpl(socket)); 252 DevToolsMessageData message_data;
118 BrowserList::const_iterator it = BrowserList::begin(); 253 message_data.class_name = "ToolsAgent";
119 TabContents* tab_contents = (*it)->tabstrip_model()->GetTabContentsAt(0); 254 message_data.method_name = "dispatchOnInspectorController";
120 DevToolsManager* manager = DevToolsManager::GetInstance();
121 manager->RegisterDevToolsClientHostFor(tab_contents->render_view_host(),
122 client_host_.get());
123 } else {
124 // TODO(pfeldman): Replace with proper parsing / dispatching.
125 DevToolsMessageData message_data;
126 message_data.class_name = "ToolsAgent";
127 message_data.method_name = "dispatchOnInspectorController";
128 255
129 size_t pos = data.find(" "); 256 size_t pos = data.find(" ");
130 message_data.arguments.push_back(data.substr(0, pos)); 257 message_data.arguments.push_back(data.substr(0, pos));
131 data = data.substr(pos + 1); 258 data = data.substr(pos + 1);
132 259
133 pos = data.find(" "); 260 pos = data.find(" ");
134 message_data.arguments.push_back(data.substr(0, pos)); 261 message_data.arguments.push_back(data.substr(0, pos));
135 data = data.substr(pos + 1); 262 data = data.substr(pos + 1);
136 263
137 message_data.arguments.push_back(data); 264 message_data.arguments.push_back(data);
138 265
139 DevToolsManager* manager = DevToolsManager::GetInstance(); 266 DevToolsManager* manager = DevToolsManager::GetInstance();
140 manager->ForwardToDevToolsAgent(client_host_.get(), 267 manager->ForwardToDevToolsAgent(it->second,
141 DevToolsAgentMsg_RpcMessage(DevToolsMessageData(message_data))); 268 DevToolsAgentMsg_RpcMessage(DevToolsMessageData(message_data)));
142 }
143 } 269 }
144 270
145 void DevToolsHttpProtocolHandler::OnClose(HttpListenSocket* socket) { 271 void DevToolsHttpProtocolHandler::OnCloseUI(HttpListenSocket* socket) {
146 SocketToRequestsMap::iterator it = socket_to_requests_.find(socket); 272 SocketToClientHostMap::iterator it = socket_to_client_host_ui_.find(socket);
147 if (it == socket_to_requests_.end()) 273 if (it == socket_to_client_host_ui_.end())
148 return; 274 return;
149 275 DevToolsClientHostImpl* client_host =
150 for (std::set<URLRequest*>::iterator it2 = it->second.begin(); 276 static_cast<DevToolsClientHostImpl*>(it->second);
151 it2 != it->second.end(); ++it2) { 277 client_host->NotifyCloseListener();
152 URLRequest* request = *it2; 278 delete client_host;
153 request->Cancel(); 279 socket_to_client_host_ui_.erase(socket);
154 request_to_socket_.erase(request);
155 request_to_buffer_.erase(request);
156 delete request;
157 }
158 socket_to_requests_.erase(socket);
159 } 280 }
160 281
161 void DevToolsHttpProtocolHandler::OnResponseStarted(URLRequest* request) { 282 void DevToolsHttpProtocolHandler::OnResponseStarted(URLRequest* request) {
162 RequestToSocketMap::iterator it = request_to_socket_.find(request); 283 RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
163 if (it == request_to_socket_.end()) 284 if (it == request_to_socket_io_.end())
164 return; 285 return;
165 286
166 HttpListenSocket* socket = it->second; 287 HttpListenSocket* socket = it->second;
167 288
168 int expected_size = static_cast<int>(request->GetExpectedContentSize()); 289 int expected_size = static_cast<int>(request->GetExpectedContentSize());
169 290
170 std::string content_type; 291 std::string content_type;
171 request->GetMimeType(&content_type); 292 request->GetMimeType(&content_type);
172 293
173 if (request->status().is_success()) { 294 if (request->status().is_success()) {
174 socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n" 295 socket->Send(StringPrintf("HTTP/1.1 200 OK\r\n"
175 "Content-Type:%s\r\n" 296 "Content-Type:%s\r\n"
176 "Content-Length:%d\r\n" 297 "Content-Length:%d\r\n"
177 "\r\n", 298 "\r\n",
178 content_type.c_str(), 299 content_type.c_str(),
179 expected_size)); 300 expected_size));
180 } else { 301 } else {
181 socket->Send("HTTP/1.1 404 Not Found\r\n" 302 socket->Send404();
182 "Content-Length: 0\r\n"
183 "\r\n");
184 } 303 }
185 304
186 int bytes_read = 0; 305 int bytes_read = 0;
187 // Some servers may treat HEAD requests as GET requests. To free up the 306 // Some servers may treat HEAD requests as GET requests. To free up the
188 // network connection as soon as possible, signal that the request has 307 // network connection as soon as possible, signal that the request has
189 // completed immediately, without trying to read any data back (all we care 308 // completed immediately, without trying to read any data back (all we care
190 // about is the response code and headers, which we already have). 309 // about is the response code and headers, which we already have).
191 net::IOBuffer* buffer = request_to_buffer_[request].get(); 310 net::IOBuffer* buffer = request_to_buffer_io_[request].get();
192 if (request->status().is_success()) 311 if (request->status().is_success())
193 request->Read(buffer, kBufferSize, &bytes_read); 312 request->Read(buffer, kBufferSize, &bytes_read);
194 OnReadCompleted(request, bytes_read); 313 OnReadCompleted(request, bytes_read);
195 } 314 }
196 315
197 void DevToolsHttpProtocolHandler::OnReadCompleted(URLRequest* request, 316 void DevToolsHttpProtocolHandler::OnReadCompleted(URLRequest* request,
198 int bytes_read) { 317 int bytes_read) {
199 RequestToSocketMap::iterator it = request_to_socket_.find(request); 318 RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
200 if (it == request_to_socket_.end()) 319 if (it == request_to_socket_io_.end())
201 return; 320 return;
202 321
203 HttpListenSocket* socket = it->second; 322 HttpListenSocket* socket = it->second;
204 323
205 net::IOBuffer* buffer = request_to_buffer_[request].get(); 324 net::IOBuffer* buffer = request_to_buffer_io_[request].get();
206 do { 325 do {
207 if (!request->status().is_success() || bytes_read <= 0) 326 if (!request->status().is_success() || bytes_read <= 0)
208 break; 327 break;
209 socket->Send(buffer->data(), bytes_read); 328 socket->Send(buffer->data(), bytes_read);
210 } while (request->Read(buffer, kBufferSize, &bytes_read)); 329 } while (request->Read(buffer, kBufferSize, &bytes_read));
211 330
212 // See comments re: HEAD requests in OnResponseStarted(). 331 // See comments re: HEAD requests in OnResponseStarted().
213 if (!request->status().is_io_pending()) 332 if (!request->status().is_io_pending())
214 RequestCompleted(request); 333 RequestCompleted(request);
215 } 334 }
216 335
217 DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port) 336 DevToolsHttpProtocolHandler::DevToolsHttpProtocolHandler(int port)
218 : port_(port), 337 : port_(port),
219 server_(NULL) { 338 server_(NULL) {
220 } 339 }
221 340
222 void DevToolsHttpProtocolHandler::Init() { 341 void DevToolsHttpProtocolHandler::Init() {
223 server_ = HttpListenSocket::Listen("127.0.0.1", port_, this); 342 server_ = HttpListenSocket::Listen("127.0.0.1", port_, this);
224 } 343 }
225 344
226 // Run on I/O thread 345 // Run on I/O thread
227 void DevToolsHttpProtocolHandler::Teardown() { 346 void DevToolsHttpProtocolHandler::Teardown() {
228 server_ = NULL; 347 server_ = NULL;
229 } 348 }
230 349
231 void DevToolsHttpProtocolHandler::Bind(URLRequest* request, 350 void DevToolsHttpProtocolHandler::Bind(URLRequest* request,
232 HttpListenSocket* socket) { 351 HttpListenSocket* socket) {
233 request_to_socket_[request] = socket; 352 request_to_socket_io_[request] = socket;
234 SocketToRequestsMap::iterator it = socket_to_requests_.find(socket); 353 SocketToRequestsMap::iterator it = socket_to_requests_io_.find(socket);
235 if (it == socket_to_requests_.end()) { 354 if (it == socket_to_requests_io_.end()) {
236 std::pair<HttpListenSocket*, std::set<URLRequest*> > value( 355 std::pair<HttpListenSocket*, std::set<URLRequest*> > value(
237 socket, 356 socket,
238 std::set<URLRequest*>()); 357 std::set<URLRequest*>());
239 it = socket_to_requests_.insert(value).first; 358 it = socket_to_requests_io_.insert(value).first;
240 } 359 }
241 it->second.insert(request); 360 it->second.insert(request);
242 request_to_buffer_[request] = new net::IOBuffer(kBufferSize); 361 request_to_buffer_io_[request] = new net::IOBuffer(kBufferSize);
243 } 362 }
244 363
245 void DevToolsHttpProtocolHandler::RequestCompleted(URLRequest* request) { 364 void DevToolsHttpProtocolHandler::RequestCompleted(URLRequest* request) {
246 RequestToSocketMap::iterator it = request_to_socket_.find(request); 365 RequestToSocketMap::iterator it = request_to_socket_io_.find(request);
247 if (it == request_to_socket_.end()) 366 if (it == request_to_socket_io_.end())
248 return; 367 return;
249 368
250 HttpListenSocket* socket = it->second; 369 HttpListenSocket* socket = it->second;
251 request_to_socket_.erase(request); 370 request_to_socket_io_.erase(request);
252 SocketToRequestsMap::iterator it2 = socket_to_requests_.find(socket); 371 SocketToRequestsMap::iterator it2 = socket_to_requests_io_.find(socket);
253 it2->second.erase(request); 372 it2->second.erase(request);
254 request_to_buffer_.erase(request); 373 request_to_buffer_io_.erase(request);
255 delete request; 374 delete request;
256 } 375 }
376
377 void DevToolsHttpProtocolHandler::Send200(HttpListenSocket* socket,
378 const std::string& data,
379 const std::string& mime_type) {
380 ChromeThread::PostTask(
381 ChromeThread::IO, FROM_HERE,
382 NewRunnableMethod(socket,
383 &HttpListenSocket::Send200,
384 data,
385 mime_type));
386 }
387
388 void DevToolsHttpProtocolHandler::Send404(HttpListenSocket* socket) {
389 ChromeThread::PostTask(
390 ChromeThread::IO, FROM_HERE,
391 NewRunnableMethod(socket,
392 &HttpListenSocket::Send404));
393 }
394
395 void DevToolsHttpProtocolHandler::Send500(HttpListenSocket* socket,
396 const std::string& message) {
397 ChromeThread::PostTask(
398 ChromeThread::IO, FROM_HERE,
399 NewRunnableMethod(socket,
400 &HttpListenSocket::Send500,
401 message));
402 }
403
404 void DevToolsHttpProtocolHandler::AcceptWebSocket(
405 HttpListenSocket* socket,
406 const HttpServerRequestInfo& request) {
407 ChromeThread::PostTask(
408 ChromeThread::IO, FROM_HERE,
409 NewRunnableMethod(socket,
410 &HttpListenSocket::AcceptWebSocket,
411 request));
412 }
413
414 TabContents* DevToolsHttpProtocolHandler::GetTabContents(int session_id) {
415 for (BrowserList::const_iterator it = BrowserList::begin(),
416 end = BrowserList::end(); it != end; ++it) {
417 TabStripModel* model = (*it)->tabstrip_model();
418 for (int i = 0, size = model->count(); i < size; ++i) {
419 NavigationController& controller =
420 model->GetTabContentsAt(i)->controller();
421 if (controller.session_id().id() == session_id)
yurys 2010/07/22 15:12:42 the session id will change on navigation, won't it
pfeldman 2010/07/26 12:10:57 These are persistent!
422 return controller.tab_contents();
423 }
424 }
425 return NULL;
426 }
OLDNEW
« no previous file with comments | « chrome/browser/debugger/devtools_http_protocol_handler.h ('k') | net/server/http_listen_socket.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698