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 // ***********************************************************
Request(const std::string & url,const std::string & method,std::shared_ptr<Transport> transport)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 
~Request()116 Request::~Request() {
117   VLOG(1) << "http::Request destroyed";
118 }
119 
AddRange(int64_t bytes)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 
AddRange(uint64_t from_byte,uint64_t to_byte)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 
GetResponseAndBlock(brillo::ErrorPtr * error)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 
GetResponse(const SuccessCallback & success_callback,const ErrorCallback & error_callback)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 
SetAccept(const std::string & accept_mime_types)159 void Request::SetAccept(const std::string& accept_mime_types) {
160   DCHECK(transport_) << "Request already sent";
161   accept_ = accept_mime_types;
162 }
163 
GetAccept() const164 const std::string& Request::GetAccept() const {
165   return accept_;
166 }
167 
SetContentType(const std::string & contentType)168 void Request::SetContentType(const std::string& contentType) {
169   DCHECK(transport_) << "Request already sent";
170   content_type_ = contentType;
171 }
172 
GetContentType() const173 const std::string& Request::GetContentType() const {
174   return content_type_;
175 }
176 
AddHeader(const std::string & header,const std::string & value)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 
AddHeaders(const HeaderList & headers)182 void Request::AddHeaders(const HeaderList& headers) {
183   DCHECK(transport_) << "Request already sent";
184   headers_.insert(headers.begin(), headers.end());
185 }
186 
AddRequestBody(const void * data,size_t size,brillo::ErrorPtr * error)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 
AddRequestBody(StreamPtr stream,brillo::ErrorPtr * error)196 bool Request::AddRequestBody(StreamPtr stream, brillo::ErrorPtr* error) {
197   return SendRequestIfNeeded(error) &&
198          connection_->SetRequestData(std::move(stream), error);
199 }
200 
AddRequestBodyAsFormData(std::unique_ptr<FormData> form_data,brillo::ErrorPtr * error)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 
AddResponseStream(StreamPtr stream,brillo::ErrorPtr * error)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 
GetRequestURL() const216 const std::string& Request::GetRequestURL() const {
217   return request_url_;
218 }
219 
GetRequestMethod() const220 const std::string& Request::GetRequestMethod() const {
221   return method_;
222 }
223 
SetReferer(const std::string & referer)224 void Request::SetReferer(const std::string& referer) {
225   DCHECK(transport_) << "Request already sent";
226   referer_ = referer;
227 }
228 
GetReferer() const229 const std::string& Request::GetReferer() const {
230   return referer_;
231 }
232 
SetUserAgent(const std::string & user_agent)233 void Request::SetUserAgent(const std::string& user_agent) {
234   DCHECK(transport_) << "Request already sent";
235   user_agent_ = user_agent;
236 }
237 
GetUserAgent() const238 const std::string& Request::GetUserAgent() const {
239   return user_agent_;
240 }
241 
SendRequestIfNeeded(brillo::ErrorPtr * error)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 // ************************************************************
Response(const std::shared_ptr<Connection> & connection)293 Response::Response(const std::shared_ptr<Connection>& connection)
294     : connection_{connection} {
295   VLOG(1) << "http::Response created";
296 }
297 
~Response()298 Response::~Response() {
299   VLOG(1) << "http::Response destroyed";
300 }
301 
IsSuccessful() const302 bool Response::IsSuccessful() const {
303   int code = GetStatusCode();
304   return code >= status_code::Continue && code < status_code::BadRequest;
305 }
306 
GetStatusCode() const307 int Response::GetStatusCode() const {
308   if (!connection_)
309     return -1;
310 
311   return connection_->GetResponseStatusCode();
312 }
313 
GetStatusText() const314 std::string Response::GetStatusText() const {
315   if (!connection_)
316     return std::string();
317 
318   return connection_->GetResponseStatusText();
319 }
320 
GetContentType() const321 std::string Response::GetContentType() const {
322   return GetHeader(response_header::kContentType);
323 }
324 
ExtractDataStream(ErrorPtr * error)325 StreamPtr Response::ExtractDataStream(ErrorPtr* error) {
326   return connection_->ExtractDataStream(error);
327 }
328 
ExtractData()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 
ExtractDataAsString()344 std::string Response::ExtractDataAsString() {
345   std::vector<uint8_t> data = ExtractData();
346   return std::string{data.begin(), data.end()};
347 }
348 
GetHeader(const std::string & header_name) const349 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