util.hpp
1 #ifndef XXHR_UTIL_H
2 #define XXHR_UTIL_H
3 
4 #include <cctype>
5 #include <cstdint>
6 #include <iomanip>
7 #include <sstream>
8 #include <string>
9 #include <vector>
10 
11 #include <boost/xpressive/xpressive.hpp>
12 #include <boost/archive/iterators/binary_from_base64.hpp>
13 #include <boost/archive/iterators/base64_from_binary.hpp>
14 #include <boost/archive/iterators/transform_width.hpp>
15 #include <boost/algorithm/string.hpp>
16 
17 #include "xxhrtypes.hpp"
18 
19 
20 namespace xxhr {
21 namespace util {
22 
23  inline Header parseHeader(const std::string& headers);
24  inline size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data);
25  inline std::vector<std::string> split(const std::string& to_split, char delimiter);
26 
30  inline std::string urlEncode(const std::string& response);
31  inline std::string decode64(const std::string &val);
32  inline std::string encode64(const std::string &val);
33 
37  struct url_parts {
38 
40  std::string protocol;
41 
43  std::string host;
44 
46  std::string port;
47 
49  std::string path;
50 
52  std::string parameters;
53 
55  std::string fragment;
56 
58  bool https() const {
59  return protocol == "https";
60  }
61  };
62 
64  inline url_parts parse_url(const std::string &url);
65 
66 } // namespace util
67 } // namespace xxhr
68 
69 
70 /*
71  * Implementation
72  */
73 
74 namespace xxhr {
75 namespace util {
76 
77  inline Header parseHeader(const std::string& headers) {
78  Header header;
79  std::vector<std::string> lines;
80  std::istringstream stream(headers);
81  {
82  std::string line;
83  while (std::getline(stream, line, '\n')) {
84  lines.push_back(line);
85  }
86  }
87 
88  for (auto& line : lines) {
89  if (line.substr(0, 5) == "HTTP/") {
90  header.clear();
91  }
92 
93  if (line.length() > 0) {
94  auto found = line.find(":");
95  if (found != std::string::npos) {
96  auto value = line.substr(found + 2, line.length() - 1);
97  if (value.back() == '\r') {
98  value = value.substr(0, value.length() - 1);
99  }
100  header[line.substr(0, found)] = value;
101  }
102  }
103  }
104 
105  return header;
106  }
107 
108  std::vector<std::string> split(const std::string& to_split, char delimiter) {
109  std::vector<std::string> tokens;
110 
111  std::stringstream stream(to_split);
112  std::string item;
113  while (std::getline(stream, item, delimiter)) {
114  tokens.push_back(item);
115  }
116 
117  return tokens;
118  }
119 
120  inline size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) {
121  data->append((char*) ptr, size * nmemb);
122  return size * nmemb;
123  }
124 
125  inline std::string urlEncode(const std::string& value) {
126  std::ostringstream escaped;
127  escaped.fill('0');
128  escaped << std::hex;
129 
130  for (auto i = value.cbegin(), n = value.cend(); i != n; ++i) {
131  std::string::value_type c = (*i);
132  // Keep alphanumeric and other accepted characters intact
133  if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {
134  escaped << c;
135  continue;
136  }
137  // Any other characters are percent-encoded
138  escaped << '%' << std::setw(2) << std::int32_t((unsigned char) c);
139  }
140 
141  return escaped.str();
142  }
143 
144  inline std::string decode64(const std::string &val) {
145  using namespace boost::archive::iterators;
146  using It = transform_width<binary_from_base64<std::string::const_iterator>, 8, 6>;
147  return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) {
148  return c == '\0';
149  });
150  }
151 
152  inline std::string encode64(const std::string &val) {
153  using namespace boost::archive::iterators;
154  using It = base64_from_binary<transform_width<std::string::const_iterator, 6, 8>>;
155  auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
156  return tmp.append((3 - val.size() % 3) % 3, '=');
157  }
158 
159 
160  const boost::xpressive::sregex url_regex =
161  boost::xpressive::sregex::compile("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
162 
163  inline url_parts parse_url(const std::string &url) {
164  url_parts ret;
165 
166  // Way too slow on MSVC (55 seconds in avg for an URI, less than one ms on Gcc / clang )
167  //std::regex ex("(http|https)://([^/ :]+):?([^/ ]*)(/?[^ #?]*)\\x3f?([^ #]*)#?([^ ]*)");
168  //std::smatch what;
169  //if (regex_match(url, what, ex)) {
170  using namespace boost::xpressive;
171  smatch what;
172  if( regex_match( url, what, url_regex ) ) {
173 
174  ret.protocol = what[1];
175  ret.host = what[2];
176  ret.port = what[3];
177 
178  if (ret.port.empty()) {
179  ret.port = (ret.protocol == "https") ? "443" : "80";
180  }
181 
182  ret.path = what[4];
183  ret.parameters = what[5];
184  ret.fragment = what[6];
185  }
186 
187  return ret;
188  }
189 
190 
191 } // namespace util
192 } // namespace xxhr
193 
194 #endif
main library namespace
Definition: api.hpp:19
bool https() const
Wether the url requires TLS.
Definition: util.hpp:58
std::map< std::string, std::string, CaseInsensitiveCompare > Header
HTTP Headers to add to the request or received in xxhr::Response.
Definition: xxhrtypes.hpp:23
Parts of a parsed url.
Definition: util.hpp:37