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_utils.h>
      6 
      7 #include <algorithm>
      8 
      9 #include <base/bind.h>
     10 #include <base/json/json_reader.h>
     11 #include <base/json/json_writer.h>
     12 #include <base/values.h>
     13 #include <brillo/data_encoding.h>
     14 #include <brillo/errors/error_codes.h>
     15 #include <brillo/mime_utils.h>
     16 #include <brillo/streams/memory_stream.h>
     17 
     18 using brillo::mime::AppendParameter;
     19 using brillo::mime::RemoveParameters;
     20 
     21 namespace brillo {
     22 namespace http {
     23 
     24 std::unique_ptr<Response> GetAndBlock(const std::string& url,
     25                                       const HeaderList& headers,
     26                                       std::shared_ptr<Transport> transport,
     27                                       brillo::ErrorPtr* error) {
     28   return SendRequestWithNoDataAndBlock(
     29       request_type::kGet, url, headers, transport, error);
     30 }
     31 
     32 RequestID Get(const std::string& url,
     33               const HeaderList& headers,
     34               std::shared_ptr<Transport> transport,
     35               const SuccessCallback& success_callback,
     36               const ErrorCallback& error_callback) {
     37   return SendRequestWithNoData(request_type::kGet,
     38                                url,
     39                                headers,
     40                                transport,
     41                                success_callback,
     42                                error_callback);
     43 }
     44 
     45 std::unique_ptr<Response> HeadAndBlock(const std::string& url,
     46                                        std::shared_ptr<Transport> transport,
     47                                        brillo::ErrorPtr* error) {
     48   return SendRequestWithNoDataAndBlock(
     49       request_type::kHead, url, {}, transport, error);
     50 }
     51 
     52 RequestID Head(const std::string& url,
     53                std::shared_ptr<Transport> transport,
     54                const SuccessCallback& success_callback,
     55                const ErrorCallback& error_callback) {
     56   return SendRequestWithNoData(request_type::kHead,
     57                                url,
     58                                {},
     59                                transport,
     60                                success_callback,
     61                                error_callback);
     62 }
     63 
     64 std::unique_ptr<Response> PostTextAndBlock(const std::string& url,
     65                                            const std::string& data,
     66                                            const std::string& mime_type,
     67                                            const HeaderList& headers,
     68                                            std::shared_ptr<Transport> transport,
     69                                            brillo::ErrorPtr* error) {
     70   return PostBinaryAndBlock(
     71       url, data.data(), data.size(), mime_type, headers, transport, error);
     72 }
     73 
     74 RequestID PostText(const std::string& url,
     75                    const std::string& data,
     76                    const std::string& mime_type,
     77                    const HeaderList& headers,
     78                    std::shared_ptr<Transport> transport,
     79                    const SuccessCallback& success_callback,
     80                    const ErrorCallback& error_callback) {
     81   return PostBinary(url,
     82                     data.data(),
     83                     data.size(),
     84                     mime_type,
     85                     headers,
     86                     transport,
     87                     success_callback,
     88                     error_callback);
     89 }
     90 
     91 std::unique_ptr<Response> SendRequestAndBlock(
     92     const std::string& method,
     93     const std::string& url,
     94     const void* data,
     95     size_t data_size,
     96     const std::string& mime_type,
     97     const HeaderList& headers,
     98     std::shared_ptr<Transport> transport,
     99     brillo::ErrorPtr* error) {
    100   Request request(url, method, transport);
    101   request.AddHeaders(headers);
    102   if (data_size > 0) {
    103     CHECK(!mime_type.empty()) << "MIME type must be specified if request body "
    104                                  "message is provided";
    105     request.SetContentType(mime_type);
    106     if (!request.AddRequestBody(data, data_size, error))
    107       return std::unique_ptr<Response>();
    108   }
    109   return request.GetResponseAndBlock(error);
    110 }
    111 
    112 std::unique_ptr<Response> SendRequestWithNoDataAndBlock(
    113     const std::string& method,
    114     const std::string& url,
    115     const HeaderList& headers,
    116     std::shared_ptr<Transport> transport,
    117     brillo::ErrorPtr* error) {
    118   return SendRequestAndBlock(
    119       method, url, nullptr, 0, {}, headers, transport, error);
    120 }
    121 
    122 RequestID SendRequest(const std::string& method,
    123                       const std::string& url,
    124                       StreamPtr stream,
    125                       const std::string& mime_type,
    126                       const HeaderList& headers,
    127                       std::shared_ptr<Transport> transport,
    128                       const SuccessCallback& success_callback,
    129                       const ErrorCallback& error_callback) {
    130   Request request(url, method, transport);
    131   request.AddHeaders(headers);
    132   if (stream && (!stream->CanGetSize() || stream->GetRemainingSize() > 0)) {
    133     CHECK(!mime_type.empty()) << "MIME type must be specified if request body "
    134                                  "message is provided";
    135     request.SetContentType(mime_type);
    136     brillo::ErrorPtr error;
    137     if (!request.AddRequestBody(std::move(stream), &error)) {
    138       transport->RunCallbackAsync(
    139           FROM_HERE, base::Bind(error_callback,
    140                                 0, base::Owned(error.release())));
    141       return 0;
    142     }
    143   }
    144   return request.GetResponse(success_callback, error_callback);
    145 }
    146 
    147 RequestID SendRequest(const std::string& method,
    148                       const std::string& url,
    149                       const void* data,
    150                       size_t data_size,
    151                       const std::string& mime_type,
    152                       const HeaderList& headers,
    153                       std::shared_ptr<Transport> transport,
    154                       const SuccessCallback& success_callback,
    155                       const ErrorCallback& error_callback) {
    156   return SendRequest(method,
    157                      url,
    158                      MemoryStream::OpenCopyOf(data, data_size, nullptr),
    159                      mime_type,
    160                      headers,
    161                      transport,
    162                      success_callback,
    163                      error_callback);
    164 }
    165 
    166 RequestID SendRequestWithNoData(const std::string& method,
    167                                 const std::string& url,
    168                                 const HeaderList& headers,
    169                                 std::shared_ptr<Transport> transport,
    170                                 const SuccessCallback& success_callback,
    171                                 const ErrorCallback& error_callback) {
    172   return SendRequest(method,
    173                      url,
    174                      {},
    175                      {},
    176                      headers,
    177                      transport,
    178                      success_callback,
    179                      error_callback);
    180 }
    181 
    182 std::unique_ptr<Response> PostBinaryAndBlock(
    183     const std::string& url,
    184     const void* data,
    185     size_t data_size,
    186     const std::string& mime_type,
    187     const HeaderList& headers,
    188     std::shared_ptr<Transport> transport,
    189     brillo::ErrorPtr* error) {
    190   return SendRequestAndBlock(request_type::kPost,
    191                              url,
    192                              data,
    193                              data_size,
    194                              mime_type,
    195                              headers,
    196                              transport,
    197                              error);
    198 }
    199 
    200 RequestID PostBinary(const std::string& url,
    201                      StreamPtr stream,
    202                      const std::string& mime_type,
    203                      const HeaderList& headers,
    204                      std::shared_ptr<Transport> transport,
    205                      const SuccessCallback& success_callback,
    206                      const ErrorCallback& error_callback) {
    207   return SendRequest(request_type::kPost,
    208                      url,
    209                      std::move(stream),
    210                      mime_type,
    211                      headers,
    212                      transport,
    213                      success_callback,
    214                      error_callback);
    215 }
    216 
    217 RequestID PostBinary(const std::string& url,
    218                      const void* data,
    219                      size_t data_size,
    220                      const std::string& mime_type,
    221                      const HeaderList& headers,
    222                      std::shared_ptr<Transport> transport,
    223                      const SuccessCallback& success_callback,
    224                      const ErrorCallback& error_callback) {
    225   return SendRequest(request_type::kPost,
    226                      url,
    227                      data,
    228                      data_size,
    229                      mime_type,
    230                      headers,
    231                      transport,
    232                      success_callback,
    233                      error_callback);
    234 }
    235 
    236 std::unique_ptr<Response> PostFormDataAndBlock(
    237     const std::string& url,
    238     const FormFieldList& data,
    239     const HeaderList& headers,
    240     std::shared_ptr<Transport> transport,
    241     brillo::ErrorPtr* error) {
    242   std::string encoded_data = brillo::data_encoding::WebParamsEncode(data);
    243   return PostBinaryAndBlock(url,
    244                             encoded_data.c_str(),
    245                             encoded_data.size(),
    246                             brillo::mime::application::kWwwFormUrlEncoded,
    247                             headers,
    248                             transport,
    249                             error);
    250 }
    251 
    252 std::unique_ptr<Response> PostFormDataAndBlock(
    253     const std::string& url,
    254     std::unique_ptr<FormData> form_data,
    255     const HeaderList& headers,
    256     std::shared_ptr<Transport> transport,
    257     brillo::ErrorPtr* error) {
    258   Request request(url, request_type::kPost, transport);
    259   request.AddHeaders(headers);
    260   if (!request.AddRequestBodyAsFormData(std::move(form_data), error))
    261     return std::unique_ptr<Response>();
    262   return request.GetResponseAndBlock(error);
    263 }
    264 
    265 RequestID PostFormData(const std::string& url,
    266                        const FormFieldList& data,
    267                        const HeaderList& headers,
    268                        std::shared_ptr<Transport> transport,
    269                        const SuccessCallback& success_callback,
    270                        const ErrorCallback& error_callback) {
    271   std::string encoded_data = brillo::data_encoding::WebParamsEncode(data);
    272   return PostBinary(url,
    273                     encoded_data.c_str(),
    274                     encoded_data.size(),
    275                     brillo::mime::application::kWwwFormUrlEncoded,
    276                     headers,
    277                     transport,
    278                     success_callback,
    279                     error_callback);
    280 }
    281 
    282 RequestID PostFormData(const std::string& url,
    283                        std::unique_ptr<FormData> form_data,
    284                        const HeaderList& headers,
    285                        std::shared_ptr<Transport> transport,
    286                        const SuccessCallback& success_callback,
    287                        const ErrorCallback& error_callback) {
    288   Request request(url, request_type::kPost, transport);
    289   request.AddHeaders(headers);
    290   brillo::ErrorPtr error;
    291   if (!request.AddRequestBodyAsFormData(std::move(form_data), &error)) {
    292     transport->RunCallbackAsync(
    293         FROM_HERE, base::Bind(error_callback, 0, base::Owned(error.release())));
    294     return 0;
    295   }
    296   return request.GetResponse(success_callback, error_callback);
    297 }
    298 
    299 std::unique_ptr<Response> PostJsonAndBlock(const std::string& url,
    300                                            const base::Value* json,
    301                                            const HeaderList& headers,
    302                                            std::shared_ptr<Transport> transport,
    303                                            brillo::ErrorPtr* error) {
    304   std::string data;
    305   if (json)
    306     base::JSONWriter::Write(*json, &data);
    307   std::string mime_type = AppendParameter(brillo::mime::application::kJson,
    308                                           brillo::mime::parameters::kCharset,
    309                                           "utf-8");
    310   return PostBinaryAndBlock(
    311       url, data.c_str(), data.size(), mime_type, headers, transport, error);
    312 }
    313 
    314 RequestID PostJson(const std::string& url,
    315                    std::unique_ptr<base::Value> json,
    316                    const HeaderList& headers,
    317                    std::shared_ptr<Transport> transport,
    318                    const SuccessCallback& success_callback,
    319                    const ErrorCallback& error_callback) {
    320   std::string data;
    321   if (json)
    322     base::JSONWriter::Write(*json, &data);
    323   std::string mime_type = AppendParameter(brillo::mime::application::kJson,
    324                                           brillo::mime::parameters::kCharset,
    325                                           "utf-8");
    326   return PostBinary(url,
    327                     data.c_str(),
    328                     data.size(),
    329                     mime_type,
    330                     headers,
    331                     transport,
    332                     success_callback,
    333                     error_callback);
    334 }
    335 
    336 std::unique_ptr<Response> PatchJsonAndBlock(
    337     const std::string& url,
    338     const base::Value* json,
    339     const HeaderList& headers,
    340     std::shared_ptr<Transport> transport,
    341     brillo::ErrorPtr* error) {
    342   std::string data;
    343   if (json)
    344     base::JSONWriter::Write(*json, &data);
    345   std::string mime_type = AppendParameter(brillo::mime::application::kJson,
    346                                           brillo::mime::parameters::kCharset,
    347                                           "utf-8");
    348   return SendRequestAndBlock(request_type::kPatch,
    349                              url,
    350                              data.c_str(),
    351                              data.size(),
    352                              mime_type,
    353                              headers,
    354                              transport,
    355                              error);
    356 }
    357 
    358 RequestID PatchJson(const std::string& url,
    359                     std::unique_ptr<base::Value> json,
    360                     const HeaderList& headers,
    361                     std::shared_ptr<Transport> transport,
    362                     const SuccessCallback& success_callback,
    363                     const ErrorCallback& error_callback) {
    364   std::string data;
    365   if (json)
    366     base::JSONWriter::Write(*json, &data);
    367   std::string mime_type =
    368       AppendParameter(brillo::mime::application::kJson,
    369                       brillo::mime::parameters::kCharset, "utf-8");
    370   return SendRequest(request_type::kPatch, url, data.c_str(), data.size(),
    371                      mime_type, headers, transport, success_callback,
    372                      error_callback);
    373 }
    374 
    375 std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
    376     Response* response,
    377     int* status_code,
    378     brillo::ErrorPtr* error) {
    379   std::unique_ptr<base::DictionaryValue> result;
    380   if (!response)
    381     return result;
    382 
    383   if (status_code)
    384     *status_code = response->GetStatusCode();
    385 
    386   // Make sure we have a correct content type. Do not try to parse
    387   // binary files, or HTML output. Limit to application/json and text/plain.
    388   auto content_type = RemoveParameters(response->GetContentType());
    389   if (content_type != brillo::mime::application::kJson &&
    390       content_type != brillo::mime::text::kPlain) {
    391     brillo::Error::AddTo(error, FROM_HERE, brillo::errors::json::kDomain,
    392                          "non_json_content_type",
    393                          "Unexpected response content type: " + content_type);
    394     return result;
    395   }
    396 
    397   std::string json = response->ExtractDataAsString();
    398   std::string error_message;
    399   auto value = base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC,
    400                                                     nullptr, &error_message);
    401   if (!value) {
    402     brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
    403                                brillo::errors::json::kParseError,
    404                                "Error '%s' occurred parsing JSON string '%s'",
    405                                error_message.c_str(), json.c_str());
    406     return result;
    407   }
    408   result = base::DictionaryValue::From(std::move(value));
    409   if (!result) {
    410     brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
    411                                brillo::errors::json::kObjectExpected,
    412                                "Response is not a valid JSON object: '%s'",
    413                                json.c_str());
    414   }
    415   return result;
    416 }
    417 
    418 std::string GetCanonicalHeaderName(const std::string& name) {
    419   std::string canonical_name = name;
    420   bool word_begin = true;
    421   for (char& c : canonical_name) {
    422     if (c == '-') {
    423       word_begin = true;
    424     } else {
    425       if (word_begin) {
    426         c = toupper(c);
    427       } else {
    428         c = tolower(c);
    429       }
    430       word_begin = false;
    431     }
    432   }
    433   return canonical_name;
    434 }
    435 
    436 }  // namespace http
    437 }  // namespace brillo
    438