Samchon Framework for CPP  1.0.0
WebCommunicator.hpp
1 #pragma once
2 #include <samchon/API.hpp>
3 
4 #include <samchon/protocol/Communicator.hpp>
5 
6 #include <exception>
7 #include <queue>
8 #include <samchon/protocol/WebSocketUtil.hpp>
9 
10 namespace samchon
11 {
12 namespace protocol
13 {
36  : public virtual Communicator
37  {
38  private:
39  bool is_server;
40 
41  public:
42  WebCommunicator(bool is_server)
43  : Communicator()
44  {
45  this->is_server = is_server;
46  };
47  virtual ~WebCommunicator() = default;
48 
49  virtual void sendData(std::shared_ptr<Invoke> invoke) override
50  {
51  std::unique_lock<std::mutex> uk(send_mtx);
52 
53  // SEND INVOKE
54  const std::string &str = invoke->toXML()->toString();
55  if (is_server == true)
56  send_data(str);
57  else // CLIENT MASKS ON SENDING DATA
58  send_masked_data(str);
59 
60  // SEND BINARY
61  for (size_t i = 0; i < invoke->size(); i++)
62  if (invoke->at(i)->getType() == "ByteArray")
63  if (is_server == true)
64  send_data(invoke->at(i)->referValue<ByteArray>());
65  else // CLIENT MASKS ON SENDING DATA
66  send_masked_data(invoke->at(i)->referValue<ByteArray>());
67  };
68 
69  protected:
70  /* =========================================================
71  SOCKET I/O
72  - READ
73  - WRITE
74  ============================================================
75  READ
76  --------------------------------------------------------- */
77  virtual void listen_message() override
78  {
79  std::shared_ptr<Invoke> binary_invoke = nullptr;
80  std::queue<std::shared_ptr<InvokeParameter>> binary_parameters;
81 
82  while (true)
83  {
84  try
85  {
86  const std::pair<unsigned char, size_t> &header = listen_header();
87 
88  // EXIT CODE
89  if (header.first == WebSocketUtil::DISCONNECT)
90  break;
91 
92  // READ DATA
93  if (header.first == WebSocketUtil::TEXT)
94  {
95  std::shared_ptr<Invoke> invoke = listen_string(header.second);
96 
97  for (size_t i = 0; i < invoke->size(); i++)
98  {
99  std::shared_ptr<InvokeParameter> &parameter = invoke->at(i);
100  if (parameter->getType() != "ByteArray")
101  continue;
102 
103  if (binary_invoke == nullptr)
104  binary_invoke = invoke;
105  binary_parameters.push(parameter);
106  }
107 
108  // NO BINARY, THEN REPLY DIRECTLY
109  if (binary_invoke == nullptr)
110  this->replyData(invoke);
111  }
112  else if (header.first == WebSocketUtil::BINARY)
113  {
114  std::shared_ptr<InvokeParameter> parameter = binary_parameters.front();
115  listen_binary(header.second, parameter);
116  binary_parameters.pop();
117 
118  if (binary_parameters.empty() == true)
119  {
120  // NO BINARY PARAMETER LEFT,
121  std::shared_ptr<Invoke> invoke = binary_invoke;
122  binary_invoke = nullptr;
123 
124  // THEN REPLY
125  this->replyData(invoke);
126  }
127  }
128  }
129  catch (...)
130  {
131  break;
132  }
133  }
134  };
135 
136  private:
137  auto listen_header() -> std::pair<unsigned char, size_t>
138  {
139  unsigned char mask = is_server ? WebSocketUtil::MASK : 0;
140 
141  std::array<unsigned char, 2> header_bytes;
142  unsigned char op_code; // false then binary
143  unsigned char size_header;
144 
145  size_t content_size = 0;
146 
147  // READ HEADER BYTES
148  listen_data(header_bytes);
149  op_code = header_bytes[0];
150  size_header = header_bytes[1];
151 
152  // INSPECT MASK VALIDATION
153  if (is_server == true)
154  {
155  if (size_header < WebSocketUtil::MASK)
156  throw std::domain_error("masked message from server has delivered.");
157  }
158  else
159  {
160  if (size_header >= WebSocketUtil::MASK)
161  throw std::domain_error("masked message from server has delivered.");
162  }
163 
164  // EXIT CODE
165  if (op_code == WebSocketUtil::DISCONNECT)
166  return{ op_code, 0 }; // DISCONNECTION SIGNAL HAS ARRIVED
167 
168  // READ CONTENT SIZE
169  if (is_server)
170  size_header -= WebSocketUtil::MASK; // CLIENT SENDS MASKED DATA, DETACH THE MASK
171 
172  if (size_header == (unsigned char)WebSocketUtil::TWO_BYTES)
173  {
174  // SIZE HEADER IS 2 BYTES
175  std::array<unsigned char, 2> size_bytes;
176  listen_data(size_bytes);
177 
178  for (size_t c = 0; c < size_bytes.size(); c++)
179  content_size += size_bytes[c] << (8 * (size_bytes.size() - 1 - c));
180  }
181  else if (size_header == (unsigned char)WebSocketUtil::EIGHT_BYTES)
182  {
183  // SIZE HEADER IS 8 BYTES
184  std::array<unsigned char, 8> size_bytes;
185  listen_data(size_bytes);
186 
187  for (size_t c = 0; c < size_bytes.size(); c++)
188  content_size += size_bytes[c] << (8 * (size_bytes.size() - 1 - c));
189  }
190  else
191  content_size = (size_t)size_header;
192 
193  return{ op_code, content_size };
194  };
195 
196  auto listen_string(size_t size) -> std::shared_ptr<Invoke>
197  {
198  // READ CONTENT
199  std::string data(size, (char)NULL);
200  if (is_server) // CLIENT SENDS MASKED DATA
201  listen_masked_data(data);
202  else
203  listen_data(data);
204 
205  // CONSTRUCT INVOKE OBJECT
206  std::shared_ptr<Invoke> invoke(new Invoke());
207  invoke->construct(std::make_shared<library::XML>(data));
208 
209  return invoke;
210  };
211 
212  void listen_binary(size_t size, std::shared_ptr<InvokeParameter> parameter)
213  {
214  // FETCH BYTE_ARRAY
215  ByteArray &data = (ByteArray&)parameter->referValue<ByteArray>();
216 
217  // READ CONTENT
218  if (is_server == true) // CLIENT SENDS MASKED DATA
219  listen_masked_data(data);
220  else
221  listen_data(data);
222  };
223 
224  template <class Container>
225  void listen_data(Container &data)
226  {
227  size_t completed = 0;
228 
229  if (completed < data.size())
230  completed += socket->read_some(boost::asio::buffer((unsigned char*)data.data() + completed, data.size() - completed));
231  };
232 
233  template <class Container>
234  void listen_masked_data(Container &data)
235  {
236  // READ MASK
237  std::array<unsigned char, 4> mask;
238  socket->read_some(boost::asio::buffer(mask));
239 
240  // READ DATA
241  listen_data(data);
242 
243  // UNMASK
244  for (size_t i = 0; i < data.size(); i++)
245  data[i] = data[i] ^ mask[i % 4];
246  };
247 
248  /* ---------------------------------------------------------
249  WRITE
250  --------------------------------------------------------- */
251  template <class Container>
252  void send_data(const Container &data)
253  {
254  unsigned char op_code = std::is_same<std::string, Container>()
255  ? WebSocketUtil::TEXT
256  : WebSocketUtil::BINARY;
257  size_t size = data.size();
258 
260  // SEND HEADER
262  ByteArray header;
263  header.write(op_code);
264 
265  if (size < 126)
266  header.write((unsigned char)size);
267  else if (size < 0xFFFF)
268  {
269  header.write((unsigned char)(WebSocketUtil::TWO_BYTES));
270  header.writeReversely((unsigned short)size);
271  }
272  else
273  {
274  header.write((unsigned char)(WebSocketUtil::EIGHT_BYTES));
275  header.writeReversely((unsigned long long)size);
276  }
277 
278  socket->write_some(boost::asio::buffer(header)); // SEND HEADER
279  socket->write_some(boost::asio::buffer(data)); // SEND DATA
280  };
281 
282  template <class Container>
283  void send_masked_data(const Container &data)
284  {
285  unsigned char op_code = std::is_same<std::string, Container>()
286  ? WebSocketUtil::TEXT
287  : WebSocketUtil::BINARY;
288  size_t size = data.size();
289 
291  // SEND HEADER
293  ByteArray header;
294  header.write(op_code);
295 
296  if (size < 126)
297  header.write((unsigned char)(size + WebSocketUtil::MASK));
298  else if (size < 0xFFFF)
299  {
300  header.write((unsigned char)(WebSocketUtil::TWO_BYTES + WebSocketUtil::MASK));
301  header.writeReversely((unsigned short)size);
302  }
303  else
304  {
305  header.write((unsigned char)(WebSocketUtil::EIGHT_BYTES + WebSocketUtil::MASK));
306  header.writeReversely((unsigned long long)size);
307  }
308 
310  // SEND DATA
312  static std::uniform_int_distribution<unsigned short> distribution(0, 255);
313  static std::random_device device;
314 
315  // CONSTRUCT MASK
316  std::array<unsigned char, 4> mask;
317  for (size_t i = 0; i < mask.size(); i++)
318  mask[i] = (unsigned char)distribution(device);
319 
320  // TO BE MASKED
321  std::vector<unsigned char> masked_data(data.size());
322  for (size_t i = 0; i < masked_data.size(); i++)
323  masked_data[i] = data[i] ^ mask[i % 4];
324 
325  // SEND
326  socket->write_some(boost::asio::buffer(header)); // SEND HEADER
327  socket->write_some(boost::asio::buffer(mask)); // SEND MASK
328  socket->write_some(boost::asio::buffer(masked_data)); // SEND MASKED DATA
329  };
330  };
331 };
332 };
virtual void replyData(std::shared_ptr< Invoke > invoke)
void writeReversely(const T &val)
Write a data.
Definition: ByteArray.hpp:222
virtual void sendData(std::shared_ptr< Invoke > invoke) override
Standard message of network I/O.
Definition: Invoke.hpp:35
Binary data class.
Definition: ByteArray.hpp:29
void write(const T &val)
Write a data.
Definition: ByteArray.hpp:197