Samchon Framework for CPP  1.0.0
WebServerConnector.hpp
1 #pragma once
2 #include <samchon/API.hpp>
3 
4 #include <samchon/protocol/ServerConnector.hpp>
5 #include <samchon/protocol/WebCommunicator.hpp>
6 
7 #include <map>
8 #include <samchon/library/RWMutex.hpp>
9 #include <samchon/library/StringUtil.hpp>
10 
11 namespace samchon
12 {
13 namespace protocol
14 {
45  : public ServerConnector,
46  public WebCommunicator
47  {
48  private:
49  typedef ServerConnector super;
50 
51  static std::map<std::pair<std::string, int>, std::string> s_cookies;
52  static library::RWMutex s_mtx;
53 
54  public:
55  WebServerConnector(IProtocol *listener)
56  : super(listener),
57  WebCommunicator(false)
58  {
59  };
60  virtual ~WebServerConnector() = default;
61 
81  virtual void connect(const std::string &ip, int port) override
82  {
83  connect(ip, port, "");
84  };
85 
106  virtual void connect(const std::string &ip, int port, const std::string &path)
107  {
108  _Connect(ip, port);
109 
110  handshake(ip, port, path);
111 
112  listen_message();
113  };
114 
115  private:
116  void handshake(const std::string &ip, int port, const std::string &path)
117  {
119  // SEND HEADER
121  // CERTIFICATION KEY
122  std::string &base64_key = WebSocketUtil::generate_base64_certification_key();
123  std::string &sha1_key = WebSocketUtil::encode_certification_key(base64_key);
124 
125  // COOKIE
126  std::string cookie;
127  {
128  library::UniqueReadLock uk(s_mtx);
129  auto it = s_cookies.find({ ip, port });
130 
131  if (it != s_cookies.end())
132  cookie = "Cookie: " + it->second + "\r\n";
133  }
134 
135  // SEND
136  std::string &query = library::StringUtil::substitute
137  (
138  std::string("") +
139  "GET {1} HTTP/1.1\r\n" + //path
140  "Host: {2}\r\n" + //ip:port
141  "Upgrade: websocket\r\n" +
142  "Connection: Upgrade\r\n" +
143  "{3}" + // cookie
144  "Sec-WebSocket-Key: {4}\r\n" + // hashed certification key
145  "Sec-WebSocket-Version: 13\r\n" +
146  "\r\n",
147 
148  path.empty() ? "/" : "/" + path,
149  ip + ":" + std::to_string(port),
150  cookie,
151  base64_key
152  );
153  socket->write_some(boost::asio::buffer(query.data(), query.size()));
154 
156  // LISTEN HEADER
158  std::array<unsigned char, 1000> byte_array;
159  size_t size = socket->read_some(boost::asio::buffer(byte_array));
160 
161  WeakString wstr((const char*)byte_array.data(), size);
162  std::string server_sha1 = wstr.between("Sec-WebSocket-Accept: ", "\r\n").str();
163 
164  // INSPECT VALIDITY
165  if (sha1_key != server_sha1)
166  throw std::domain_error("WebSocket handshaking has failed.");
167 
168  // SET-COOKIE
169  if (wstr.find("Set-Cookie: ") != std::string::npos)
170  {
171  WeakString set_cookie = wstr.between("Set-Cookie: ", "\r\n");
172  library::UniqueWriteLock uk(s_mtx);
173 
174  s_cookies[{ip, port}] = set_cookie.str();
175  }
176  };
177  };
178 };
179 };
auto str() const -> std::string
Get the string content.
Definition: WeakString.hpp:926
virtual void connect(const std::string &ip, int port) override
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
Unique lock for reading.
auto between(const WeakString &start={}, const WeakString &end={}) const -> WeakString
Generates a substring.
Definition: WeakString.hpp:475
Unique lock for writing.
A string class only references characeters, reference only.
Definition: WeakString.hpp:35
virtual void connect(const std::string &ip, int port, const std::string &path)