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 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 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