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