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