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