| Index: chrome/test/webdriver/webdriver_dispatch.cc
|
| diff --git a/chrome/test/webdriver/webdriver_dispatch.cc b/chrome/test/webdriver/webdriver_dispatch.cc
|
| deleted file mode 100644
|
| index d7fd78314f5432c1e0ef6ed1f894d6ba1d927c3b..0000000000000000000000000000000000000000
|
| --- a/chrome/test/webdriver/webdriver_dispatch.cc
|
| +++ /dev/null
|
| @@ -1,406 +0,0 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#include "chrome/test/webdriver/webdriver_dispatch.h"
|
| -
|
| -#include <sstream>
|
| -#include <string>
|
| -#include <vector>
|
| -
|
| -#include "base/command_line.h"
|
| -#include "base/format_macros.h"
|
| -#include "base/json/json_reader.h"
|
| -#include "base/logging.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/message_loop/message_loop_proxy.h"
|
| -#include "base/strings/string_split.h"
|
| -#include "base/strings/string_util.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/synchronization/waitable_event.h"
|
| -#include "base/sys_info.h"
|
| -#include "base/threading/platform_thread.h"
|
| -#include "base/threading/thread.h"
|
| -#include "chrome/common/chrome_version_info.h"
|
| -#include "chrome/test/webdriver/commands/command.h"
|
| -#include "chrome/test/webdriver/http_response.h"
|
| -#include "chrome/test/webdriver/webdriver_logging.h"
|
| -#include "chrome/test/webdriver/webdriver_session_manager.h"
|
| -#include "chrome/test/webdriver/webdriver_switches.h"
|
| -#include "chrome/test/webdriver/webdriver_util.h"
|
| -
|
| -namespace webdriver {
|
| -
|
| -namespace {
|
| -
|
| -// Maximum safe size of HTTP response message. Any larger than this,
|
| -// the message may not be transferred at all.
|
| -const size_t kMaxHttpMessageSize = 1024 * 1024 * 16; // 16MB
|
| -
|
| -void ReadRequestBody(const struct mg_request_info* const request_info,
|
| - struct mg_connection* const connection,
|
| - std::string* request_body) {
|
| - int content_length = 0;
|
| - // 64 maximum header count hard-coded in mongoose.h
|
| - for (int header_index = 0; header_index < 64; ++header_index) {
|
| - if (request_info->http_headers[header_index].name == NULL) {
|
| - break;
|
| - }
|
| - if (strcmp(request_info->http_headers[header_index].name,
|
| - "Content-Length") == 0) {
|
| - content_length = atoi(request_info->http_headers[header_index].value);
|
| - break;
|
| - }
|
| - }
|
| - if (content_length > 0) {
|
| - request_body->resize(content_length);
|
| - int bytes_read = 0;
|
| - while (bytes_read < content_length) {
|
| - bytes_read += mg_read(connection,
|
| - &(*request_body)[bytes_read],
|
| - content_length - bytes_read);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void WriteHttpResponse(struct mg_connection* connection,
|
| - const HttpResponse& response) {
|
| - HttpResponse modified_response(response);
|
| - if (!CommandLine::ForCurrentProcess()->HasSwitch(kEnableKeepAlive))
|
| - modified_response.AddHeader("connection", "close");
|
| - std::string data;
|
| - modified_response.GetData(&data);
|
| - mg_write(connection, data.data(), data.length());
|
| -}
|
| -
|
| -void DispatchCommand(Command* const command,
|
| - const std::string& method,
|
| - Response* response) {
|
| - if (!command->Init(response))
|
| - return;
|
| -
|
| - if (method == "POST") {
|
| - command->ExecutePost(response);
|
| - } else if (method == "GET") {
|
| - command->ExecuteGet(response);
|
| - } else if (method == "DELETE") {
|
| - command->ExecuteDelete(response);
|
| - } else {
|
| - NOTREACHED();
|
| - }
|
| - command->Finish(response);
|
| -}
|
| -
|
| -void SendOkWithBody(struct mg_connection* connection,
|
| - const std::string& content) {
|
| - HttpResponse response;
|
| - response.set_body(content);
|
| - WriteHttpResponse(connection, response);
|
| -}
|
| -
|
| -void Shutdown(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - base::WaitableEvent* shutdown_event =
|
| - reinterpret_cast<base::WaitableEvent*>(user_data);
|
| - WriteHttpResponse(connection, HttpResponse());
|
| - shutdown_event->Signal();
|
| -}
|
| -
|
| -void SendStatus(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - chrome::VersionInfo version_info;
|
| - base::DictionaryValue* build_info = new base::DictionaryValue;
|
| - build_info->SetString("time",
|
| - base::StringPrintf("%s %s PST", __DATE__, __TIME__));
|
| - build_info->SetString("version", version_info.Version());
|
| - build_info->SetString("revision", version_info.LastChange());
|
| -
|
| - base::DictionaryValue* os_info = new base::DictionaryValue;
|
| - os_info->SetString("name", base::SysInfo::OperatingSystemName());
|
| - os_info->SetString("version", base::SysInfo::OperatingSystemVersion());
|
| - os_info->SetString("arch", base::SysInfo::OperatingSystemArchitecture());
|
| -
|
| - base::DictionaryValue* status = new base::DictionaryValue;
|
| - status->Set("build", build_info);
|
| - status->Set("os", os_info);
|
| -
|
| - Response response;
|
| - response.SetStatus(kSuccess);
|
| - response.SetValue(status); // Assumes ownership of |status|.
|
| -
|
| - internal::SendResponse(connection,
|
| - request_info->request_method,
|
| - response);
|
| -}
|
| -
|
| -void SendLog(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - std::string content, log;
|
| - if (FileLog::Get()->GetLogContents(&log)) {
|
| - content = "START ChromeDriver log";
|
| - const size_t kMaxSizeWithoutHeaders = kMaxHttpMessageSize - 10000;
|
| - if (log.size() > kMaxSizeWithoutHeaders) {
|
| - log = log.substr(log.size() - kMaxSizeWithoutHeaders);
|
| - content += " (only last several MB)";
|
| - }
|
| - content += ":\n" + log + "END ChromeDriver log";
|
| - } else {
|
| - content = "No ChromeDriver log found";
|
| - }
|
| - SendOkWithBody(connection, content);
|
| -}
|
| -
|
| -void SimulateHang(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - base::PlatformThread::Sleep(base::TimeDelta::FromMinutes(5));
|
| -}
|
| -
|
| -void SendNoContentResponse(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - WriteHttpResponse(connection, HttpResponse(HttpResponse::kNoContent));
|
| -}
|
| -
|
| -void SendForbidden(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - WriteHttpResponse(connection, HttpResponse(HttpResponse::kForbidden));
|
| -}
|
| -
|
| -void SendNotImplementedError(struct mg_connection* connection,
|
| - const struct mg_request_info* request_info,
|
| - void* user_data) {
|
| - // Send a well-formed WebDriver JSON error response to ensure clients
|
| - // handle it correctly.
|
| - std::string body = base::StringPrintf(
|
| - "{\"status\":%d,\"value\":{\"message\":"
|
| - "\"Command has not been implemented yet: %s %s\"}}",
|
| - kUnknownCommand, request_info->request_method, request_info->uri);
|
| -
|
| - HttpResponse response(HttpResponse::kNotImplemented);
|
| - response.AddHeader("Content-Type", "application/json");
|
| - response.set_body(body);
|
| - WriteHttpResponse(connection, response);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -namespace internal {
|
| -
|
| -void PrepareHttpResponse(const Response& command_response,
|
| - HttpResponse* const http_response) {
|
| - ErrorCode status = command_response.GetStatus();
|
| - switch (status) {
|
| - case kSuccess:
|
| - http_response->set_status(HttpResponse::kOk);
|
| - break;
|
| -
|
| - // TODO(jleyba): kSeeOther, kBadRequest, kSessionNotFound,
|
| - // and kMethodNotAllowed should be detected before creating
|
| - // a command_response, and should thus not need conversion.
|
| - case kSeeOther: {
|
| - const base::Value* const value = command_response.GetValue();
|
| - std::string location;
|
| - if (!value->GetAsString(&location)) {
|
| - // This should never happen.
|
| - http_response->set_status(HttpResponse::kInternalServerError);
|
| - http_response->set_body("Unable to set 'Location' header: response "
|
| - "value is not a string: " +
|
| - command_response.ToJSON());
|
| - return;
|
| - }
|
| - http_response->AddHeader("Location", location);
|
| - http_response->set_status(HttpResponse::kSeeOther);
|
| - break;
|
| - }
|
| -
|
| - case kBadRequest:
|
| - case kSessionNotFound:
|
| - http_response->set_status(status);
|
| - break;
|
| -
|
| - case kMethodNotAllowed: {
|
| - const base::Value* const value = command_response.GetValue();
|
| - if (!value->IsType(base::Value::TYPE_LIST)) {
|
| - // This should never happen.
|
| - http_response->set_status(HttpResponse::kInternalServerError);
|
| - http_response->set_body(
|
| - "Unable to set 'Allow' header: response value was "
|
| - "not a list of strings: " + command_response.ToJSON());
|
| - return;
|
| - }
|
| -
|
| - const base::ListValue* const list_value =
|
| - static_cast<const base::ListValue* const>(value);
|
| - std::vector<std::string> allowed_methods;
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - std::string method;
|
| - if (list_value->GetString(i, &method)) {
|
| - allowed_methods.push_back(method);
|
| - } else {
|
| - // This should never happen.
|
| - http_response->set_status(HttpResponse::kInternalServerError);
|
| - http_response->set_body(
|
| - "Unable to set 'Allow' header: response value was "
|
| - "not a list of strings: " + command_response.ToJSON());
|
| - return;
|
| - }
|
| - }
|
| - http_response->AddHeader("Allow", JoinString(allowed_methods, ','));
|
| - http_response->set_status(HttpResponse::kMethodNotAllowed);
|
| - break;
|
| - }
|
| -
|
| - // All other errors should be treated as generic 500s. The client
|
| - // will be responsible for inspecting the message body for details.
|
| - case kInternalServerError:
|
| - default:
|
| - http_response->set_status(HttpResponse::kInternalServerError);
|
| - break;
|
| - }
|
| -
|
| - http_response->SetMimeType("application/json; charset=utf-8");
|
| - http_response->set_body(command_response.ToJSON());
|
| -}
|
| -
|
| -void SendResponse(struct mg_connection* const connection,
|
| - const std::string& request_method,
|
| - const Response& response) {
|
| - HttpResponse http_response;
|
| - PrepareHttpResponse(response, &http_response);
|
| - WriteHttpResponse(connection, http_response);
|
| -}
|
| -
|
| -bool ParseRequestInfo(const struct mg_request_info* const request_info,
|
| - struct mg_connection* const connection,
|
| - std::string* method,
|
| - std::vector<std::string>* path_segments,
|
| - base::DictionaryValue** parameters,
|
| - Response* const response) {
|
| - *method = request_info->request_method;
|
| - if (*method == "HEAD")
|
| - *method = "GET";
|
| - else if (*method == "PUT")
|
| - *method = "POST";
|
| -
|
| - std::string uri(request_info->uri);
|
| - SessionManager* manager = SessionManager::GetInstance();
|
| - uri = uri.substr(manager->url_base().length());
|
| -
|
| - base::SplitString(uri, '/', path_segments);
|
| -
|
| - if (*method == "POST") {
|
| - std::string json;
|
| - ReadRequestBody(request_info, connection, &json);
|
| - if (json.length() > 0) {
|
| - std::string error_msg;
|
| - scoped_ptr<base::Value> params(base::JSONReader::ReadAndReturnError(
|
| - json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error_msg));
|
| - if (!params.get()) {
|
| - response->SetError(new Error(
|
| - kBadRequest,
|
| - "Failed to parse command data: " + error_msg +
|
| - "\n Data: " + json));
|
| - return false;
|
| - }
|
| - if (!params->IsType(base::Value::TYPE_DICTIONARY)) {
|
| - response->SetError(new Error(
|
| - kBadRequest,
|
| - "Data passed in URL must be a dictionary. Data: " + json));
|
| - return false;
|
| - }
|
| - *parameters = static_cast<base::DictionaryValue*>(params.release());
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -void DispatchHelper(Command* command_ptr,
|
| - const std::string& method,
|
| - Response* response) {
|
| - CHECK(method == "GET" || method == "POST" || method == "DELETE");
|
| - scoped_ptr<Command> command(command_ptr);
|
| -
|
| - if ((method == "GET" && !command->DoesGet()) ||
|
| - (method == "POST" && !command->DoesPost()) ||
|
| - (method == "DELETE" && !command->DoesDelete())) {
|
| - base::ListValue* methods = new base::ListValue;
|
| - if (command->DoesPost())
|
| - methods->Append(new base::StringValue("POST"));
|
| - if (command->DoesGet()) {
|
| - methods->Append(new base::StringValue("GET"));
|
| - methods->Append(new base::StringValue("HEAD"));
|
| - }
|
| - if (command->DoesDelete())
|
| - methods->Append(new base::StringValue("DELETE"));
|
| - response->SetStatus(kMethodNotAllowed);
|
| - response->SetValue(methods);
|
| - return;
|
| - }
|
| -
|
| - DispatchCommand(command.get(), method, response);
|
| -}
|
| -
|
| -} // namespace internal
|
| -
|
| -Dispatcher::Dispatcher(const std::string& url_base)
|
| - : url_base_(url_base) {
|
| - // Overwrite mongoose's default handler for /favicon.ico to always return a
|
| - // 204 response so we don't spam the logs with 404s.
|
| - AddCallback("/favicon.ico", &SendNoContentResponse, NULL);
|
| - AddCallback("/hang", &SimulateHang, NULL);
|
| -}
|
| -
|
| -Dispatcher::~Dispatcher() {}
|
| -
|
| -void Dispatcher::AddShutdown(const std::string& pattern,
|
| - base::WaitableEvent* shutdown_event) {
|
| - AddCallback(url_base_ + pattern, &Shutdown, shutdown_event);
|
| -}
|
| -
|
| -void Dispatcher::AddStatus(const std::string& pattern) {
|
| - AddCallback(url_base_ + pattern, &SendStatus, NULL);
|
| -}
|
| -
|
| -void Dispatcher::AddLog(const std::string& pattern) {
|
| - AddCallback(url_base_ + pattern, &SendLog, NULL);
|
| -}
|
| -
|
| -void Dispatcher::SetNotImplemented(const std::string& pattern) {
|
| - AddCallback(url_base_ + pattern, &SendNotImplementedError, NULL);
|
| -}
|
| -
|
| -void Dispatcher::ForbidAllOtherRequests() {
|
| - AddCallback("*", &SendForbidden, NULL);
|
| -}
|
| -
|
| -void Dispatcher::AddCallback(const std::string& uri_pattern,
|
| - webdriver::mongoose::HttpCallback callback,
|
| - void* user_data) {
|
| - callbacks_.push_back(webdriver::mongoose::CallbackDetails(
|
| - uri_pattern,
|
| - callback,
|
| - user_data));
|
| -}
|
| -
|
| -
|
| -bool Dispatcher::ProcessHttpRequest(
|
| - struct mg_connection* connection,
|
| - const struct mg_request_info* request_info) {
|
| - std::vector<webdriver::mongoose::CallbackDetails>::const_iterator callback;
|
| - for (callback = callbacks_.begin();
|
| - callback < callbacks_.end();
|
| - ++callback) {
|
| - if (MatchPattern(request_info->uri, callback->uri_regex_)) {
|
| - callback->func_(connection, request_info, callback->user_data_);
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -} // namespace webdriver
|
|
|