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