blob: 1c32efeb7e854d5c85d668c5ea8f737fbe67c6e1 [file] [log] [blame]
Chris Sosa45d9f102014-03-24 11:18:54 -07001// 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 "buffet/http_request.h"
6
Alex Vakulenkoa3062c52014-04-21 17:05:51 -07007#include <base/logging.h>
Alex Vakulenkob8fc1df2014-08-20 15:38:07 -07008#include <chromeos/string_utils.h>
Alex Vakulenkoa3062c52014-04-21 17:05:51 -07009
10#include "buffet/http_connection_curl.h"
Chris Sosa45d9f102014-03-24 11:18:54 -070011#include "buffet/http_transport_curl.h"
Alex Vakulenkoa3062c52014-04-21 17:05:51 -070012#include "buffet/map_utils.h"
Chris Sosa45d9f102014-03-24 11:18:54 -070013#include "buffet/mime_utils.h"
14
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070015namespace buffet {
Alex Vakulenkob3aac252014-05-07 17:35:24 -070016namespace http {
Chris Sosa45d9f102014-03-24 11:18:54 -070017
18// request_type
19const char request_type::kOptions[] = "OPTIONS";
20const char request_type::kGet[] = "GET";
21const char request_type::kHead[] = "HEAD";
22const char request_type::kPost[] = "POST";
23const char request_type::kPut[] = "PUT";
24const char request_type::kPatch[] = "PATCH";
25const char request_type::kDelete[] = "DELETE";
26const char request_type::kTrace[] = "TRACE";
27const char request_type::kConnect[] = "CONNECT";
28const char request_type::kCopy[] = "COPY";
29const char request_type::kMove[] = "MOVE";
30
31// request_header
32const char request_header::kAccept[] = "Accept";
33const char request_header::kAcceptCharset[] = "Accept-Charset";
34const char request_header::kAcceptEncoding[] = "Accept-Encoding";
35const char request_header::kAcceptLanguage[] = "Accept-Language";
36const char request_header::kAllow[] = "Allow";
37const char request_header::kAuthorization[] = "Authorization";
38const char request_header::kCacheControl[] = "Cache-Control";
39const char request_header::kConnection[] = "Connection";
40const char request_header::kContentEncoding[] = "Content-Encoding";
41const char request_header::kContentLanguage[] = "Content-Language";
42const char request_header::kContentLength[] = "Content-Length";
43const char request_header::kContentLocation[] = "Content-Location";
44const char request_header::kContentMd5[] = "Content-MD5";
45const char request_header::kContentRange[] = "Content-Range";
46const char request_header::kContentType[] = "Content-Type";
47const char request_header::kCookie[] = "Cookie";
48const char request_header::kDate[] = "Date";
49const char request_header::kExpect[] = "Expect";
50const char request_header::kExpires[] = "Expires";
51const char request_header::kFrom[] = "From";
52const char request_header::kHost[] = "Host";
53const char request_header::kIfMatch[] = "If-Match";
54const char request_header::kIfModifiedSince[] = "If-Modified-Since";
55const char request_header::kIfNoneMatch[] = "If-None-Match";
56const char request_header::kIfRange[] = "If-Range";
57const char request_header::kIfUnmodifiedSince[] = "If-Unmodified-Since";
58const char request_header::kLastModified[] = "Last-Modified";
59const char request_header::kMaxForwards[] = "Max-Forwards";
60const char request_header::kPragma[] = "Pragma";
61const char request_header::kProxyAuthorization[] = "Proxy-Authorization";
62const char request_header::kRange[] = "Range";
63const char request_header::kReferer[] = "Referer";
64const char request_header::kTE[] = "TE";
65const char request_header::kTrailer[] = "Trailer";
66const char request_header::kTransferEncoding[] = "Transfer-Encoding";
67const char request_header::kUpgrade[] = "Upgrade";
68const char request_header::kUserAgent[] = "User-Agent";
69const char request_header::kVia[] = "Via";
70const char request_header::kWarning[] = "Warning";
71
72// response_header
73const char response_header::kAcceptRanges[] = "Accept-Ranges";
74const char response_header::kAge[] = "Age";
75const char response_header::kAllow[] = "Allow";
76const char response_header::kCacheControl[] = "Cache-Control";
77const char response_header::kConnection[] = "Connection";
78const char response_header::kContentEncoding[] = "Content-Encoding";
79const char response_header::kContentLanguage[] = "Content-Language";
80const char response_header::kContentLength[] = "Content-Length";
81const char response_header::kContentLocation[] = "Content-Location";
82const char response_header::kContentMd5[] = "Content-MD5";
83const char response_header::kContentRange[] = "Content-Range";
84const char response_header::kContentType[] = "Content-Type";
85const char response_header::kDate[] = "Date";
86const char response_header::kETag[] = "ETag";
87const char response_header::kExpires[] = "Expires";
88const char response_header::kLastModified[] = "Last-Modified";
89const char response_header::kLocation[] = "Location";
90const char response_header::kPragma[] = "Pragma";
91const char response_header::kProxyAuthenticate[] = "Proxy-Authenticate";
92const char response_header::kRetryAfter[] = "Retry-After";
93const char response_header::kServer[] = "Server";
94const char response_header::kSetCookie[] = "Set-Cookie";
95const char response_header::kTrailer[] = "Trailer";
96const char response_header::kTransferEncoding[] = "Transfer-Encoding";
97const char response_header::kUpgrade[] = "Upgrade";
98const char response_header::kVary[] = "Vary";
99const char response_header::kVia[] = "Via";
100const char response_header::kWarning[] = "Warning";
101const char response_header::kWwwAuthenticate[] = "WWW-Authenticate";
102
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700103// ***********************************************************
104// ********************** Request Class **********************
105// ***********************************************************
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700106Request::Request(const std::string& url, const char* method,
107 std::shared_ptr<Transport> transport) :
108 transport_(transport), request_url_(url), method_(method) {
109 VLOG(1) << "http::Request created";
110 if (!transport_)
111 transport_.reset(new http::curl::Transport());
Chris Sosa45d9f102014-03-24 11:18:54 -0700112}
113
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700114Request::~Request() {
115 VLOG(1) << "http::Request destroyed";
Chris Sosa45d9f102014-03-24 11:18:54 -0700116}
117
118void Request::AddRange(int64_t bytes) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700119 if (bytes < 0) {
120 ranges_.emplace_back(Request::range_value_omitted, -bytes);
121 } else {
122 ranges_.emplace_back(bytes, Request::range_value_omitted);
123 }
Chris Sosa45d9f102014-03-24 11:18:54 -0700124}
125
126void Request::AddRange(uint64_t from_byte, uint64_t to_byte) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700127 ranges_.emplace_back(from_byte, to_byte);
Chris Sosa45d9f102014-03-24 11:18:54 -0700128}
129
Alex Vakulenko5f472062014-08-14 17:54:04 -0700130std::unique_ptr<Response> Request::GetResponse(chromeos::ErrorPtr* error) {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700131 if (!SendRequestIfNeeded(error) || !connection_->FinishRequest(error))
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700132 return std::unique_ptr<Response>();
133 std::unique_ptr<Response> response(new Response(std::move(connection_)));
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700134 transport_.reset(); // Indicate that the response has been received
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700135 return response;
Chris Sosa45d9f102014-03-24 11:18:54 -0700136}
137
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700138void Request::SetAccept(const char* accept_mime_types) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700139 accept_ = accept_mime_types;
Chris Sosa45d9f102014-03-24 11:18:54 -0700140}
141
142std::string Request::GetAccept() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700143 return accept_;
Chris Sosa45d9f102014-03-24 11:18:54 -0700144}
145
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700146void Request::SetContentType(const char* contentType) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700147 content_type_ = contentType;
Chris Sosa45d9f102014-03-24 11:18:54 -0700148}
149
150std::string Request::GetContentType() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700151 return content_type_;
Chris Sosa45d9f102014-03-24 11:18:54 -0700152}
153
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700154void Request::AddHeader(const char* header, const char* value) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700155 headers_[header] = value;
Chris Sosa45d9f102014-03-24 11:18:54 -0700156}
157
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700158void Request::AddHeaders(const HeaderList& headers) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700159 headers_.insert(headers.begin(), headers.end());
Alex Vakulenkob645cc92014-04-15 11:34:35 -0700160}
161
Alex Vakulenko5f472062014-08-14 17:54:04 -0700162bool Request::AddRequestBody(const void* data,
163 size_t size,
164 chromeos::ErrorPtr* error) {
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700165 if (!SendRequestIfNeeded(error))
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700166 return false;
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700167 return connection_->WriteRequestData(data, size, error);
Chris Sosa45d9f102014-03-24 11:18:54 -0700168}
169
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700170void Request::SetReferer(const char* referer) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700171 referer_ = referer;
Chris Sosa45d9f102014-03-24 11:18:54 -0700172}
173
174std::string Request::GetReferer() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700175 return referer_;
Chris Sosa45d9f102014-03-24 11:18:54 -0700176}
177
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700178void Request::SetUserAgent(const char* user_agent) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700179 user_agent_ = user_agent;
Chris Sosa45d9f102014-03-24 11:18:54 -0700180}
181
182std::string Request::GetUserAgent() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700183 return user_agent_;
Chris Sosa45d9f102014-03-24 11:18:54 -0700184}
185
Alex Vakulenko5f472062014-08-14 17:54:04 -0700186bool Request::SendRequestIfNeeded(chromeos::ErrorPtr* error) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700187 if (transport_) {
188 if (!connection_) {
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700189 http::HeaderList headers = MapToVector(headers_);
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700190 std::vector<std::string> ranges;
191 if (method_ != request_type::kHead) {
192 ranges.reserve(ranges_.size());
193 for (auto p : ranges_) {
194 if (p.first != range_value_omitted ||
195 p.second != range_value_omitted) {
196 std::string range;
197 if (p.first != range_value_omitted) {
Alex Vakulenkob8fc1df2014-08-20 15:38:07 -0700198 range = chromeos::string_utils::ToString(p.first);
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700199 }
200 range += '-';
201 if (p.second != range_value_omitted) {
Alex Vakulenkob8fc1df2014-08-20 15:38:07 -0700202 range += chromeos::string_utils::ToString(p.second);
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700203 }
204 ranges.push_back(range);
205 }
206 }
207 }
208 if (!ranges.empty())
209 headers.emplace_back(request_header::kRange,
Alex Vakulenkob8fc1df2014-08-20 15:38:07 -0700210 "bytes=" +
211 chromeos::string_utils::Join(',', ranges));
Chris Sosa45d9f102014-03-24 11:18:54 -0700212
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700213 headers.emplace_back(request_header::kAccept, GetAccept());
214 if (method_ != request_type::kGet && method_ != request_type::kHead) {
215 if (!content_type_.empty())
216 headers.emplace_back(request_header::kContentType, content_type_);
217 }
218 connection_ = transport_->CreateConnection(transport_, request_url_,
219 method_, headers,
220 user_agent_, referer_,
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700221 error);
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700222 }
223
224 if (connection_)
225 return true;
226 } else {
Alex Vakulenko5f472062014-08-14 17:54:04 -0700227 chromeos::Error::AddTo(error, http::curl::kErrorDomain,
228 "request_already_received",
229 "HTTP response already received");
Chris Sosa45d9f102014-03-24 11:18:54 -0700230 }
231 return false;
232}
233
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700234// ************************************************************
235// ********************** Response Class **********************
236// ************************************************************
237Response::Response(std::unique_ptr<Connection> connection)
238 : connection_(std::move(connection)) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700239 VLOG(1) << "http::Response created";
240 // Response object doesn't have streaming interface for response data (yet),
241 // so read the data into a buffer and cache it.
242 if (connection_) {
243 size_t size = static_cast<size_t>(connection_->GetResponseDataSize());
244 response_data_.reserve(size);
245 unsigned char buffer[1024];
246 size_t read = 0;
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700247 while (connection_->ReadResponseData(buffer, sizeof(buffer),
248 &read, nullptr) && read > 0) {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700249 response_data_.insert(response_data_.end(), buffer, buffer + read);
250 }
251 }
252}
253
254Response::~Response() {
255 VLOG(1) << "http::Response destroyed";
256}
257
258bool Response::IsSuccessful() const {
259 int code = GetStatusCode();
260 return code >= status_code::Continue && code < status_code::BadRequest;
261}
262
Chris Sosa45d9f102014-03-24 11:18:54 -0700263int Response::GetStatusCode() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700264 if (!connection_)
Chris Sosa45d9f102014-03-24 11:18:54 -0700265 return -1;
266
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700267 return connection_->GetResponseStatusCode();
Chris Sosa45d9f102014-03-24 11:18:54 -0700268}
269
270std::string Response::GetStatusText() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700271 if (!connection_)
Chris Sosa45d9f102014-03-24 11:18:54 -0700272 return std::string();
273
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700274 return connection_->GetResponseStatusText();
Chris Sosa45d9f102014-03-24 11:18:54 -0700275}
276
277std::string Response::GetContentType() const {
278 return GetHeader(response_header::kContentType);
279}
280
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700281const std::vector<unsigned char>& Response::GetData() const {
282 return response_data_;
Chris Sosa45d9f102014-03-24 11:18:54 -0700283}
284
285std::string Response::GetDataAsString() const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700286 if (response_data_.empty())
287 return std::string();
Chris Sosa45d9f102014-03-24 11:18:54 -0700288
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700289 const char* data_buf = reinterpret_cast<const char*>(response_data_.data());
290 return std::string(data_buf, data_buf + response_data_.size());
Chris Sosa45d9f102014-03-24 11:18:54 -0700291}
292
Alex Vakulenkob8ba5952014-04-17 11:35:56 -0700293std::string Response::GetHeader(const char* header_name) const {
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700294 if (connection_)
295 return connection_->GetResponseHeader(header_name);
Chris Sosa45d9f102014-03-24 11:18:54 -0700296
297 return std::string();
298}
299
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700300} // namespace http
Alex Vakulenkoaf23b322014-05-08 16:25:45 -0700301} // namespace buffet