http_transport_fake.cc revision 9ed0cab99f18acb3570a35e9408f24355f6b8324
1// Copyright 2014 The Chromium OS 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 <brillo/http/http_transport_fake.h> 6 7#include <utility> 8 9#include <base/json/json_reader.h> 10#include <base/json/json_writer.h> 11#include <base/logging.h> 12#include <brillo/bind_lambda.h> 13#include <brillo/http/http_connection_fake.h> 14#include <brillo/http/http_request.h> 15#include <brillo/mime_utils.h> 16#include <brillo/streams/memory_stream.h> 17#include <brillo/strings/string_utils.h> 18#include <brillo/url_utils.h> 19 20namespace brillo { 21 22using http::fake::Transport; 23using http::fake::ServerRequestResponseBase; 24using http::fake::ServerRequest; 25using http::fake::ServerResponse; 26 27Transport::Transport() { 28 VLOG(1) << "fake::Transport created"; 29} 30 31Transport::~Transport() { 32 VLOG(1) << "fake::Transport destroyed"; 33} 34 35std::shared_ptr<http::Connection> Transport::CreateConnection( 36 const std::string& url, 37 const std::string& method, 38 const HeaderList& headers, 39 const std::string& user_agent, 40 const std::string& referer, 41 brillo::ErrorPtr* error) { 42 std::shared_ptr<http::Connection> connection; 43 if (create_connection_error_) { 44 if (error) 45 *error = std::move(create_connection_error_); 46 return connection; 47 } 48 HeaderList headers_copy = headers; 49 if (!user_agent.empty()) { 50 headers_copy.push_back( 51 std::make_pair(http::request_header::kUserAgent, user_agent)); 52 } 53 if (!referer.empty()) { 54 headers_copy.push_back( 55 std::make_pair(http::request_header::kReferer, referer)); 56 } 57 connection = 58 std::make_shared<http::fake::Connection>(url, method, shared_from_this()); 59 CHECK(connection) << "Unable to create Connection object"; 60 if (!connection->SendHeaders(headers_copy, error)) 61 connection.reset(); 62 request_count_++; 63 return connection; 64} 65 66void Transport::RunCallbackAsync(const tracked_objects::Location& from_here, 67 const base::Closure& callback) { 68 if (!async_) { 69 callback.Run(); 70 return; 71 } 72 async_callback_queue_.push(callback); 73} 74 75bool Transport::HandleOneAsyncRequest() { 76 if (async_callback_queue_.empty()) 77 return false; 78 79 base::Closure callback = async_callback_queue_.front(); 80 async_callback_queue_.pop(); 81 callback.Run(); 82 return true; 83} 84 85void Transport::HandleAllAsyncRequests() { 86 while (!async_callback_queue_.empty()) 87 HandleOneAsyncRequest(); 88} 89 90http::RequestID Transport::StartAsyncTransfer( 91 http::Connection* connection, 92 const SuccessCallback& success_callback, 93 const ErrorCallback& error_callback) { 94 // Fake transport doesn't use this method. 95 LOG(FATAL) << "This method should not be called on fake transport"; 96 return 0; 97} 98 99bool Transport::CancelRequest(RequestID request_id) { 100 return false; 101} 102 103void Transport::SetDefaultTimeout(base::TimeDelta timeout) { 104} 105 106static inline std::string GetHandlerMapKey(const std::string& url, 107 const std::string& method) { 108 return method + ":" + url; 109} 110 111void Transport::AddHandler(const std::string& url, 112 const std::string& method, 113 const HandlerCallback& handler) { 114 // Make sure we can override/replace existing handlers. 115 handlers_[GetHandlerMapKey(url, method)] = handler; 116} 117 118void Transport::AddSimpleReplyHandler(const std::string& url, 119 const std::string& method, 120 int status_code, 121 const std::string& reply_text, 122 const std::string& mime_type) { 123 auto handler = [status_code, reply_text, mime_type]( 124 const ServerRequest& request, ServerResponse* response) { 125 response->ReplyText(status_code, reply_text, mime_type); 126 }; 127 AddHandler(url, method, base::Bind(handler)); 128} 129 130Transport::HandlerCallback Transport::GetHandler( 131 const std::string& url, 132 const std::string& method) const { 133 // First try the exact combination of URL/Method 134 auto p = handlers_.find(GetHandlerMapKey(url, method)); 135 if (p != handlers_.end()) 136 return p->second; 137 // If not found, try URL/* 138 p = handlers_.find(GetHandlerMapKey(url, "*")); 139 if (p != handlers_.end()) 140 return p->second; 141 // If still not found, try */method 142 p = handlers_.find(GetHandlerMapKey("*", method)); 143 if (p != handlers_.end()) 144 return p->second; 145 // Finally, try */* 146 p = handlers_.find(GetHandlerMapKey("*", "*")); 147 return (p != handlers_.end()) ? p->second : HandlerCallback(); 148} 149 150void ServerRequestResponseBase::SetData(StreamPtr stream) { 151 data_.clear(); 152 if (stream) { 153 uint8_t buffer[1024]; 154 size_t size = 0; 155 if (stream->CanGetSize()) 156 data_.reserve(stream->GetRemainingSize()); 157 158 do { 159 CHECK(stream->ReadBlocking(buffer, sizeof(buffer), &size, nullptr)); 160 data_.insert(data_.end(), buffer, buffer + size); 161 } while (size > 0); 162 } 163} 164 165std::string ServerRequestResponseBase::GetDataAsString() const { 166 if (data_.empty()) 167 return std::string(); 168 auto chars = reinterpret_cast<const char*>(data_.data()); 169 return std::string(chars, data_.size()); 170} 171 172std::unique_ptr<base::DictionaryValue> 173ServerRequestResponseBase::GetDataAsJson() const { 174 if (brillo::mime::RemoveParameters( 175 GetHeader(request_header::kContentType)) == 176 brillo::mime::application::kJson) { 177 auto value = base::JSONReader::Read(GetDataAsString()); 178 if (value) { 179 base::DictionaryValue* dict = nullptr; 180 if (value->GetAsDictionary(&dict)) { 181 // |value| is now owned by |dict|. 182 base::IgnoreResult(value.release()); 183 return std::unique_ptr<base::DictionaryValue>(dict); 184 } 185 } 186 } 187 return std::unique_ptr<base::DictionaryValue>(); 188} 189 190std::string ServerRequestResponseBase::GetDataAsNormalizedJsonString() const { 191 std::string value; 192 // Make sure we serialize the JSON back without any pretty print so 193 // the string comparison works correctly. 194 auto json = GetDataAsJson(); 195 if (json) 196 base::JSONWriter::Write(*json, &value); 197 return value; 198} 199 200void ServerRequestResponseBase::AddHeaders(const HeaderList& headers) { 201 for (const auto& pair : headers) { 202 if (pair.second.empty()) 203 headers_.erase(pair.first); 204 else 205 headers_.insert(pair); 206 } 207} 208 209std::string ServerRequestResponseBase::GetHeader( 210 const std::string& header_name) const { 211 auto p = headers_.find(header_name); 212 return p != headers_.end() ? p->second : std::string(); 213} 214 215ServerRequest::ServerRequest(const std::string& url, const std::string& method) 216 : method_(method) { 217 auto params = brillo::url::GetQueryStringParameters(url); 218 url_ = brillo::url::RemoveQueryString(url, true); 219 form_fields_.insert(params.begin(), params.end()); 220} 221 222std::string ServerRequest::GetFormField(const std::string& field_name) const { 223 if (!form_fields_parsed_) { 224 std::string mime_type = brillo::mime::RemoveParameters( 225 GetHeader(request_header::kContentType)); 226 if (mime_type == brillo::mime::application::kWwwFormUrlEncoded && 227 !GetData().empty()) { 228 auto fields = brillo::data_encoding::WebParamsDecode(GetDataAsString()); 229 form_fields_.insert(fields.begin(), fields.end()); 230 } 231 form_fields_parsed_ = true; 232 } 233 auto p = form_fields_.find(field_name); 234 return p != form_fields_.end() ? p->second : std::string(); 235} 236 237void ServerResponse::Reply(int status_code, 238 const void* data, 239 size_t data_size, 240 const std::string& mime_type) { 241 data_.clear(); 242 status_code_ = status_code; 243 SetData(MemoryStream::OpenCopyOf(data, data_size, nullptr)); 244 AddHeaders({{response_header::kContentLength, 245 brillo::string_utils::ToString(data_size)}, 246 {response_header::kContentType, mime_type}}); 247} 248 249void ServerResponse::ReplyText(int status_code, 250 const std::string& text, 251 const std::string& mime_type) { 252 Reply(status_code, text.data(), text.size(), mime_type); 253} 254 255void ServerResponse::ReplyJson(int status_code, const base::Value* json) { 256 std::string text; 257 base::JSONWriter::WriteWithOptions( 258 *json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text); 259 std::string mime_type = 260 brillo::mime::AppendParameter(brillo::mime::application::kJson, 261 brillo::mime::parameters::kCharset, 262 "utf-8"); 263 ReplyText(status_code, text, mime_type); 264} 265 266void ServerResponse::ReplyJson(int status_code, 267 const http::FormFieldList& fields) { 268 base::DictionaryValue json; 269 for (const auto& pair : fields) { 270 json.SetString(pair.first, pair.second); 271 } 272 ReplyJson(status_code, &json); 273} 274 275std::string ServerResponse::GetStatusText() const { 276 static std::vector<std::pair<int, const char*>> status_text_map = { 277 {100, "Continue"}, 278 {101, "Switching Protocols"}, 279 {102, "Processing"}, 280 {200, "OK"}, 281 {201, "Created"}, 282 {202, "Accepted"}, 283 {203, "Non-Authoritative Information"}, 284 {204, "No Content"}, 285 {205, "Reset Content"}, 286 {206, "Partial Content"}, 287 {207, "Multi-Status"}, 288 {208, "Already Reported"}, 289 {226, "IM Used"}, 290 {300, "Multiple Choices"}, 291 {301, "Moved Permanently"}, 292 {302, "Found"}, 293 {303, "See Other"}, 294 {304, "Not Modified"}, 295 {305, "Use Proxy"}, 296 {306, "Switch Proxy"}, 297 {307, "Temporary Redirect"}, 298 {308, "Permanent Redirect"}, 299 {400, "Bad Request"}, 300 {401, "Unauthorized"}, 301 {402, "Payment Required"}, 302 {403, "Forbidden"}, 303 {404, "Not Found"}, 304 {405, "Method Not Allowed"}, 305 {406, "Not Acceptable"}, 306 {407, "Proxy Authentication Required"}, 307 {408, "Request Timeout"}, 308 {409, "Conflict"}, 309 {410, "Gone"}, 310 {411, "Length Required"}, 311 {412, "Precondition Failed"}, 312 {413, "Request Entity Too Large"}, 313 {414, "Request - URI Too Long"}, 314 {415, "Unsupported Media Type"}, 315 {429, "Too Many Requests"}, 316 {431, "Request Header Fields Too Large"}, 317 {500, "Internal Server Error"}, 318 {501, "Not Implemented"}, 319 {502, "Bad Gateway"}, 320 {503, "Service Unavailable"}, 321 {504, "Gateway Timeout"}, 322 {505, "HTTP Version Not Supported"}, 323 }; 324 325 for (const auto& pair : status_text_map) { 326 if (pair.first == status_code_) 327 return pair.second; 328 } 329 return std::string(); 330} 331 332} // namespace brillo 333