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