Samchon Framework for CPP  1.0.0
HTTPLoader.hpp
1 #pragma once
2 #include <samchon/API.hpp>
3 
4 #include <thread>
5 #include <boost/asio.hpp>
6 #include <samchon/ByteArray.hpp>
7 #include <samchon/library/URLVariables.hpp>
8 
9 #include <array>
10 #include <random>
11 #include <chrono>
12 #include <samchon/library/Date.hpp>
13 #include <samchon/library/StringUtil.hpp>
14 
15 namespace samchon
16 {
17 namespace library
18 {
26  class HTTPLoader
27  {
28  private:
29  std::string url;
30 
34  int method;
35 
40  {
42  return map;
43  };
44 
45  public:
46  enum METHOD : int
47  {
48  GET = 1,
49  POST = 2
50  };
51 
52  public:
53  /* ------------------------------------------------------------
54  CONSTRUCTORS
55  ------------------------------------------------------------ */
62  HTTPLoader(const std::string &url, int method = POST)
63  {
64  this->url = url;
65  this->method = method;
66  };
67  virtual ~HTTPLoader() = default;
68 
69  /* ------------------------------------------------------------
70  SETTERS & GETTERS
71  ------------------------------------------------------------ */
75  void setURL(const std::string &val)
76  {
77  this->url = val;
78  };
79 
83  void setMethod(int val)
84  {
85  this->method = method;
86  };
87 
91  auto getURL() const -> std::string
92  {
93  return url;
94  };
95 
99  auto getMethod() const -> int
100  {
101  return method;
102  };
103 
107  auto getCookie(const std::string &key) const -> std::string
108  {
109  auto it = cookie_map().find(key);
110 
111  if (it == cookie_map().end())
112  return "";
113  else
114  return it->second;
115  };
116 
117  /* ------------------------------------------------------------
118  LOADERS
119  ------------------------------------------------------------ */
128  auto load(const URLVariables &parameters = {}) const -> ByteArray
129  {
131  // SENDING REQUEST HEADER
133  // FOR HEADER
134  WeakString host = url;
135  std::string path;
136 
137  if (host.find("://") != std::string::npos)
138  host = host.between("://");
139  if (host.find("/") != std::string::npos)
140  {
141  path = "/" + host.between("/").str();
142  host = host.between("", "/");
143  }
144 
145  // ENCODIG PATH
146  {
147  size_t idx = path.find('?');
148  if (idx == std::string::npos)
149  path = URLVariables::encode(path);
150  else
151  {
152  std::string &front = path.substr(0, idx);
153  std::string &back = path.substr(idx + 1);
154 
155  path = URLVariables::encode(front) + "?" + back;
156  }
157  }
158 
159  std::string header;
160  if (method == GET)
161  {
162  header = StringUtil::substitute
163  (
164  std::string("") +
165  "GET {2}{3} HTTP/1.1\n" +
166  "Host: {1}\n" +
167  "Accept: */*\n" +
168  "Accept-Encoding: gzip, deflate\n"
169 
170  "Connection: Keep-Alive\n" +
171  "Cookie: {4}\n"
172  "\n",
173 
174  host.str(), path,
175  ((parameters.empty() == true)
176  ? std::string("")
177  : "?" + parameters.toString()),
178  getCookie(host)
179  );
180  }
181  else
182  {
183  std::string &parameterStr = parameters.toString();
184 
185  header = StringUtil::substitute
186  (
187  std::string("") +
188  "POST {2} HTTP/1.1\n" +
189  "Host: {1}\n" +
190  "Accept: */*\n" +
191  "Connection: Keep-Alive\n" +
192 
193  /*"Accept-Language: en-US\n" +
194  "Accept-Encoding: gzip, deflate\n" +
195  "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\n" +*/
196 
197  "Content-Type: application/x-www-form-urlencoded\n" +
198  "Content-Length: {3}\n" +
199  "Cookie: {5}\n"
200  "\n" +
201  "{4}",
202 
203  host.str(), path,
204  parameterStr.size(), parameterStr,
205  getCookie(host)
206  );
207  }
208 
209  // PREPARE & GET IP ADDRESS
210  boost::asio::io_service ioService;
211 
212  boost::asio::ip::tcp::resolver resolver(ioService);
213  boost::asio::ip::tcp::resolver::query query(host, "http");
214  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
215 
216  // SOCKET - CONNECT AND SEND HEADER
217  boost::asio::ip::tcp::socket socket(ioService);
218  socket.connect(*endpoint_iterator);
219 
220  socket.write_some(boost::asio::buffer(header));
221 
223  // LISTEN HEADER FROM SERVER
226  {
227  header.clear();
228 
229  while (true)
230  {
231  std::array<char, 1> buffer;
232 
233  size_t piece_size = socket.read_some(boost::asio::buffer(buffer));
234  header += buffer[0];
235 
236  if (header.size() > 4 && header.substr(header.size() - 4) == "\r\n\r\n")
237  break;
238  }
239 
240  WeakString wstr = header;
241  std::vector<WeakString> wstrArray = wstr.split("\r\n");
242 
243  for (size_t i = 0; i < wstrArray.size(); i++)
244  {
245  WeakString wstr = wstrArray[i];
246  size_t index = wstr.find(":");
247 
248  if (index == std::string::npos)
249  continue;
250 
251  headerMap.set(wstr.substr(0, index), wstr.substr(index + 1).trim());
252  }
253  }
254 
255  // REGISTER COOKIE
256  if (headerMap.has("Set-Cookie") == true)
257  {
258  std::string &cookie = headerMap.get("Set-Cookie");
259 
260  ((HashMap<std::string, std::string>*)&cookie_map())->set(host, cookie);
261  }
262 
263  // CONTENT-LENGTH
264  bool reserved = headerMap.has("Content-Length");
265  bool chunked = headerMap.has("Transfer-Encoding") && headerMap.get("Transfer-Encoding") == "chunked";
266 
268  // GET DATA
270  ByteArray data;
271 
272  if (reserved == true)
273  {
274  // CONTENT-LENGTH
275  data.reserve((size_t)stoull(headerMap.get("Content-Length")));
276 
277  while (true)
278  {
279  std::array<unsigned char, 1000> piece;
280  boost::system::error_code error;
281 
282  size_t size = socket.read_some(boost::asio::buffer(piece), error);
283  if (size == 0 || error)
284  break;
285 
286  data.insert
287  (
288  data.end(),
289  piece.begin(), piece.begin() + size
290  );
291 
292  if (data.size() == data.capacity())
293  break;
294  }
295  }
296  else if (chunked == true)
297  {
298  std::vector<unsigned char> prevData;
299 
300  while (true)
301  {
302  std::array<char, 1000> piece;
303  boost::system::error_code error;
304 
305  size_t size = socket.read_some(boost::asio::buffer(piece), error);
306  if (size == 0 || error)
307  break;
308 
309  prevData.insert(prevData.end(), piece.begin(), piece.begin() + size);
310 
311  // HANDLING LAST
312  WeakString wstr((const char*)&prevData[0], (const char*)&prevData[0] + prevData.size());
313 
314  if (wstr.substring(wstr.size() - 7, wstr.size()) == "\r\n0\r\n\r\n")
315  {
316  size_t startIndex = 0;
317  size_t endIndex;
318 
319  while (true)
320  {
321  size_t pos = wstr.find("\r\n", startIndex);
322  WeakString piece = wstr.substr(startIndex, pos);
323 
324  size_t size = stoull(piece.str(), 0, 16);
325  if (size == 0)
326  break;
327 
328  startIndex = pos + 2;
329  endIndex = (startIndex + size < prevData.size()) ? startIndex + size : prevData.size();
330 
331  data.insert(data.end(), prevData.begin() + startIndex, prevData.begin() + endIndex);
332  startIndex = endIndex + 2;
333  }
334 
335  break;
336  }
337  }
338  }
339  else
340  {
341  while (true)
342  {
343  std::array<unsigned char, 1000> piece;
344  boost::system::error_code error;
345 
346  size_t size = socket.read_some(boost::asio::buffer(piece), error);
347  if (size == 0 || error)
348  break;
349 
350  data.insert
351  (
352  data.end(),
353  piece.begin(), piece.begin() + size
354  );
355  }
356  }
357 
358  // RETURN
359  return data;
360  };
361  };
362 };
363 };
auto getCookie(const std::string &key) const -> std::string
Get cookie.
Definition: HTTPLoader.hpp:107
auto str() const -> std::string
Get the string content.
Definition: WeakString.hpp:926
auto has(const Key &key) const -> bool
Whether have the item or not.
Definition: HashMap.hpp:125
auto get(const Key &key) -> T &
Get element.
Definition: HashMap.hpp:144
A http, web-page loader.
Definition: HTTPLoader.hpp:26
static auto substitute(const std::string &format, const T &val, const _Args &...args) -> std::string
Substitutes "{n}" tokens within the specified string with the respective arguments passed in...
Definition: StringUtil.hpp:54
static auto encode(const WeakString &wstr) -> std::string
Encode a string into a valid URI.
auto between(const WeakString &start={}, const WeakString &end={}) const -> WeakString
Generates a substring.
Definition: WeakString.hpp:475
int method
Method, Get or Post.
Definition: HTTPLoader.hpp:34
auto getURL() const -> std::string
Get url.
Definition: HTTPLoader.hpp:91
static HashMap< std::string, std::string > & cookie_map()
Cookies got from remote web server.
Definition: HTTPLoader.hpp:39
HTTPLoader(const std::string &url, int method=POST)
Construct from request url and method.
Definition: HTTPLoader.hpp:62
auto find(const WeakString &delim, size_t startIndex=NULL) const -> size_t
Finds first occurence in string.
Definition: WeakString.hpp:273
auto getMethod() const -> int
Get method.
Definition: HTTPLoader.hpp:99
Binary data class.
Definition: ByteArray.hpp:29
auto load(const URLVariables &parameters={}) const -> ByteArray
Load data from target url.
Definition: HTTPLoader.hpp:128
auto substr(size_t startIndex, size_t size=SIZE_MAX) const -> WeakString
Generates a substring.
Definition: WeakString.hpp:419
void set(const Key &key, const T &val)
Set element.
Definition: HashMap.hpp:165
auto split(const WeakString &delim) const -> std::vector< WeakString >
Generates substrings.
Definition: WeakString.hpp:502
void setURL(const std::string &val)
Set url.
Definition: HTTPLoader.hpp:75
A string class only references characeters, reference only.
Definition: WeakString.hpp:35
URLVariables class is for representing variables of HTTP.
void setMethod(int val)
Set method.
Definition: HTTPLoader.hpp:83