Ion
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
httpclient.cc
Go to the documentation of this file.
1 
18 #include "ion/remote/httpclient.h"
19 
20 #include <algorithm>
21 #include <sstream>
22 #include <vector>
23 
24 #include "ion/base/logging.h"
25 #include "ion/base/stringutils.h"
26 #include "third_party/mongoose/mongoose.h"
27 
28 namespace ion {
29 namespace remote {
30 
31 namespace {
32 
34 static std::string BuildRangeRequestHeader(int64 start, int64 end) {
35  std::stringstream headers;
36  headers << "Range: bytes=" << start << "-" << end << "\r\n\r\n";
37  return headers.str();
38 }
39 
40 static std::string BuildUploadHeaders(const std::string& data) {
41  std::stringstream headers;
42  headers << "Content-Type: text/plain\r\n";
43  headers << "Content-Length: " << data.size() << "\r\n\r\n";
44  headers << data;
45  return headers.str();
46 }
47 
49 static std::string BuildUri(const HttpClient::Url& url) {
50  typedef std::map<std::string, std::string>::const_iterator iterator;
51  std::string uri = url.path;
52  if (url.args.size()) {
53  uri.append("?");
54  for (iterator it = url.args.begin(); it != url.args.end(); ++it) {
55  if (it != url.args.begin())
56  uri.append("&");
57  uri.append(it->first);
58  uri.append("=");
59  uri.append(it->second);
60  }
61  }
62  return uri;
63 }
64 
65 static const HttpClient::Response SendRequest(const HttpClient::Url& url,
66  const char* method,
67  const std::string& headers) {
68  HttpClient::Response response;
69  response.url = url;
70  if (url.IsValid()) {
71  static const int kErrorStringLength = 2048;
72  char error[kErrorStringLength];
73  std::ostringstream header_str;
74  header_str << headers;
75  header_str << "Host: " + url.hostname << "\r\n";
76  const std::string header_string = header_str.str();
77 
79  if (mg_connection* connection = mg_download(url.hostname.c_str(),
80  url.port,
81  url.is_https ? 1 : 0,
82  error,
83  kErrorStringLength,
84  "%s %s HTTP/1.1\r\n%s\r\n",
85  method,
86  BuildUri(url).c_str(),
87  header_string.c_str())) {
89  mg_request_info* info = mg_get_request_info(connection);
91  if (info->uri)
92  response.status = base::StringToInt32(info->uri);
93  for (int i = 0; i < info->num_headers; ++i) {
94  response.headers[info->http_headers[i].name] =
95  info->http_headers[i].value;
96  }
98  static const int kBufferSize = 512;
99  char buf[kBufferSize];
100  int bytes_read;
101  response.data.reserve(kBufferSize);
103  while ((bytes_read = mg_read(connection, buf, sizeof(buf))) > 0) {
104  response.data.insert(response.data.end(), buf, &buf[bytes_read]);
105  }
106  mg_close_connection(connection);
107  }
108  }
109  return response;
110 }
111 
112 } // anonymous namespace
113 
114 HttpClient::Url::Url() : port(-1), is_https(false) {
115 }
116 
117 HttpClient::Url::Url(const std::string& url) : port(-1), is_https(false) {
118  Set(url);
119 }
120 
121 void HttpClient::Url::Set(const std::string& url) {
122  typedef std::string::const_iterator iterator;
123 
125  port = 80;
126  is_https = false;
127  hostname.clear();
128  path.clear();
129  args.clear();
130 
131  if (url.empty())
132  return;
133 
134  iterator end = url.end();
135 
137  iterator query_pos = std::find(url.begin(), end, '?');
138 
140  iterator proto_start = url.begin();
141  iterator proto_end = std::find(proto_start, end, ':');
142 
143  if (proto_end != end) {
144  const std::string prot = &*(proto_end);
145  if ((prot.length() > 3) && (prot.substr(0, 3) == "://")) {
146  const std::string protocol(proto_start, proto_end);
147  if (protocol == "https") {
148  port = 443;
149  is_https = true;
150  } else if (protocol != "http") {
151  LOG(ERROR) << "Unknown protocol '" << protocol
152  << "', defaulting to http";
153  }
154  proto_end += 3; // Skip the "://".
155  } else {
157  proto_end = url.begin();
158  }
159  } else {
161  proto_end = url.begin();
162  }
163 
166  iterator host_start = proto_end;
167  iterator path_start = std::find(host_start, end, '/');
168 
170  iterator hostEnd =
171  std::find(proto_end, (path_start != end) ? path_start : query_pos, ':');
172 
173  hostname = std::string(host_start, hostEnd);
174 
176  if ((hostEnd != end) && ((&*(hostEnd))[0] == ':')) {
177  hostEnd++;
178  iterator portEnd = (path_start != end) ? path_start : query_pos;
179  const std::string port_string(hostEnd, portEnd);
180  port = base::StringToInt32(port_string);
181  }
182 
184  if (path_start != end)
185  path = std::string(path_start, query_pos);
186  else
187  path = "/";
188 
190  if (query_pos != end) {
191  query_pos++;
192  std::vector<std::string> queries =
193  base::SplitString(std::string(query_pos, url.end()), "&");
194  const size_t num_queries = queries.size();
195  for (size_t i = 0; i < num_queries; ++i) {
196  const std::vector<std::string> pairs =
197  base::SplitString(queries[i], "=");
199  DCHECK(pairs.size() == 1 || pairs.size() == 2);
200  if (pairs.size() > 1)
201  args[pairs[0]] = pairs[1];
202  else
203  args[pairs[0]] = "";
204  }
205  }
206 }
207 
209  return port > 0 && !hostname.empty();
210 }
211 
213 }
214 
216 }
217 
219 }
220 
221 const HttpClient::Response HttpClient::Get(const std::string& url) const {
222  return SendRequest(Url(url), "GET", "");
223 }
224 
226  const std::string& url, int64 start, int64 end) const {
227  return SendRequest(Url(url), "GET", BuildRangeRequestHeader(start, end));
228 }
229 
230 const HttpClient::Response HttpClient::Head(const std::string& url) const {
231  return SendRequest(Url(url), "HEAD", "");
232 }
233 
234 const HttpClient::Response HttpClient::Post(const std::string& url,
235  const std::string& data) {
236  return SendRequest(Url(url), "POST", BuildUploadHeaders(data));
237 }
238 
239 const HttpClient::Response HttpClient::Put(const std::string& url,
240  const std::string& data) {
241  return SendRequest(Url(url), "PUT", BuildUploadHeaders(data));
242 }
243 
244 } // namespace remote
245 } // namespace ion
virtual const Response Head(const std::string &url) const
Sends a HEAD request for a URL and returns the remote host's response.
Definition: httpclient.cc:230
Simple wrapper around a URL.
Definition: httpclient.h:35
#define DCHECK(expr)
Definition: logging.h:331
virtual const Response GetRange(const std::string &url, int64 start, int64 end) const
Sends a GET request for the passed byte range and returns the remote host's response.
Definition: httpclient.cc:225
#define LOG(severity)
Logs the streamed message unconditionally with a severity of severity.
Definition: logging.h:216
virtual const Response Put(const std::string &url, const std::string &data)
PUTs data to URL and returns the remote host's response.
Definition: httpclient.cc:239
virtual const Response Get(const std::string &url) const
Sends a GET request for a URL and returns the remote host's response.
Definition: httpclient.cc:221
Url()
Constructs an empty, invalid Url.
Definition: httpclient.cc:114
std::vector< std::string > ION_API SplitString(const std::string &str, const std::string &delimiters)
Splits a string into a vector of substrings, given a set of delimiter characters (expressed as a stri...
Definition: stringutils.cc:187
int32 ION_API StringToInt32(const std::string &str)
Extracts and returns an integral value from str.
Definition: stringutils.cc:328
bool IsValid() const
Returns whether this Url is valid.
Definition: httpclient.cc:208
Copyright 2016 Google Inc.
void Set(const std::string &url)
Sets the url from the passed value.
Definition: httpclient.cc:121
virtual const Response Post(const std::string &url, const std::string &data)
POSTs data to URL and returns the remote host's response.
Definition: httpclient.cc:234