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_request.h>
      6 
      7 #include <base/bind.h>
      8 #include <base/callback.h>
      9 #include <base/logging.h>
     10 #include <brillo/http/http_form_data.h>
     11 #include <brillo/map_utils.h>
     12 #include <brillo/mime_utils.h>
     13 #include <brillo/streams/memory_stream.h>
     14 #include <brillo/strings/string_utils.h>
     15 
     16 namespace brillo {
     17 namespace http {
     18 
     19 // request_type
     20 const char request_type::kOptions[]               = "OPTIONS";
     21 const char request_type::kGet[]                   = "GET";
     22 const char request_type::kHead[]                  = "HEAD";
     23 const char request_type::kPost[]                  = "POST";
     24 const char request_type::kPut[]                   = "PUT";
     25 const char request_type::kPatch[]                 = "PATCH";
     26 const char request_type::kDelete[]                = "DELETE";
     27 const char request_type::kTrace[]                 = "TRACE";
     28 const char request_type::kConnect[]               = "CONNECT";
     29 const char request_type::kCopy[]                  = "COPY";
     30 const char request_type::kMove[]                  = "MOVE";
     31 
     32 // request_header
     33 const char request_header::kAccept[]              = "Accept";
     34 const char request_header::kAcceptCharset[]       = "Accept-Charset";
     35 const char request_header::kAcceptEncoding[]      = "Accept-Encoding";
     36 const char request_header::kAcceptLanguage[]      = "Accept-Language";
     37 const char request_header::kAllow[]               = "Allow";
     38 const char request_header::kAuthorization[]       = "Authorization";
     39 const char request_header::kCacheControl[]        = "Cache-Control";
     40 const char request_header::kConnection[]          = "Connection";
     41 const char request_header::kContentEncoding[]     = "Content-Encoding";
     42 const char request_header::kContentLanguage[]     = "Content-Language";
     43 const char request_header::kContentLength[]       = "Content-Length";
     44 const char request_header::kContentLocation[]     = "Content-Location";
     45 const char request_header::kContentMd5[]          = "Content-MD5";
     46 const char request_header::kContentRange[]        = "Content-Range";
     47 const char request_header::kContentType[]         = "Content-Type";
     48 const char request_header::kCookie[]              = "Cookie";
     49 const char request_header::kDate[]                = "Date";
     50 const char request_header::kExpect[]              = "Expect";
     51 const char request_header::kExpires[]             = "Expires";
     52 const char request_header::kFrom[]                = "From";
     53 const char request_header::kHost[]                = "Host";
     54 const char request_header::kIfMatch[]             = "If-Match";
     55 const char request_header::kIfModifiedSince[]     = "If-Modified-Since";
     56 const char request_header::kIfNoneMatch[]         = "If-None-Match";
     57 const char request_header::kIfRange[]             = "If-Range";
     58 const char request_header::kIfUnmodifiedSince[]   = "If-Unmodified-Since";
     59 const char request_header::kLastModified[]        = "Last-Modified";
     60 const char request_header::kMaxForwards[]         = "Max-Forwards";
     61 const char request_header::kPragma[]              = "Pragma";
     62 const char request_header::kProxyAuthorization[]  = "Proxy-Authorization";
     63 const char request_header::kRange[]               = "Range";
     64 const char request_header::kReferer[]             = "Referer";
     65 const char request_header::kTE[]                  = "TE";
     66 const char request_header::kTrailer[]             = "Trailer";
     67 const char request_header::kTransferEncoding[]    = "Transfer-Encoding";
     68 const char request_header::kUpgrade[]             = "Upgrade";
     69 const char request_header::kUserAgent[]           = "User-Agent";
     70 const char request_header::kVia[]                 = "Via";
     71 const char request_header::kWarning[]             = "Warning";
     72 
     73 // response_header
     74 const char response_header::kAcceptRanges[]       = "Accept-Ranges";
     75 const char response_header::kAge[]                = "Age";
     76 const char response_header::kAllow[]              = "Allow";
     77 const char response_header::kCacheControl[]       = "Cache-Control";
     78 const char response_header::kConnection[]         = "Connection";
     79 const char response_header::kContentEncoding[]    = "Content-Encoding";
     80 const char response_header::kContentLanguage[]    = "Content-Language";
     81 const char response_header::kContentLength[]      = "Content-Length";
     82 const char response_header::kContentLocation[]    = "Content-Location";
     83 const char response_header::kContentMd5[]         = "Content-MD5";
     84 const char response_header::kContentRange[]       = "Content-Range";
     85 const char response_header::kContentType[]        = "Content-Type";
     86 const char response_header::kDate[]               = "Date";
     87 const char response_header::kETag[]               = "ETag";
     88 const char response_header::kExpires[]            = "Expires";
     89 const char response_header::kLastModified[]       = "Last-Modified";
     90 const char response_header::kLocation[]           = "Location";
     91 const char response_header::kPragma[]             = "Pragma";
     92 const char response_header::kProxyAuthenticate[]  = "Proxy-Authenticate";
     93 const char response_header::kRetryAfter[]         = "Retry-After";
     94 const char response_header::kServer[]             = "Server";
     95 const char response_header::kSetCookie[]          = "Set-Cookie";
     96 const char response_header::kTrailer[]            = "Trailer";
     97 const char response_header::kTransferEncoding[]   = "Transfer-Encoding";
     98 const char response_header::kUpgrade[]            = "Upgrade";
     99 const char response_header::kVary[]               = "Vary";
    100 const char response_header::kVia[]                = "Via";
    101 const char response_header::kWarning[]            = "Warning";
    102 const char response_header::kWwwAuthenticate[]    = "WWW-Authenticate";
    103 
    104 // ***********************************************************
    105 // ********************** Request Class **********************
    106 // ***********************************************************
    107 Request::Request(const std::string& url,
    108                  const std::string& method,
    109                  std::shared_ptr<Transport> transport)
    110     : transport_(transport), request_url_(url), method_(method) {
    111   VLOG(1) << "http::Request created";
    112   if (!transport_)
    113     transport_ = http::Transport::CreateDefault();
    114 }
    115 
    116 Request::~Request() {
    117   VLOG(1) << "http::Request destroyed";
    118 }
    119 
    120 void Request::AddRange(int64_t bytes) {
    121   DCHECK(transport_) << "Request already sent";
    122   if (bytes < 0) {
    123     ranges_.emplace_back(Request::range_value_omitted, -bytes);
    124   } else {
    125     ranges_.emplace_back(bytes, Request::range_value_omitted);
    126   }
    127 }
    128 
    129 void Request::AddRange(uint64_t from_byte, uint64_t to_byte) {
    130   DCHECK(transport_) << "Request already sent";
    131   ranges_.emplace_back(from_byte, to_byte);
    132 }
    133 
    134 std::unique_ptr<Response> Request::GetResponseAndBlock(
    135     brillo::ErrorPtr* error) {
    136   if (!SendRequestIfNeeded(error) || !connection_->FinishRequest(error))
    137     return std::unique_ptr<Response>();
    138   std::unique_ptr<Response> response(new Response(connection_));
    139   connection_.reset();
    140   transport_.reset();  // Indicate that the response has been received
    141   return response;
    142 }
    143 
    144 RequestID Request::GetResponse(const SuccessCallback& success_callback,
    145                                const ErrorCallback& error_callback) {
    146   ErrorPtr error;
    147   if (!SendRequestIfNeeded(&error)) {
    148     transport_->RunCallbackAsync(
    149         FROM_HERE, base::Bind(error_callback, 0, base::Owned(error.release())));
    150     return 0;
    151   }
    152   RequestID id =
    153       connection_->FinishRequestAsync(success_callback, error_callback);
    154   connection_.reset();
    155   transport_.reset();  // Indicate that the request has been dispatched.
    156   return id;
    157 }
    158 
    159 void Request::SetAccept(const std::string& accept_mime_types) {
    160   DCHECK(transport_) << "Request already sent";
    161   accept_ = accept_mime_types;
    162 }
    163 
    164 const std::string& Request::GetAccept() const {
    165   return accept_;
    166 }
    167 
    168 void Request::SetContentType(const std::string& contentType) {
    169   DCHECK(transport_) << "Request already sent";
    170   content_type_ = contentType;
    171 }
    172 
    173 const std::string& Request::GetContentType() const {
    174   return content_type_;
    175 }
    176 
    177 void Request::AddHeader(const std::string& header, const std::string& value) {
    178   DCHECK(transport_) << "Request already sent";
    179   headers_.emplace(header, value);
    180 }
    181 
    182 void Request::AddHeaders(const HeaderList& headers) {
    183   DCHECK(transport_) << "Request already sent";
    184   headers_.insert(headers.begin(), headers.end());
    185 }
    186 
    187 bool Request::AddRequestBody(const void* data,
    188                              size_t size,
    189                              brillo::ErrorPtr* error) {
    190   if (!SendRequestIfNeeded(error))
    191     return false;
    192   StreamPtr stream = MemoryStream::OpenCopyOf(data, size, error);
    193   return stream && connection_->SetRequestData(std::move(stream), error);
    194 }
    195 
    196 bool Request::AddRequestBody(StreamPtr stream, brillo::ErrorPtr* error) {
    197   return SendRequestIfNeeded(error) &&
    198          connection_->SetRequestData(std::move(stream), error);
    199 }
    200 
    201 bool Request::AddRequestBodyAsFormData(std::unique_ptr<FormData> form_data,
    202                                        brillo::ErrorPtr* error) {
    203   AddHeader(request_header::kContentType, form_data->GetContentType());
    204   if (!SendRequestIfNeeded(error))
    205     return false;
    206   return connection_->SetRequestData(form_data->ExtractDataStream(), error);
    207 }
    208 
    209 bool Request::AddResponseStream(StreamPtr stream, brillo::ErrorPtr* error) {
    210   if (!SendRequestIfNeeded(error))
    211     return false;
    212   connection_->SetResponseData(std::move(stream));
    213   return true;
    214 }
    215 
    216 const std::string& Request::GetRequestURL() const {
    217   return request_url_;
    218 }
    219 
    220 const std::string& Request::GetRequestMethod() const {
    221   return method_;
    222 }
    223 
    224 void Request::SetReferer(const std::string& referer) {
    225   DCHECK(transport_) << "Request already sent";
    226   referer_ = referer;
    227 }
    228 
    229 const std::string& Request::GetReferer() const {
    230   return referer_;
    231 }
    232 
    233 void Request::SetUserAgent(const std::string& user_agent) {
    234   DCHECK(transport_) << "Request already sent";
    235   user_agent_ = user_agent;
    236 }
    237 
    238 const std::string& Request::GetUserAgent() const {
    239   return user_agent_;
    240 }
    241 
    242 bool Request::SendRequestIfNeeded(brillo::ErrorPtr* error) {
    243   if (transport_) {
    244     if (!connection_) {
    245       http::HeaderList headers = brillo::MapToVector(headers_);
    246       std::vector<std::string> ranges;
    247       if (method_ != request_type::kHead) {
    248         ranges.reserve(ranges_.size());
    249         for (auto p : ranges_) {
    250           if (p.first != range_value_omitted ||
    251               p.second != range_value_omitted) {
    252             std::string range;
    253             if (p.first != range_value_omitted) {
    254               range = brillo::string_utils::ToString(p.first);
    255             }
    256             range += '-';
    257             if (p.second != range_value_omitted) {
    258               range += brillo::string_utils::ToString(p.second);
    259             }
    260             ranges.push_back(range);
    261           }
    262         }
    263       }
    264       if (!ranges.empty())
    265         headers.emplace_back(
    266             request_header::kRange,
    267             "bytes=" + brillo::string_utils::Join(",", ranges));
    268 
    269       headers.emplace_back(request_header::kAccept, GetAccept());
    270       if (method_ != request_type::kGet && method_ != request_type::kHead) {
    271         if (!content_type_.empty())
    272           headers.emplace_back(request_header::kContentType, content_type_);
    273       }
    274       connection_ = transport_->CreateConnection(
    275           request_url_, method_, headers, user_agent_, referer_, error);
    276     }
    277 
    278     if (connection_)
    279       return true;
    280   } else {
    281     brillo::Error::AddTo(error,
    282                            FROM_HERE,
    283                            http::kErrorDomain,
    284                            "response_already_received",
    285                            "HTTP response already received");
    286   }
    287   return false;
    288 }
    289 
    290 // ************************************************************
    291 // ********************** Response Class **********************
    292 // ************************************************************
    293 Response::Response(const std::shared_ptr<Connection>& connection)
    294     : connection_{connection} {
    295   VLOG(1) << "http::Response created";
    296 }
    297 
    298 Response::~Response() {
    299   VLOG(1) << "http::Response destroyed";
    300 }
    301 
    302 bool Response::IsSuccessful() const {
    303   int code = GetStatusCode();
    304   return code >= status_code::Continue && code < status_code::BadRequest;
    305 }
    306 
    307 int Response::GetStatusCode() const {
    308   if (!connection_)
    309     return -1;
    310 
    311   return connection_->GetResponseStatusCode();
    312 }
    313 
    314 std::string Response::GetStatusText() const {
    315   if (!connection_)
    316     return std::string();
    317 
    318   return connection_->GetResponseStatusText();
    319 }
    320 
    321 std::string Response::GetContentType() const {
    322   return GetHeader(response_header::kContentType);
    323 }
    324 
    325 StreamPtr Response::ExtractDataStream(ErrorPtr* error) {
    326   return connection_->ExtractDataStream(error);
    327 }
    328 
    329 std::vector<uint8_t> Response::ExtractData() {
    330   std::vector<uint8_t> data;
    331   StreamPtr src_stream = connection_->ExtractDataStream(nullptr);
    332   StreamPtr dest_stream = MemoryStream::CreateRef(&data, nullptr);
    333   if (src_stream && dest_stream) {
    334     char buffer[1024];
    335     size_t read = 0;
    336     while (src_stream->ReadBlocking(buffer, sizeof(buffer), &read, nullptr) &&
    337            read > 0) {
    338       CHECK(dest_stream->WriteAllBlocking(buffer, read, nullptr));
    339     }
    340   }
    341   return data;
    342 }
    343 
    344 std::string Response::ExtractDataAsString() {
    345   std::vector<uint8_t> data = ExtractData();
    346   return std::string{data.begin(), data.end()};
    347 }
    348 
    349 std::string Response::GetHeader(const std::string& header_name) const {
    350   if (connection_)
    351     return connection_->GetResponseHeader(header_name);
    352 
    353   return std::string();
    354 }
    355 
    356 }  // namespace http
    357 }  // namespace brillo
    358