Home | History | Annotate | Download | only in http
      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 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 
     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