• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetAndBlock(const std::string & url,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
Get(const std::string & url,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
HeadAndBlock(const std::string & url,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
Head(const std::string & url,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostTextAndBlock(const std::string & url,const std::string & data,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PostText(const std::string & url,const std::string & data,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
SendRequestAndBlock(const std::string & method,const std::string & url,const void * data,size_t data_size,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
SendRequestWithNoDataAndBlock(const std::string & method,const std::string & url,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
SendRequest(const std::string & method,const std::string & url,StreamPtr stream,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
SendRequest(const std::string & method,const std::string & url,const void * data,size_t data_size,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
SendRequestWithNoData(const std::string & method,const std::string & url,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostBinaryAndBlock(const std::string & url,const void * data,size_t data_size,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PostBinary(const std::string & url,StreamPtr stream,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostBinary(const std::string & url,const void * data,size_t data_size,const std::string & mime_type,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostFormDataAndBlock(const std::string & url,const FormFieldList & data,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PostFormDataAndBlock(const std::string & url,std::unique_ptr<FormData> form_data,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PostFormData(const std::string & url,const FormFieldList & data,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostFormData(const std::string & url,std::unique_ptr<FormData> form_data,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PostJsonAndBlock(const std::string & url,const base::Value * json,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PostJson(const std::string & url,std::unique_ptr<base::Value> json,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
PatchJsonAndBlock(const std::string & url,const base::Value * json,const HeaderList & headers,std::shared_ptr<Transport> transport,brillo::ErrorPtr * error)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 
PatchJson(const std::string & url,std::unique_ptr<base::Value> json,const HeaderList & headers,std::shared_ptr<Transport> transport,const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
ParseJsonResponse(Response * response,int * status_code,brillo::ErrorPtr * error)375 std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
376     Response* response,
377     int* status_code,
378     brillo::ErrorPtr* error) {
379   if (!response)
380     return std::unique_ptr<base::DictionaryValue>();
381 
382   if (status_code)
383     *status_code = response->GetStatusCode();
384 
385   // Make sure we have a correct content type. Do not try to parse
386   // binary files, or HTML output. Limit to application/json and text/plain.
387   auto content_type = RemoveParameters(response->GetContentType());
388   if (content_type != brillo::mime::application::kJson &&
389       content_type != brillo::mime::text::kPlain) {
390     brillo::Error::AddTo(error, FROM_HERE, brillo::errors::json::kDomain,
391                          "non_json_content_type",
392                          "Unexpected response content type: " + content_type);
393     return std::unique_ptr<base::DictionaryValue>();
394   }
395 
396   std::string json = response->ExtractDataAsString();
397   std::string error_message;
398   auto value = base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC,
399                                                     nullptr, &error_message);
400   if (!value) {
401     brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
402                                brillo::errors::json::kParseError,
403                                "Error '%s' occurred parsing JSON string '%s'",
404                                error_message.c_str(), json.c_str());
405     return std::unique_ptr<base::DictionaryValue>();
406   }
407   base::DictionaryValue* dict_value = nullptr;
408   if (!value->GetAsDictionary(&dict_value)) {
409     brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
410                                brillo::errors::json::kObjectExpected,
411                                "Response is not a valid JSON object: '%s'",
412                                json.c_str());
413     return std::unique_ptr<base::DictionaryValue>();
414   } else {
415     // |value| is now owned by |dict_value|, so release the scoped_ptr now.
416     base::IgnoreResult(value.release());
417   }
418   return std::unique_ptr<base::DictionaryValue>(dict_value);
419 }
420 
GetCanonicalHeaderName(const std::string & name)421 std::string GetCanonicalHeaderName(const std::string& name) {
422   std::string canonical_name = name;
423   bool word_begin = true;
424   for (char& c : canonical_name) {
425     if (c == '-') {
426       word_begin = true;
427     } else {
428       if (word_begin) {
429         c = toupper(c);
430       } else {
431         c = tolower(c);
432       }
433       word_begin = false;
434     }
435   }
436   return canonical_name;
437 }
438 
439 }  // namespace http
440 }  // namespace brillo
441