1 // Copyright (c) 2013 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 "chrome/test/chromedriver/server/http_handler.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/json/json_reader.h" 10 #include "base/json/json_writer.h" 11 #include "base/logging.h" // For CHECK macros. 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop_proxy.h" 15 #include "base/strings/string_split.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/sys_info.h" 19 #include "base/values.h" 20 #include "chrome/test/chromedriver/alert_commands.h" 21 #include "chrome/test/chromedriver/chrome/adb_impl.h" 22 #include "chrome/test/chromedriver/chrome/device_manager.h" 23 #include "chrome/test/chromedriver/chrome/status.h" 24 #include "chrome/test/chromedriver/net/port_server.h" 25 #include "chrome/test/chromedriver/net/url_request_context_getter.h" 26 #include "chrome/test/chromedriver/session.h" 27 #include "chrome/test/chromedriver/session_thread_map.h" 28 #include "chrome/test/chromedriver/util.h" 29 #include "chrome/test/chromedriver/version.h" 30 #include "net/server/http_server_request_info.h" 31 #include "net/server/http_server_response_info.h" 32 33 #if defined(OS_MACOSX) 34 #include "base/mac/scoped_nsautorelease_pool.h" 35 #endif 36 37 namespace { 38 39 const char kLocalStorage[] = "localStorage"; 40 const char kSessionStorage[] = "sessionStorage"; 41 const char kShutdownPath[] = "shutdown"; 42 43 void UnimplementedCommand( 44 const base::DictionaryValue& params, 45 const std::string& session_id, 46 const CommandCallback& callback) { 47 callback.Run(Status(kUnknownCommand), scoped_ptr<base::Value>(), session_id); 48 } 49 50 } // namespace 51 52 CommandMapping::CommandMapping(HttpMethod method, 53 const std::string& path_pattern, 54 const Command& command) 55 : method(method), path_pattern(path_pattern), command(command) {} 56 57 CommandMapping::~CommandMapping() {} 58 59 HttpHandler::HttpHandler(const std::string& url_base) 60 : url_base_(url_base), 61 received_shutdown_(false), 62 command_map_(new CommandMap()), 63 weak_ptr_factory_(this) {} 64 65 HttpHandler::HttpHandler( 66 const base::Closure& quit_func, 67 const scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, 68 const std::string& url_base, 69 int adb_port, 70 scoped_ptr<PortServer> port_server) 71 : quit_func_(quit_func), 72 url_base_(url_base), 73 received_shutdown_(false), 74 weak_ptr_factory_(this) { 75 #if defined(OS_MACOSX) 76 base::mac::ScopedNSAutoreleasePool autorelease_pool; 77 #endif 78 context_getter_ = new URLRequestContextGetter(io_task_runner); 79 socket_factory_ = CreateSyncWebSocketFactory(context_getter_.get()); 80 adb_.reset(new AdbImpl(io_task_runner, adb_port)); 81 device_manager_.reset(new DeviceManager(adb_.get())); 82 port_server_ = port_server.Pass(); 83 port_manager_.reset(new PortManager(12000, 13000)); 84 85 CommandMapping commands[] = { 86 CommandMapping( 87 kPost, 88 internal::kNewSessionPathPattern, 89 base::Bind(&ExecuteCreateSession, 90 &session_thread_map_, 91 WrapToCommand( 92 "InitSession", 93 base::Bind(&ExecuteInitSession, 94 InitSessionParams(context_getter_, 95 socket_factory_, 96 device_manager_.get(), 97 port_server_.get(), 98 port_manager_.get()))))), 99 CommandMapping(kGet, 100 "session/:sessionId", 101 WrapToCommand("GetSessionCapabilities", 102 base::Bind(&ExecuteGetSessionCapabilities))), 103 CommandMapping(kDelete, 104 "session/:sessionId", 105 base::Bind(&ExecuteSessionCommand, 106 &session_thread_map_, 107 "Quit", 108 base::Bind(&ExecuteQuit, false), 109 true)), 110 CommandMapping(kGet, 111 "session/:sessionId/window_handle", 112 WrapToCommand("GetWindow", 113 base::Bind(&ExecuteGetCurrentWindowHandle))), 114 CommandMapping( 115 kGet, 116 "session/:sessionId/window_handles", 117 WrapToCommand("GetWindows", base::Bind(&ExecuteGetWindowHandles))), 118 CommandMapping(kPost, 119 "session/:sessionId/url", 120 WrapToCommand("Navigate", base::Bind(&ExecuteGet))), 121 CommandMapping(kPost, 122 "session/:sessionId/chromium/launch_app", 123 WrapToCommand("LaunchApp", base::Bind(&ExecuteLaunchApp))), 124 CommandMapping(kGet, 125 "session/:sessionId/alert", 126 WrapToCommand("IsAlertOpen", 127 base::Bind(&ExecuteAlertCommand, 128 base::Bind(&ExecuteGetAlert)))), 129 CommandMapping( 130 kPost, 131 "session/:sessionId/dismiss_alert", 132 WrapToCommand("DismissAlert", 133 base::Bind(&ExecuteAlertCommand, 134 base::Bind(&ExecuteDismissAlert)))), 135 CommandMapping( 136 kPost, 137 "session/:sessionId/accept_alert", 138 WrapToCommand("AcceptAlert", 139 base::Bind(&ExecuteAlertCommand, 140 base::Bind(&ExecuteAcceptAlert)))), 141 CommandMapping( 142 kGet, 143 "session/:sessionId/alert_text", 144 WrapToCommand("GetAlertMessage", 145 base::Bind(&ExecuteAlertCommand, 146 base::Bind(&ExecuteGetAlertText)))), 147 CommandMapping( 148 kPost, 149 "session/:sessionId/alert_text", 150 WrapToCommand("SetAlertPrompt", 151 base::Bind(&ExecuteAlertCommand, 152 base::Bind(&ExecuteSetAlertValue)))), 153 CommandMapping(kPost, 154 "session/:sessionId/forward", 155 WrapToCommand("GoForward", base::Bind(&ExecuteGoForward))), 156 CommandMapping(kPost, 157 "session/:sessionId/back", 158 WrapToCommand("GoBack", base::Bind(&ExecuteGoBack))), 159 CommandMapping(kPost, 160 "session/:sessionId/refresh", 161 WrapToCommand("Refresh", base::Bind(&ExecuteRefresh))), 162 CommandMapping( 163 kPost, 164 "session/:sessionId/execute", 165 WrapToCommand("ExecuteScript", base::Bind(&ExecuteExecuteScript))), 166 CommandMapping(kPost, 167 "session/:sessionId/execute_async", 168 WrapToCommand("ExecuteAsyncScript", 169 base::Bind(&ExecuteExecuteAsyncScript))), 170 CommandMapping( 171 kGet, 172 "session/:sessionId/url", 173 WrapToCommand("GetUrl", base::Bind(&ExecuteGetCurrentUrl))), 174 CommandMapping(kGet, 175 "session/:sessionId/title", 176 WrapToCommand("GetTitle", base::Bind(&ExecuteGetTitle))), 177 CommandMapping( 178 kGet, 179 "session/:sessionId/source", 180 WrapToCommand("GetSource", base::Bind(&ExecuteGetPageSource))), 181 CommandMapping( 182 kGet, 183 "session/:sessionId/screenshot", 184 WrapToCommand("Screenshot", base::Bind(&ExecuteScreenshot))), 185 CommandMapping( 186 kGet, 187 "session/:sessionId/chromium/heap_snapshot", 188 WrapToCommand("HeapSnapshot", base::Bind(&ExecuteTakeHeapSnapshot))), 189 CommandMapping(kPost, 190 "session/:sessionId/visible", 191 base::Bind(&UnimplementedCommand)), 192 CommandMapping(kGet, 193 "session/:sessionId/visible", 194 base::Bind(&UnimplementedCommand)), 195 CommandMapping( 196 kPost, 197 "session/:sessionId/element", 198 WrapToCommand("FindElement", base::Bind(&ExecuteFindElement, 50))), 199 CommandMapping( 200 kPost, 201 "session/:sessionId/elements", 202 WrapToCommand("FindElements", base::Bind(&ExecuteFindElements, 50))), 203 CommandMapping(kPost, 204 "session/:sessionId/element/active", 205 WrapToCommand("GetActiveElement", 206 base::Bind(&ExecuteGetActiveElement))), 207 CommandMapping(kPost, 208 "session/:sessionId/element/:id/element", 209 WrapToCommand("FindChildElement", 210 base::Bind(&ExecuteFindChildElement, 50))), 211 CommandMapping(kPost, 212 "session/:sessionId/element/:id/elements", 213 WrapToCommand("FindChildElements", 214 base::Bind(&ExecuteFindChildElements, 50))), 215 CommandMapping( 216 kPost, 217 "session/:sessionId/element/:id/click", 218 WrapToCommand("ClickElement", base::Bind(&ExecuteClickElement))), 219 CommandMapping( 220 kPost, 221 "session/:sessionId/element/:id/clear", 222 WrapToCommand("ClearElement", base::Bind(&ExecuteClearElement))), 223 CommandMapping( 224 kPost, 225 "session/:sessionId/element/:id/submit", 226 WrapToCommand("SubmitElement", base::Bind(&ExecuteSubmitElement))), 227 CommandMapping( 228 kGet, 229 "session/:sessionId/element/:id/text", 230 WrapToCommand("GetElementText", base::Bind(&ExecuteGetElementText))), 231 CommandMapping( 232 kPost, 233 "session/:sessionId/element/:id/value", 234 WrapToCommand("TypeElement", base::Bind(&ExecuteSendKeysToElement))), 235 CommandMapping( 236 kPost, 237 "session/:sessionId/file", 238 WrapToCommand("UploadFile", base::Bind(&ExecuteUploadFile))), 239 CommandMapping(kGet, 240 "session/:sessionId/element/:id/value", 241 WrapToCommand("GetElementValue", 242 base::Bind(&ExecuteGetElementValue))), 243 CommandMapping(kGet, 244 "session/:sessionId/element/:id/name", 245 WrapToCommand("GetElementTagName", 246 base::Bind(&ExecuteGetElementTagName))), 247 CommandMapping(kGet, 248 "session/:sessionId/element/:id/selected", 249 WrapToCommand("IsElementSelected", 250 base::Bind(&ExecuteIsElementSelected))), 251 CommandMapping(kGet, 252 "session/:sessionId/element/:id/enabled", 253 WrapToCommand("IsElementEnabled", 254 base::Bind(&ExecuteIsElementEnabled))), 255 CommandMapping(kGet, 256 "session/:sessionId/element/:id/displayed", 257 WrapToCommand("IsElementDisplayed", 258 base::Bind(&ExecuteIsElementDisplayed))), 259 CommandMapping( 260 kPost, 261 "session/:sessionId/element/:id/hover", 262 WrapToCommand("HoverElement", base::Bind(&ExecuteHoverOverElement))), 263 CommandMapping(kGet, 264 "session/:sessionId/element/:id/location", 265 WrapToCommand("GetElementLocation", 266 base::Bind(&ExecuteGetElementLocation))), 267 CommandMapping( 268 kGet, 269 "session/:sessionId/element/:id/location_in_view", 270 WrapToCommand( 271 "GetElementLocationInView", 272 base::Bind(&ExecuteGetElementLocationOnceScrolledIntoView))), 273 CommandMapping( 274 kGet, 275 "session/:sessionId/element/:id/size", 276 WrapToCommand("GetElementSize", base::Bind(&ExecuteGetElementSize))), 277 CommandMapping(kGet, 278 "session/:sessionId/element/:id/attribute/:name", 279 WrapToCommand("GetElementAttribute", 280 base::Bind(&ExecuteGetElementAttribute))), 281 CommandMapping( 282 kGet, 283 "session/:sessionId/element/:id/equals/:other", 284 WrapToCommand("IsElementEqual", base::Bind(&ExecuteElementEquals))), 285 CommandMapping( 286 kGet, 287 "session/:sessionId/cookie", 288 WrapToCommand("GetCookies", base::Bind(&ExecuteGetCookies))), 289 CommandMapping(kPost, 290 "session/:sessionId/cookie", 291 WrapToCommand("AddCookie", base::Bind(&ExecuteAddCookie))), 292 CommandMapping(kDelete, 293 "session/:sessionId/cookie", 294 WrapToCommand("DeleteAllCookies", 295 base::Bind(&ExecuteDeleteAllCookies))), 296 CommandMapping( 297 kDelete, 298 "session/:sessionId/cookie/:name", 299 WrapToCommand("DeleteCookie", base::Bind(&ExecuteDeleteCookie))), 300 CommandMapping( 301 kPost, 302 "session/:sessionId/frame", 303 WrapToCommand("SwitchToFrame", base::Bind(&ExecuteSwitchToFrame))), 304 CommandMapping( 305 kPost, 306 "session/:sessionId/frame/parent", 307 WrapToCommand("SwitchToParentFrame", 308 base::Bind(&ExecuteSwitchToParentFrame))), 309 CommandMapping( 310 kPost, 311 "session/:sessionId/window", 312 WrapToCommand("SwitchToWindow", base::Bind(&ExecuteSwitchToWindow))), 313 CommandMapping( 314 kGet, 315 "session/:sessionId/window/:windowHandle/size", 316 WrapToCommand("GetWindowSize", base::Bind(&ExecuteGetWindowSize))), 317 CommandMapping(kGet, 318 "session/:sessionId/window/:windowHandle/position", 319 WrapToCommand("GetWindowPosition", 320 base::Bind(&ExecuteGetWindowPosition))), 321 CommandMapping( 322 kPost, 323 "session/:sessionId/window/:windowHandle/size", 324 WrapToCommand("SetWindowSize", base::Bind(&ExecuteSetWindowSize))), 325 CommandMapping(kPost, 326 "session/:sessionId/window/:windowHandle/position", 327 WrapToCommand("SetWindowPosition", 328 base::Bind(&ExecuteSetWindowPosition))), 329 CommandMapping( 330 kPost, 331 "session/:sessionId/window/:windowHandle/maximize", 332 WrapToCommand("MaximizeWindow", base::Bind(&ExecuteMaximizeWindow))), 333 CommandMapping(kDelete, 334 "session/:sessionId/window", 335 WrapToCommand("CloseWindow", base::Bind(&ExecuteClose))), 336 CommandMapping(kPost, 337 "session/:sessionId/element/:id/drag", 338 base::Bind(&UnimplementedCommand)), 339 CommandMapping( 340 kGet, 341 "session/:sessionId/element/:id/css/:propertyName", 342 WrapToCommand("GetElementCSSProperty", 343 base::Bind(&ExecuteGetElementValueOfCSSProperty))), 344 CommandMapping( 345 kPost, 346 "session/:sessionId/timeouts/implicit_wait", 347 WrapToCommand("SetImplicitWait", base::Bind(&ExecuteImplicitlyWait))), 348 CommandMapping(kPost, 349 "session/:sessionId/timeouts/async_script", 350 WrapToCommand("SetScriptTimeout", 351 base::Bind(&ExecuteSetScriptTimeout))), 352 CommandMapping( 353 kPost, 354 "session/:sessionId/timeouts", 355 WrapToCommand("SetTimeout", base::Bind(&ExecuteSetTimeout))), 356 CommandMapping(kPost, 357 "session/:sessionId/execute_sql", 358 base::Bind(&UnimplementedCommand)), 359 CommandMapping( 360 kGet, 361 "session/:sessionId/location", 362 WrapToCommand("GetGeolocation", base::Bind(&ExecuteGetLocation))), 363 CommandMapping( 364 kPost, 365 "session/:sessionId/location", 366 WrapToCommand("SetGeolocation", base::Bind(&ExecuteSetLocation))), 367 CommandMapping(kGet, 368 "session/:sessionId/application_cache/status", 369 base::Bind(&ExecuteGetStatus)), 370 CommandMapping(kGet, 371 "session/:sessionId/browser_connection", 372 base::Bind(&UnimplementedCommand)), 373 CommandMapping(kPost, 374 "session/:sessionId/browser_connection", 375 base::Bind(&UnimplementedCommand)), 376 CommandMapping( 377 kGet, 378 "session/:sessionId/local_storage/key/:key", 379 WrapToCommand("GetLocalStorageItem", 380 base::Bind(&ExecuteGetStorageItem, kLocalStorage))), 381 CommandMapping( 382 kDelete, 383 "session/:sessionId/local_storage/key/:key", 384 WrapToCommand("RemoveLocalStorageItem", 385 base::Bind(&ExecuteRemoveStorageItem, kLocalStorage))), 386 CommandMapping( 387 kGet, 388 "session/:sessionId/local_storage", 389 WrapToCommand("GetLocalStorageKeys", 390 base::Bind(&ExecuteGetStorageKeys, kLocalStorage))), 391 CommandMapping( 392 kPost, 393 "session/:sessionId/local_storage", 394 WrapToCommand("SetLocalStorageKeys", 395 base::Bind(&ExecuteSetStorageItem, kLocalStorage))), 396 CommandMapping( 397 kDelete, 398 "session/:sessionId/local_storage", 399 WrapToCommand("ClearLocalStorage", 400 base::Bind(&ExecuteClearStorage, kLocalStorage))), 401 CommandMapping( 402 kGet, 403 "session/:sessionId/local_storage/size", 404 WrapToCommand("GetLocalStorageSize", 405 base::Bind(&ExecuteGetStorageSize, kLocalStorage))), 406 CommandMapping( 407 kGet, 408 "session/:sessionId/session_storage/key/:key", 409 WrapToCommand("GetSessionStorageItem", 410 base::Bind(&ExecuteGetStorageItem, kSessionStorage))), 411 CommandMapping(kDelete, 412 "session/:sessionId/session_storage/key/:key", 413 WrapToCommand("RemoveSessionStorageItem", 414 base::Bind(&ExecuteRemoveStorageItem, 415 kSessionStorage))), 416 CommandMapping( 417 kGet, 418 "session/:sessionId/session_storage", 419 WrapToCommand("GetSessionStorageKeys", 420 base::Bind(&ExecuteGetStorageKeys, kSessionStorage))), 421 CommandMapping( 422 kPost, 423 "session/:sessionId/session_storage", 424 WrapToCommand("SetSessionStorageItem", 425 base::Bind(&ExecuteSetStorageItem, kSessionStorage))), 426 CommandMapping( 427 kDelete, 428 "session/:sessionId/session_storage", 429 WrapToCommand("ClearSessionStorage", 430 base::Bind(&ExecuteClearStorage, kSessionStorage))), 431 CommandMapping( 432 kGet, 433 "session/:sessionId/session_storage/size", 434 WrapToCommand("GetSessionStorageSize", 435 base::Bind(&ExecuteGetStorageSize, kSessionStorage))), 436 CommandMapping(kGet, 437 "session/:sessionId/orientation", 438 base::Bind(&UnimplementedCommand)), 439 CommandMapping(kPost, 440 "session/:sessionId/orientation", 441 base::Bind(&UnimplementedCommand)), 442 CommandMapping(kPost, 443 "session/:sessionId/click", 444 WrapToCommand("Click", base::Bind(&ExecuteMouseClick))), 445 CommandMapping( 446 kPost, 447 "session/:sessionId/doubleclick", 448 WrapToCommand("DoubleClick", base::Bind(&ExecuteMouseDoubleClick))), 449 CommandMapping( 450 kPost, 451 "session/:sessionId/buttondown", 452 WrapToCommand("MouseDown", base::Bind(&ExecuteMouseButtonDown))), 453 CommandMapping( 454 kPost, 455 "session/:sessionId/buttonup", 456 WrapToCommand("MouseUp", base::Bind(&ExecuteMouseButtonUp))), 457 CommandMapping( 458 kPost, 459 "session/:sessionId/moveto", 460 WrapToCommand("MouseMove", base::Bind(&ExecuteMouseMoveTo))), 461 CommandMapping( 462 kPost, 463 "session/:sessionId/keys", 464 WrapToCommand("Type", base::Bind(&ExecuteSendKeysToActiveElement))), 465 CommandMapping(kGet, 466 "session/:sessionId/ime/available_engines", 467 base::Bind(&UnimplementedCommand)), 468 CommandMapping(kGet, 469 "session/:sessionId/ime/active_engine", 470 base::Bind(&UnimplementedCommand)), 471 CommandMapping(kGet, 472 "session/:sessionId/ime/activated", 473 base::Bind(&UnimplementedCommand)), 474 CommandMapping(kPost, 475 "session/:sessionId/ime/deactivate", 476 base::Bind(&UnimplementedCommand)), 477 CommandMapping(kPost, 478 "session/:sessionId/ime/activate", 479 base::Bind(&UnimplementedCommand)), 480 CommandMapping(kPost, 481 "session/:sessionId/touch/click", 482 WrapToCommand("Tap", base::Bind(&ExecuteTouchSingleTap))), 483 CommandMapping(kPost, 484 "session/:sessionId/touch/down", 485 WrapToCommand("TouchDown", base::Bind(&ExecuteTouchDown))), 486 CommandMapping(kPost, 487 "session/:sessionId/touch/up", 488 WrapToCommand("TouchUp", base::Bind(&ExecuteTouchUp))), 489 CommandMapping(kPost, 490 "session/:sessionId/touch/move", 491 WrapToCommand("TouchMove", base::Bind(&ExecuteTouchMove))), 492 CommandMapping(kPost, 493 "session/:sessionId/touch/scroll", 494 base::Bind(&UnimplementedCommand)), 495 CommandMapping(kPost, 496 "session/:sessionId/touch/doubleclick", 497 base::Bind(&UnimplementedCommand)), 498 CommandMapping(kPost, 499 "session/:sessionId/touch/longclick", 500 base::Bind(&UnimplementedCommand)), 501 CommandMapping(kPost, 502 "session/:sessionId/touch/flick", 503 WrapToCommand("TouchFlick", base::Bind(&ExecuteFlick))), 504 CommandMapping(kPost, 505 "session/:sessionId/log", 506 WrapToCommand("GetLog", base::Bind(&ExecuteGetLog))), 507 CommandMapping(kGet, 508 "session/:sessionId/log/types", 509 WrapToCommand("GetLogTypes", 510 base::Bind(&ExecuteGetAvailableLogTypes))), 511 CommandMapping(kPost, "logs", base::Bind(&UnimplementedCommand)), 512 CommandMapping(kGet, "status", base::Bind(&ExecuteGetStatus)), 513 514 // Custom Chrome commands: 515 // Allow quit all to be called with GET or POST. 516 CommandMapping( 517 kGet, 518 kShutdownPath, 519 base::Bind(&ExecuteQuitAll, 520 WrapToCommand("QuitAll", base::Bind(&ExecuteQuit, true)), 521 &session_thread_map_)), 522 CommandMapping( 523 kPost, 524 kShutdownPath, 525 base::Bind(&ExecuteQuitAll, 526 WrapToCommand("QuitAll", base::Bind(&ExecuteQuit, true)), 527 &session_thread_map_)), 528 CommandMapping(kGet, 529 "session/:sessionId/is_loading", 530 WrapToCommand("IsLoading", base::Bind(&ExecuteIsLoading))), 531 CommandMapping(kGet, 532 "session/:sessionId/autoreport", 533 WrapToCommand("IsAutoReporting", 534 base::Bind(&ExecuteIsAutoReporting))), 535 CommandMapping(kPost, 536 "session/:sessionId/autoreport", 537 WrapToCommand( 538 "SetAutoReporting", 539 base::Bind(&ExecuteSetAutoReporting))), 540 }; 541 command_map_.reset( 542 new CommandMap(commands, commands + arraysize(commands))); 543 } 544 545 HttpHandler::~HttpHandler() {} 546 547 void HttpHandler::Handle(const net::HttpServerRequestInfo& request, 548 const HttpResponseSenderFunc& send_response_func) { 549 CHECK(thread_checker_.CalledOnValidThread()); 550 551 if (received_shutdown_) 552 return; 553 554 std::string path = request.path; 555 if (!StartsWithASCII(path, url_base_, true)) { 556 scoped_ptr<net::HttpServerResponseInfo> response( 557 new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST)); 558 response->SetBody("unhandled request", "text/plain"); 559 send_response_func.Run(response.Pass()); 560 return; 561 } 562 563 path.erase(0, url_base_.length()); 564 565 HandleCommand(request, path, send_response_func); 566 567 if (path == kShutdownPath) 568 received_shutdown_ = true; 569 } 570 571 Command HttpHandler::WrapToCommand( 572 const char* name, 573 const SessionCommand& session_command) { 574 return base::Bind(&ExecuteSessionCommand, 575 &session_thread_map_, 576 name, 577 session_command, 578 false); 579 } 580 581 Command HttpHandler::WrapToCommand( 582 const char* name, 583 const WindowCommand& window_command) { 584 return WrapToCommand(name, base::Bind(&ExecuteWindowCommand, window_command)); 585 } 586 587 Command HttpHandler::WrapToCommand( 588 const char* name, 589 const ElementCommand& element_command) { 590 return WrapToCommand(name, 591 base::Bind(&ExecuteElementCommand, element_command)); 592 } 593 594 void HttpHandler::HandleCommand( 595 const net::HttpServerRequestInfo& request, 596 const std::string& trimmed_path, 597 const HttpResponseSenderFunc& send_response_func) { 598 base::DictionaryValue params; 599 std::string session_id; 600 CommandMap::const_iterator iter = command_map_->begin(); 601 while (true) { 602 if (iter == command_map_->end()) { 603 scoped_ptr<net::HttpServerResponseInfo> response( 604 new net::HttpServerResponseInfo(net::HTTP_NOT_FOUND)); 605 response->SetBody("unknown command: " + trimmed_path, "text/plain"); 606 send_response_func.Run(response.Pass()); 607 return; 608 } 609 if (internal::MatchesCommand( 610 request.method, trimmed_path, *iter, &session_id, ¶ms)) { 611 break; 612 } 613 ++iter; 614 } 615 616 if (request.data.length()) { 617 base::DictionaryValue* body_params; 618 scoped_ptr<base::Value> parsed_body(base::JSONReader::Read(request.data)); 619 if (!parsed_body || !parsed_body->GetAsDictionary(&body_params)) { 620 scoped_ptr<net::HttpServerResponseInfo> response( 621 new net::HttpServerResponseInfo(net::HTTP_BAD_REQUEST)); 622 response->SetBody("missing command parameters", "text/plain"); 623 send_response_func.Run(response.Pass()); 624 return; 625 } 626 params.MergeDictionary(body_params); 627 } 628 629 iter->command.Run(params, 630 session_id, 631 base::Bind(&HttpHandler::PrepareResponse, 632 weak_ptr_factory_.GetWeakPtr(), 633 trimmed_path, 634 send_response_func)); 635 } 636 637 void HttpHandler::PrepareResponse( 638 const std::string& trimmed_path, 639 const HttpResponseSenderFunc& send_response_func, 640 const Status& status, 641 scoped_ptr<base::Value> value, 642 const std::string& session_id) { 643 CHECK(thread_checker_.CalledOnValidThread()); 644 scoped_ptr<net::HttpServerResponseInfo> response = 645 PrepareResponseHelper(trimmed_path, status, value.Pass(), session_id); 646 send_response_func.Run(response.Pass()); 647 if (trimmed_path == kShutdownPath) 648 quit_func_.Run(); 649 } 650 651 scoped_ptr<net::HttpServerResponseInfo> HttpHandler::PrepareResponseHelper( 652 const std::string& trimmed_path, 653 const Status& status, 654 scoped_ptr<base::Value> value, 655 const std::string& session_id) { 656 if (status.code() == kUnknownCommand) { 657 scoped_ptr<net::HttpServerResponseInfo> response( 658 new net::HttpServerResponseInfo(net::HTTP_NOT_IMPLEMENTED)); 659 response->SetBody("unimplemented command: " + trimmed_path, "text/plain"); 660 return response.Pass(); 661 } 662 663 if (status.IsError()) { 664 Status full_status(status); 665 full_status.AddDetails(base::StringPrintf( 666 "Driver info: chromedriver=%s,platform=%s %s %s", 667 kChromeDriverVersion, 668 base::SysInfo::OperatingSystemName().c_str(), 669 base::SysInfo::OperatingSystemVersion().c_str(), 670 base::SysInfo::OperatingSystemArchitecture().c_str())); 671 scoped_ptr<base::DictionaryValue> error(new base::DictionaryValue()); 672 error->SetString("message", full_status.message()); 673 value.reset(error.release()); 674 } 675 if (!value) 676 value.reset(base::Value::CreateNullValue()); 677 678 base::DictionaryValue body_params; 679 body_params.SetInteger("status", status.code()); 680 body_params.Set("value", value.release()); 681 body_params.SetString("sessionId", session_id); 682 std::string body; 683 base::JSONWriter::WriteWithOptions( 684 &body_params, base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION, 685 &body); 686 scoped_ptr<net::HttpServerResponseInfo> response( 687 new net::HttpServerResponseInfo(net::HTTP_OK)); 688 response->SetBody(body, "application/json; charset=utf-8"); 689 return response.Pass(); 690 } 691 692 namespace internal { 693 694 const char kNewSessionPathPattern[] = "session"; 695 696 bool MatchesMethod(HttpMethod command_method, const std::string& method) { 697 std::string lower_method = base::StringToLowerASCII(method); 698 switch (command_method) { 699 case kGet: 700 return lower_method == "get"; 701 case kPost: 702 return lower_method == "post" || lower_method == "put"; 703 case kDelete: 704 return lower_method == "delete"; 705 } 706 return false; 707 } 708 709 bool MatchesCommand(const std::string& method, 710 const std::string& path, 711 const CommandMapping& command, 712 std::string* session_id, 713 base::DictionaryValue* out_params) { 714 if (!MatchesMethod(command.method, method)) 715 return false; 716 717 std::vector<std::string> path_parts; 718 base::SplitString(path, '/', &path_parts); 719 std::vector<std::string> command_path_parts; 720 base::SplitString(command.path_pattern, '/', &command_path_parts); 721 if (path_parts.size() != command_path_parts.size()) 722 return false; 723 724 base::DictionaryValue params; 725 for (size_t i = 0; i < path_parts.size(); ++i) { 726 CHECK(command_path_parts[i].length()); 727 if (command_path_parts[i][0] == ':') { 728 std::string name = command_path_parts[i]; 729 name.erase(0, 1); 730 CHECK(name.length()); 731 if (name == "sessionId") 732 *session_id = path_parts[i]; 733 else 734 params.SetString(name, path_parts[i]); 735 } else if (command_path_parts[i] != path_parts[i]) { 736 return false; 737 } 738 } 739 out_params->MergeDictionary(¶ms); 740 return true; 741 } 742 743 } // namespace internal 744