http_request.cc revision 2eed2f9a423b8e776b4d54530c567736c371ba48
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 <chromeos/http/http_request.h>
6
7#include <base/bind.h>
8#include <base/callback.h>
9#include <base/logging.h>
10#include <chromeos/http/http_form_data.h>
11#include <chromeos/map_utils.h>
12#include <chromeos/mime_utils.h>
13#include <chromeos/streams/memory_stream.h>
14#include <chromeos/strings/string_utils.h>
15
16namespace chromeos {
17namespace http {
18
19// request_type
20const char request_type::kOptions[]               = "OPTIONS";
21const char request_type::kGet[]                   = "GET";
22const char request_type::kHead[]                  = "HEAD";
23const char request_type::kPost[]                  = "POST";
24const char request_type::kPut[]                   = "PUT";
25const char request_type::kPatch[]                 = "PATCH";
26const char request_type::kDelete[]                = "DELETE";
27const char request_type::kTrace[]                 = "TRACE";
28const char request_type::kConnect[]               = "CONNECT";
29const char request_type::kCopy[]                  = "COPY";
30const char request_type::kMove[]                  = "MOVE";
31
32// request_header
33const char request_header::kAccept[]              = "Accept";
34const char request_header::kAcceptCharset[]       = "Accept-Charset";
35const char request_header::kAcceptEncoding[]      = "Accept-Encoding";
36const char request_header::kAcceptLanguage[]      = "Accept-Language";
37const char request_header::kAllow[]               = "Allow";
38const char request_header::kAuthorization[]       = "Authorization";
39const char request_header::kCacheControl[]        = "Cache-Control";
40const char request_header::kConnection[]          = "Connection";
41const char request_header::kContentEncoding[]     = "Content-Encoding";
42const char request_header::kContentLanguage[]     = "Content-Language";
43const char request_header::kContentLength[]       = "Content-Length";
44const char request_header::kContentLocation[]     = "Content-Location";
45const char request_header::kContentMd5[]          = "Content-MD5";
46const char request_header::kContentRange[]        = "Content-Range";
47const char request_header::kContentType[]         = "Content-Type";
48const char request_header::kCookie[]              = "Cookie";
49const char request_header::kDate[]                = "Date";
50const char request_header::kExpect[]              = "Expect";
51const char request_header::kExpires[]             = "Expires";
52const char request_header::kFrom[]                = "From";
53const char request_header::kHost[]                = "Host";
54const char request_header::kIfMatch[]             = "If-Match";
55const char request_header::kIfModifiedSince[]     = "If-Modified-Since";
56const char request_header::kIfNoneMatch[]         = "If-None-Match";
57const char request_header::kIfRange[]             = "If-Range";
58const char request_header::kIfUnmodifiedSince[]   = "If-Unmodified-Since";
59const char request_header::kLastModified[]        = "Last-Modified";
60const char request_header::kMaxForwards[]         = "Max-Forwards";
61const char request_header::kPragma[]              = "Pragma";
62const char request_header::kProxyAuthorization[]  = "Proxy-Authorization";
63const char request_header::kRange[]               = "Range";
64const char request_header::kReferer[]             = "Referer";
65const char request_header::kTE[]                  = "TE";
66const char request_header::kTrailer[]             = "Trailer";
67const char request_header::kTransferEncoding[]    = "Transfer-Encoding";
68const char request_header::kUpgrade[]             = "Upgrade";
69const char request_header::kUserAgent[]           = "User-Agent";
70const char request_header::kVia[]                 = "Via";
71const char request_header::kWarning[]             = "Warning";
72
73// response_header
74const char response_header::kAcceptRanges[]       = "Accept-Ranges";
75const char response_header::kAge[]                = "Age";
76const char response_header::kAllow[]              = "Allow";
77const char response_header::kCacheControl[]       = "Cache-Control";
78const char response_header::kConnection[]         = "Connection";
79const char response_header::kContentEncoding[]    = "Content-Encoding";
80const char response_header::kContentLanguage[]    = "Content-Language";
81const char response_header::kContentLength[]      = "Content-Length";
82const char response_header::kContentLocation[]    = "Content-Location";
83const char response_header::kContentMd5[]         = "Content-MD5";
84const char response_header::kContentRange[]       = "Content-Range";
85const char response_header::kContentType[]        = "Content-Type";
86const char response_header::kDate[]               = "Date";
87const char response_header::kETag[]               = "ETag";
88const char response_header::kExpires[]            = "Expires";
89const char response_header::kLastModified[]       = "Last-Modified";
90const char response_header::kLocation[]           = "Location";
91const char response_header::kPragma[]             = "Pragma";
92const char response_header::kProxyAuthenticate[]  = "Proxy-Authenticate";
93const char response_header::kRetryAfter[]         = "Retry-After";
94const char response_header::kServer[]             = "Server";
95const char response_header::kSetCookie[]          = "Set-Cookie";
96const char response_header::kTrailer[]            = "Trailer";
97const char response_header::kTransferEncoding[]   = "Transfer-Encoding";
98const char response_header::kUpgrade[]            = "Upgrade";
99const char response_header::kVary[]               = "Vary";
100const char response_header::kVia[]                = "Via";
101const char response_header::kWarning[]            = "Warning";
102const char response_header::kWwwAuthenticate[]    = "WWW-Authenticate";
103
104// ***********************************************************
105// ********************** Request Class **********************
106// ***********************************************************
107Request::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
116Request::~Request() {
117  VLOG(1) << "http::Request destroyed";
118}
119
120void 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
129void 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
134std::unique_ptr<Response> Request::GetResponseAndBlock(
135    chromeos::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
144RequestID 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
159void Request::SetAccept(const std::string& accept_mime_types) {
160  DCHECK(transport_) << "Request already sent";
161  accept_ = accept_mime_types;
162}
163
164const std::string& Request::GetAccept() const {
165  return accept_;
166}
167
168void Request::SetContentType(const std::string& contentType) {
169  DCHECK(transport_) << "Request already sent";
170  content_type_ = contentType;
171}
172
173const std::string& Request::GetContentType() const {
174  return content_type_;
175}
176
177void Request::AddHeader(const std::string& header, const std::string& value) {
178  DCHECK(transport_) << "Request already sent";
179  headers_.emplace(header, value);
180}
181
182void Request::AddHeaders(const HeaderList& headers) {
183  DCHECK(transport_) << "Request already sent";
184  headers_.insert(headers.begin(), headers.end());
185}
186
187bool Request::AddRequestBody(const void* data,
188                             size_t size,
189                             chromeos::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
196bool Request::AddRequestBody(StreamPtr stream, chromeos::ErrorPtr* error) {
197  return SendRequestIfNeeded(error) &&
198         connection_->SetRequestData(std::move(stream), error);
199}
200
201bool Request::AddRequestBodyAsFormData(std::unique_ptr<FormData> form_data,
202                                       chromeos::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
209bool Request::AddResponseStream(StreamPtr stream, chromeos::ErrorPtr* error) {
210  if (!SendRequestIfNeeded(error))
211    return false;
212  connection_->SetResponseData(std::move(stream));
213  return true;
214}
215
216const std::string& Request::GetRequestURL() const {
217  return request_url_;
218}
219
220const std::string& Request::GetRequestMethod() const {
221  return method_;
222}
223
224void Request::SetReferer(const std::string& referer) {
225  DCHECK(transport_) << "Request already sent";
226  referer_ = referer;
227}
228
229const std::string& Request::GetReferer() const {
230  return referer_;
231}
232
233void Request::SetUserAgent(const std::string& user_agent) {
234  DCHECK(transport_) << "Request already sent";
235  user_agent_ = user_agent;
236}
237
238const std::string& Request::GetUserAgent() const {
239  return user_agent_;
240}
241
242bool Request::SendRequestIfNeeded(chromeos::ErrorPtr* error) {
243  if (transport_) {
244    if (!connection_) {
245      http::HeaderList headers = chromeos::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 = chromeos::string_utils::ToString(p.first);
255            }
256            range += '-';
257            if (p.second != range_value_omitted) {
258              range += chromeos::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=" + chromeos::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    chromeos::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// ************************************************************
293Response::Response(const std::shared_ptr<Connection>& connection)
294    : connection_{connection} {
295  VLOG(1) << "http::Response created";
296}
297
298Response::~Response() {
299  VLOG(1) << "http::Response destroyed";
300}
301
302bool Response::IsSuccessful() const {
303  int code = GetStatusCode();
304  return code >= status_code::Continue && code < status_code::BadRequest;
305}
306
307int Response::GetStatusCode() const {
308  if (!connection_)
309    return -1;
310
311  return connection_->GetResponseStatusCode();
312}
313
314std::string Response::GetStatusText() const {
315  if (!connection_)
316    return std::string();
317
318  return connection_->GetResponseStatusText();
319}
320
321std::string Response::GetContentType() const {
322  return GetHeader(response_header::kContentType);
323}
324
325StreamPtr Response::ExtractDataStream(ErrorPtr* error) {
326  return connection_->ExtractDataStream(error);
327}
328
329std::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
344std::string Response::ExtractDataAsString() {
345  std::vector<uint8_t> data = ExtractData();
346  return std::string{data.begin(), data.end()};
347}
348
349std::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 chromeos
358