HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
NET_WebSocket.h
Go to the documentation of this file.
1 /*
2  * PROPRIETARY INFORMATION. This software is proprietary to
3  * Side Effects Software Inc., and is not to be reproduced,
4  * transmitted, or disclosed in any way without written permission.
5  *
6  * NAME: NET_WebSocket.h
7  *
8  * COMMENTS:
9  * class used to handle websockets
10  */
11 
12 #ifndef __NET_WEBSOCKET_H__
13 #define __NET_WEBSOCKET_H__
14 
15 #include "NET_API.h"
16 
17 #include "NET_CircularBuffer.h"
18 #include "NET_Time.h"
19 #include "NET_WebAPIResponse.h"
20 #include "NET_WebStream.h"
21 
22 #include <UT/UT_StringHolder.h>
23 #include <UT/UT_StringArray.h>
24 #include <UT/UT_Url.h>
25 #include <UT/UT_SharedPtr.h>
26 #include <UT/UT_NetPacket.h>
27 #include <UT/UT_Lock.h>
28 #include <UT/UT_Map.h>
29 #include <UT/UT_ConcurrentQueue.h>
30 #include <UT/UT_FastRandom.h>
31 
32 #include <SYS/SYS_AtomicInt.h>
33 
34 #include <hboost/uuid/uuid.hpp>
35 #include <hboost/uuid/uuid_generators.hpp>
36 #include <hboost/uuid/uuid_io.hpp>
37 #include <hboost/any.hpp>
38 
39 #include <future>
40 
41 template <typename T>
42 using NET_Promise = std::promise<T>;
43 template <typename T>
44 using NET_Future = std::future<T>;
45 
46 /// Class used for web socket operations.
47 class NET_API NET_WebSocket : public UTenable_shared_from_this<NET_WebSocket>
48 {
49 private:
50  using OpCode = char;
51  static constexpr OpCode OpCode_Continue = 0x0;
52  static constexpr OpCode OpCode_Utf8 = 0x01;
53  static constexpr OpCode OpCode_Binary = 0x02;
54  static constexpr OpCode OpCode_Terminate = 0x08;
55  static constexpr OpCode OpCode_Ping = 0x09;
56  static constexpr OpCode OpCode_Pong = 0x0a;
57 
58 public:
59  NET_WebSocket();
60  virtual ~NET_WebSocket() = default;
61 
62  using Mask = uint32;
63  /// @brief The currently supported different frame types.
64  enum FrameType : OpCode
65  {
66  Continue = OpCode_Continue,
67  Utf8 = OpCode_Utf8,
68  Binary = OpCode_Binary,
69  Terminate = OpCode_Terminate,
70  Ping = OpCode_Ping,
71  Pong = OpCode_Pong
72  };
73 
74  /// Class used to read a websocket frame. This class solves partial reads
75  /// and handles bad frames as well.
76  class NET_API Buffer
77  {
78  public:
79  Buffer();
80 
81  enum class ParseResult
82  {
83  Error,
84  Incomplete,
85  Timeout,
86  Complete
87  };
88 
89  ParseResult read(
91  const std::chrono::milliseconds& timeout,
92  bool& finished,
93  bool& is_client_closed);
94  ParseResult readFrame(
96  const std::chrono::milliseconds& timeout,
97  bool &finished,
98  bool &is_client_closed);
99 
100  FrameType frameType() const { return myMessageType; }
101 
102  void reset();
103 
105  {
106  return UT_StringHolder(myData.data(), myData.entries());
107  }
108 
109  private:
110  enum class ParseState
111  {
112  UNKNOWN,
113  HEADER_1,
114  HEADER_2,
115  PAYLOAD_EXT_ONE,
116  PAYLOAD_EXT_TWO,
117  PAYLOAD_MASK_KEY,
118  PAYLOAD
119  };
120 
121  ParseResult parse_(UT_StringView& chunk);
122  ParseResult readAndParse_(
124  const std::chrono::milliseconds& timeout,
125  bool& is_client_closed);
126  void resetForNextFrame_();
127 
128  UT_Array<char> myData;
129  exint myFrameLength;
130  exint myFrameIndex;
131  /// @brief The frame type for the full message.
132  NET_WebSocket::FrameType myMessageType;
133  /// @brief The frame type for this current frame.
134  NET_WebSocket::FrameType myFrameType;
135  unsigned myIsMasked : 1, myIsFinished : 1;
136  ParseState myCurrentState;
137  uint8 myMask[4];
138  };
139 
140  /// @brief All possible error codes that could occur to be sent to the
141  /// onError callback.
143  {
144  READ_FRAME
145  };
146 
147  class NET_API Event
148  {
149  public:
150  enum Type
151  {
163  CONNECT_ERROR
164  };
166 
169  };
170 
171  enum State
172  {
176  CLOSED
177  };
178 
179  /// Read an entire message in. Really only useful for testing purposes.
180  static bool readAll(NET_WebStream *stream,
181  UT_WorkBuffer &msg,
182  FrameType &type);
183  /// Write a websocket message to the stream.
184  static bool write(NET_WebStream *stream,
185  FrameType type,
186  const UT_StringRef &message,
187  Mask mask = 0,
188  exint max_frame_size = -1);
189  /// Package a frame which is suitable for sending through a web stream.
190  static bool makeFrame(UT_WorkBuffer &frame,
191  FrameType,
192  const UT_StringView mview,
193  Mask mask = 0,
194  bool is_finished = true);
195  /// Package up the message into the full message suitable for sending on the
196  /// socket.
197  static bool makeMessage(
198  UT_WorkBuffer& full_message,
199  FrameType type,
200  const UT_StringRef& message,
201  Mask mask,
202  exint max_fram_size);
203 
204  template <typename SocketT = NET_WebSocket, typename... Args>
206  const UT_Url& url,
207  const std::chrono::milliseconds& timeout,
208  Args&&... args)
209  {
211  = UTmakeShared<SocketT>(std::forward<Args>(args)...);
212  ws->reconnect(url, timeout);
213 
214  return ws;
215  }
216 
217  /// @brief Convert the frame type to string representation of this frame type.
218  ///
219  /// @param type The frame type to convert.
220  ///
221  /// @return The string representation of the frame type.
223  {
224  switch(type)
225  {
226  case Continue:
227  return UT_StringLit("Continue");
228  case Utf8:
229  return UT_StringLit("Utf8");
230  case Binary:
231  return UT_StringLit("Binary");
232  case Ping:
233  return UT_StringLit("Ping");
234  case Pong:
235  return UT_StringLit("Pong");
236  case Terminate:
237  return UT_StringLit("Terminate");
238  default:
239  return UT_StringLit();
240  };
241  return UT_StringLit();
242  }
243 
244  // +++++++
245  // NB: The ability to change this is STRICTLY ONLY for test purposes and is
246  // not meant to be changed in product under and circumstances!!!
247  // +++++++
249 
250  /// @brief The file descriptor of this websocket.
251  ///
252  /// @return The file descriptor
253  int getFD() const
254  {
255  if (!myStream)
256  return -1;
257  return myStream->getFD();
258  }
259 
260  /// @brief The url this websocket is using.
261  ///
262  /// @return The url object representing the used url.
263  const UT_Url& url() const { return myUrl; }
264 
265  /// @brief Send a message to the other end of the websocket. This function
266  /// will send the data immediately and does NOT queue the data.
267  /// This is a blocking function.
268  ///
269  /// @param data
270  bool send(const UT_StringHolder &data)
271  {
272  UT_ASSERT(isConnected());
273 
274  return internalSend(NET_WebSocket::Utf8, data, myMaxFrameSize);
275  }
276  bool reconnect(const UT_Url& url, const std::chrono::milliseconds& timeout);
277  virtual bool disconnect();
278 
279  /// @brief Send a ping to the server. Regular use does not need to worry
280  /// about this. Other abstractions will do this for you
282  {
283  UT_ASSERT(isConnected());
284  internalSend(NET_WebSocket::Ping, ping, myMaxFrameSize);
285  myIsWaitingForPong = true;
286  }
288  {
289  UT_ASSERT(isConnected());
290 
291  internalSend(NET_WebSocket::Pong, pong, myMaxFrameSize);
292  }
293 
294  /// @brief Tell if this websocket is currently in use
295  ///
296  /// @return True if this websocket is still in use.
297  bool isConnected() const { return myState == State::OPEN; }
298  bool usableStream() const { return myStream && myStream->isValid(); }
299 
300  /// @brief The current max frame size used.
301  ///
302  /// @return The max frame size
303  uint32 maxFrameSize() const { return myMaxFrameSize; }
304  /// @brief Set the max number of bytes sent in each frame
305  ///
306  /// @param size The new max frame size
307  void setMaxFrameSize(uint32 size) { myMaxFrameSize = size; }
308 
309  /// @brief The current amount of bytes waiting to be sent over the network.
310  ///
311  /// @return The number of bytes waiting to be sent.
313  {
314  UT_AutoLock lock(myMessageLock);
315  return myMessageQueue.size();
316  }
317 
318  bool hasData();
319 
320  virtual void onEvent(const Event& ev) {}
321 
322 protected:
323  friend class NET_WebSocketMonitor;
324 
325  static bool setFrameType(FrameType& type, OpCode op_code)
326  {
327  switch (op_code)
328  {
329  case OpCode_Continue:
330  type = FrameType::Continue;
331  return true;
332  case OpCode_Utf8:
333  type = FrameType::Utf8;
334  return true;
335  case OpCode_Binary:
336  type = FrameType::Binary;
337  return true;
338  case OpCode_Terminate:
339  type = FrameType::Terminate;
340  return true;
341  case OpCode_Ping:
342  type = FrameType::Ping;
343  return true;
344  case OpCode_Pong:
345  type = FrameType::Pong;
346  return true;
347  default:
348  return false;
349  };
350  return false;
351  }
352 
353  /// @brief Create the entire message and place it on the queue to send.
354  ///
355  /// @param type The type of message.
356  /// @param message The message to include.
357  /// @param mask The mask for the message.
358  /// @param max_frame_size The max frame size for the message.
359  ///
360  /// @return True if creating the message and buffering did not fail.
361  bool internalSend(
362  FrameType type,
363  const UT_StringRef& message,
364  exint max_frame_size);
365 
366  /// @brief Flush the currently queue messages to send over the network.
367  void flush_();
368 
369  /// @brief Read a single frame from the server. If you need to read the
370  /// entire message then loop until this function returns false or
371  /// is_finished is true. This function will call the callbacks when a full
372  /// message is received.
373  ///
374  /// @return True if the read frame was a success.
375  Event read_(
376  const std::chrono::milliseconds& timeout
377  = std::chrono::milliseconds(500));
378 
380 
383 
385 
387 
389 
391 
394 
396 
398 };
399 
400 class NET_API NET_WebSocketAPI : public NET_WebSocket
401 {
402 public:
403 
404  using id_t = hboost::uuids::uuid;
405 
406  void onEvent(const Event& ev) override;
407 
409  {
411  NET_Future<NET_WebAPIResponse> f = p.get_future();
412  UT_Lock::Scope scope(myLock);
413  myPromises.emplace(id, std::move(p));
414  return f;
415  }
416  static hboost::uuids::uuid generateId()
417  {
418  return hboost::uuids::random_generator()();
419  }
420 
421 private:
422  UT_Lock myLock;
424 };
425 
427 
428 #endif // __NET_WEBSOCKET_H__
429 
FrameType frameType() const
NET_Future< NET_WebAPIResponse > addPromise(id_t id)
SYS_AtomicInt64 myBufferedAmount
GLsizeiptr size
Definition: glew.h:1681
GLuint GLuint stream
Definition: glew.h:7265
std::promise< T > NET_Promise
Definition: NET_WebSocket.h:42
FrameType
The currently supported different frame types.
Definition: NET_WebSocket.h:64
Unsorted map container.
Definition: UT_Map.h:83
void sendPong(const UT_StringRef &pong=UT_StringHolder::theEmptyString)
const Args & args
Definition: printf.h:628
bool send(const UT_StringHolder &data)
Send a message to the other end of the websocket. This function will send the data immediately and do...
bool isConnected() const
Tell if this websocket is currently in use.
const UT_Url & url() const
The url this websocket is using.
bool myIsWaitingForPong
int64 exint
Definition: SYS_Types.h:125
UT_StringHolder myMessage
static uint64 theMax16FrameSize
GLenum GLint GLuint mask
Definition: glew.h:1845
GLboolean reset
Definition: glew.h:4959
unsigned long long uint64
Definition: SYS_Types.h:117
UT_StringHolder data() const
exint bufferedAmount() const
The current amount of bytes waiting to be sent over the network.
ErrorCode
All possible error codes that could occur to be sent to the onError callback.
void read(T &in, bool &v)
Definition: ImfXdr.h:611
static bool setFrameType(FrameType &type, OpCode op_code)
NET_CircularBuffer< char > myMessageQueue
hboost::uuids::uuid id_t
unsigned char uint8
Definition: SYS_Types.h:36
NET_Time myLastHeartbeat
std::enable_shared_from_this< T > UTenable_shared_from_this
Definition: UT_SharedPtr.h:31
A utility class to do read-only operations on a subset of an existing string.
Definition: UT_StringView.h:40
GLclampf f
Definition: glew.h:3499
virtual void onEvent(const Event &ev)
std::shared_ptr< T > UT_SharedPtr
Wrapper around std::shared_ptr.
Definition: UT_SharedPtr.h:28
GLint GLenum GLsizei GLint GLsizei const void * data
Definition: glew.h:1379
static const UT_StringHolder theEmptyString
Class used for web socket operations.
Definition: NET_WebSocket.h:47
GLsizei GLenum GLuint GLuint GLsizei GLchar * message
Definition: glew.h:2581
Definition: UT_Url.h:22
static constexpr UT_StringLit frameTypeToString(FrameType type)
Convert the frame type to string representation of this frame type.
void setMaxFrameSize(uint32 size)
Set the max number of bytes sent in each frame.
static hboost::uuids::uuid generateId()
Buffer myReadBuffer
UT_UniquePtr< NET_WebStream > NET_WebStreamPtr
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1253
uint32 myMaxFrameSize
void sendPing(const UT_StringRef &ping=UT_StringHolder::theEmptyString)
Send a ping to the server. Regular use does not need to worry about this. Other abstractions will do ...
GLfloat GLfloat p
Definition: glew.h:16321
Error
Definition: oidn.hpp:306
std::future< T > NET_Future
Definition: NET_WebSocket.h:44
uint32 maxFrameSize() const
The current max frame size used.
unsigned int uint32
Definition: SYS_Types.h:40
NET_WebStreamPtr myStream
static UT_SharedPtr< SocketT > connect(const UT_Url &url, const std::chrono::milliseconds &timeout, Args &&...args)
#define UT_ASSERT(ZZ)
Definition: UT_Assert.h:135
FrameType
Different types of local frames that can be computed for components.
Definition: GU_LocalFrame.h:35
GLbitfield GLuint64 timeout
Definition: glew.h:6605
int getFD() const
The file descriptor of this websocket.
UT_Lock myMessageLock
void write(T &out, bool v)
Definition: ImfXdr.h:332
UT_SharedPtr< NET_WebSocket > NET_WebSocketSPtr
UT_FastRandom myRand
bool usableStream() const