Samchon Framework for CPP  1.0.0
HTTPLoader.cpp
1 #include <samchon/library/HTTPLoader.hpp>
2 
3 #include <iostream>
4 #include <array>
5 #include <random>
6 
7 #include <chrono>
8 #include <thread>
9 #include <boost/asio.hpp>
10 
11 #include <samchon/library/Datetime.hpp>
12 #include <samchon/library/URLVariables.hpp>
13 #include <samchon/library/StringUtil.hpp>
14 
15 using namespace std;
16 using namespace samchon;
17 using namespace samchon::library;
18 
19 HashMap<string, string> HTTPLoader::cookieMap;
20 
21 void toClipboard(const string &);
22 
23 /* ------------------------------------------------------------
24  CONSTRUCTORS
25 ------------------------------------------------------------ */
26 HTTPLoader::HTTPLoader(int method)
27 {
28  this->method = method;
29 }
30 HTTPLoader::HTTPLoader(const string &url, int method)
31  : HTTPLoader(method)
32 {
33  this->url = url;
34 }
35 
36 /* ------------------------------------------------------------
37  SETTERS & GETTERS
38 ------------------------------------------------------------ */
39 void HTTPLoader::setURL(const string &url)
40 {
41  this->url = url;
42 }
43 void HTTPLoader::setMethod(int method)
44 {
45  this->method = method;
46 }
47 
48 auto HTTPLoader::getURL() const -> string
49 {
50  return url;
51 }
52 auto HTTPLoader::getMethod() const -> int
53 {
54  return method;
55 }
56 
57 auto HTTPLoader::getCookie(const string &host) const -> string
58 {
59  auto it = cookieMap.find(host);
60 
61  if (it == cookieMap.end())
62  return "";
63  else
64  return it->second;
65 
66  /*static string session;
67  if (session.empty() == false)
68  return session;
69 
70  static vector<char> ALPHA_CHAR_CODES = {48, 49, 50, 51, 52, 53, 54,
71  55, 56, 57, 65, 66, 67, 68, 69, 70};
72 
73  static random_device device;
74  static uniform_int_distribution<size_t> distribution(0, ALPHA_CHAR_CODES.size() - 1);
75 
76  string val;
77  size_t i, j;
78  Datetime now;
79 
80  for (i = 0; i < 8; i++)
81  val.push_back(ALPHA_CHAR_CODES[distribution(device)]);
82 
83  for (i = 0; i < 3; i++)
84  {
85  val.push_back('-');
86 
87  for (j = 0; j < 4; j++)
88  val.push_back(ALPHA_CHAR_CODES[distribution(device)]);
89  }
90  val.push_back('-');
91  val.append(to_string(now.toLinuxTime()));
92 
93  session = "s_pers=" + URLVariables::encode(" s_fid=" + val + ";");
94  return session;*/
95 }
96 
97 /* ------------------------------------------------------------
98  LOADERS
99 ------------------------------------------------------------ */
100 auto HTTPLoader::load(const URLVariables &parameters) const -> ByteArray
101 {
103  // SENDING REQUEST HEADER
105  // FOR HEADER
106  WeakString host = url;
107  string path;
108 
109  if (host.find("://") != string::npos)
110  host = host.between("://");
111  if (host.find("/") != string::npos)
112  {
113  path = "/" + host.between("/").str();
114  host = host.between("", "/");
115  }
116 
117  // ENCODIG PATH
118  {
119  size_t idx = path.find('?');
120  if (idx == string::npos)
121  path = URLVariables::encode(path);
122  else
123  {
124  string &front = path.substr(0, idx);
125  string &back = path.substr(idx + 1);
126 
127  path = URLVariables::encode(front) + "?" + back;
128  }
129  }
130 
131  string header;
132  if (method == GET)
133  {
134  header = StringUtil::substitute
135  (
136  string("") +
137  "GET {2}{3} HTTP/1.1\n" +
138  "Host: {1}\n" +
139  "Accept: */*\n" +
140  "Accept-Encoding: gzip, deflate\n"
141 
142  "Connection: Keep-Alive\n" +
143  "Cookie: {4}\n"
144  "\n",
145 
146  host.str(), path,
147  ((parameters.empty() == true)
148  ? string("")
149  : "?" + parameters.toString()),
150  getCookie(host)
151  );
152  }
153  else
154  {
155  std::string &parameterStr = parameters.toString();
156 
157  header = StringUtil::substitute
158  (
159  string("") +
160  "POST {2} HTTP/1.1\n" +
161  "Host: {1}\n" +
162  "Accept: */*\n" +
163  "Connection: Keep-Alive\n" +
164 
165  /*"Accept-Language: en-US\n" +
166  "Accept-Encoding: gzip, deflate\n" +
167  "User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko\n" +*/
168 
169  "Content-Type: application/x-www-form-urlencoded\n" +
170  "Content-Length: {3}\n" +
171  "Cookie: {5}\n"
172  "\n" +
173  "{4}",
174 
175  host.str(), path,
176  parameterStr.size(), parameterStr,
177  getCookie(host)
178  );
179  }
180 
181  // PREPARE & GET IP ADDRESS
182  boost::asio::io_service ioService;
183 
184  boost::asio::ip::tcp::resolver resolver(ioService);
185  boost::asio::ip::tcp::resolver::query query(host, "http");
186  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
187 
188  // SOCKET - CONNECT AND SEND HEADER
189  boost::asio::ip::tcp::socket socket(ioService);
190  socket.connect(*endpoint_iterator);
191 
192  socket.write_some(boost::asio::buffer(header));
193 
195  // LISTEN HEADER FROM SERVER
197  HashMap<string, string> headerMap;
198  {
199  header.clear();
200 
201  while (true)
202  {
203  array<char, 1> buffer;
204 
205  socket.read_some(boost::asio::buffer(buffer));
206  header += buffer[0];
207 
208  if (header.size() > 4 && header.substr(header.size() - 4) == "\r\n\r\n")
209  break;
210  }
211 
212  WeakString wstr = header;
213  vector<WeakString> wstrArray = wstr.split("\r\n");
214 
215  for (size_t i = 0; i < wstrArray.size(); i++)
216  {
217  WeakString wstr = wstrArray[i];
218  size_t index = wstr.find(":");
219 
220  if (index == string::npos)
221  continue;
222 
223  headerMap.set(wstr.substr(0, index), wstr.substr(index + 1).trim());
224  }
225  }
226 
227  // REGISTER COOKIE
228  if (headerMap.has("Set-Cookie") == true)
229  {
230  string &cookie = headerMap.get("Set-Cookie");
231 
232  ((HashMap<string, string>*)&cookieMap)->set(host, cookie);
233  }
234 
235  // CONTENT-LENGTH
236  bool reserved = headerMap.has("Content-Length");
237  bool chunked = headerMap.has("Transfer-Encoding") && headerMap.get("Transfer-Encoding") == "chunked";
238 
239  /*boost::asio::streambuf response;
240  {
241  boost::asio::read_until(socket, response, "\r\n\r\n");
242 
243  std::istream response_stream(&response);
244  string item;
245 
246  while (std::getline(response_stream, item) && item != "\r")
247  {
248  size_t index = item.find(":");
249  if (index == string::npos)
250  continue;
251 
252  string &key = item.substr(0, index);
253  string &value = item.substr(index + 2);
254 
255  if (value.back() == '\r')
256  value.pop_back();
257 
258  headerMap.insert({ key, value });
259  }
260 
261  // REGISTER COOKIE
262  if (headerMap.has("Set-Cookie") == true)
263  {
264  string &cookie = headerMap.get("Set-Cookie");
265 
266  ((Map<string, string>*)&cookieMap)->set(host, cookie);
267  }
268  }*/
269 
271  // GET DATA
273  ByteArray data;
274 
275  if (reserved == true)
276  {
277  // CONTENT-LENGTH
278  data.reserve((size_t)stoull(headerMap.get("Content-Length")));
279 
280  while (true)
281  {
282  array<unsigned char, 1000> piece;
283  boost::system::error_code error;
284 
285  size_t size = socket.read_some(boost::asio::buffer(piece), error);
286  if (size == 0 || error)
287  break;
288 
289  data.insert
290  (
291  data.end(),
292  piece.begin(), piece.begin() + size
293  );
294 
295  if (data.size() == data.capacity())
296  break;
297  }
298  }
299 
300  /*else if (chunked == true)
301  {
302  while (true)
303  {
304  string chunk;
305  boost::system::error_code error;
306 
307  while (true)
308  {
309  array<char, 1> chunkPiece;
310  socket.read_some(boost::asio::buffer(chunkPiece), error);
311 
312  if (error)
313  return data;
314 
315  chunk += chunkPiece[0];
316  if (chunk.size() > 2 && chunk.substr(chunk.size() - 2) == "\r\n")
317  break;
318  }
319 
320  size_t size = (size_t)stoull(chunk.substr(0, chunk.size() - 1), 0, 16);
321  if (size == 0)
322  break;
323 
324  vector<unsigned char> piece(size + 2, NULL);
325  socket.read_some(boost::asio::buffer(piece), error);
326 
327  if (error)
328  break;
329 
330  data.insert(data.end(), piece.begin(), piece.begin() + size);
331  }
332  }*/
333 
334  else if (chunked == true)
335  {
336  vector<unsigned char> prevData;
337 
338  while (true)
339  {
340  array<char, 1000> piece;
341  boost::system::error_code error;
342 
343  size_t size = socket.read_some(boost::asio::buffer(piece), error);
344  if (size == 0 || error)
345  break;
346 
347  prevData.insert(prevData.end(), piece.begin(), piece.begin() + size);
348 
349  // HANDLING LAST
350  WeakString wstr((const char*)&prevData[0], (const char*)&prevData[0] + prevData.size());
351 
352  if (wstr.substring(wstr.size() - 7, wstr.size()) == "\r\n0\r\n\r\n")
353  {
354  // FINDS ALL \R\N
355  /*vector<WeakString> wstrArray = wstr.split("\r\n");
356 
357  for (size_t i = 0; i < wstrArray.size(); i++)
358  {
359  WeakString wstr = wstrArray[i];
360  bool isChunkLine = true;
361 
362  for (size_t j = 0; j < wstr.size(); j++)
363  {
364  char ch = wstr[j];
365 
366  if (!(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f')))
367  {
368  isChunkLine = false;
369  break;
370  }
371  }
372 
373  if (isChunkLine == false)
374  postStr += wstr.str() + "\r\n";
375  else
376  cout << wstr.str() << endl;
377  }*/
378 
379  size_t startIndex = 0;
380  size_t endIndex;
381 
382  while (true)
383  {
384  size_t pos = wstr.find("\r\n", startIndex);
385  WeakString piece = wstr.substr(startIndex, pos);
386 
387  size_t size = stoull(piece, 0, 16);
388  if (size == 0)
389  break;
390 
391  startIndex = pos + 2;
392  endIndex = std::min(startIndex + size, prevData.size());
393 
394  data.insert(data.end(), prevData.begin() + startIndex, prevData.begin() + endIndex);
395  startIndex = endIndex + 2;
396  }
397 
398  break;
399  }
400  }
401 
402  /*ByteArray data;
403  data.write(postStr);
404 
405  return data;*/
406  }
407  else
408  {
409  while (true)
410  {
411  array<unsigned char, 1000> piece;
412  boost::system::error_code error;
413 
414  size_t size = socket.read_some(boost::asio::buffer(piece), error);
415  if (size == 0 || error)
416  break;
417 
418  data.insert
419  (
420  data.end(),
421  piece.begin(), piece.begin() + size
422  );
423  }
424  }
425 
426  // RETURN
427  return data;
428 }
429 
430 /*#include <Windows.h>
431 
432 void toClipboard(const string &str)
433 {
434  OpenClipboard(0);
435  EmptyClipboard();
436  HGLOBAL hg = GlobalAlloc(GMEM_MOVEABLE, str.size());
437 
438  if (!hg)
439  {
440  CloseClipboard();
441  return;
442  }
443  memcpy(GlobalLock(hg), str.c_str(), str.size());
444 
445  GlobalUnlock(hg);
446  SetClipboardData(CF_TEXT, hg);
447  CloseClipboard();
448  GlobalFree(hg);
449 }*/
auto has(const Key &key) const -> bool
Whether have the item or not.
Definition: HashMap.hpp:127
auto substr(size_t startIndex, size_t endIndex=SIZE_MAX) const -> WeakString
Generates a substring.
Definition: WeakString.cpp:174
Definition: RWMutex.hpp:4
auto split(const WeakString &delim) const -> std::vector< WeakString >
Generates substrings.
Definition: WeakString.cpp:212
auto str() const -> std::string
Get the string content.
Definition: WeakString.cpp:546
Package of libraries.
Definition: library.hpp:84
auto get(const Key &key) -> T &
Get element.
Definition: HashMap.hpp:146
A http, web-page loader.
Definition: HTTPLoader.hpp:23
auto find(const WeakString &delim, size_t startIndex=NULL) const -> size_t
Finds first occurence in string.
Definition: WeakString.cpp:91
Binary data class.
Definition: ByteArray.hpp:30
void set(const Key &key, const T &val)
Set element.
Definition: HashMap.hpp:167
auto between(const WeakString &start={}, const WeakString &end={}) const -> WeakString
Generates a substring.
Definition: WeakString.cpp:194
Top level namespace of products built from samchon.
Definition: ByteArray.hpp:7
Customized std::unordered_map.
Definition: HashMap.hpp:105
A string class only references characeters, reference only.
Definition: WeakString.hpp:32
URLVariables class is for representing variables of HTTP.