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