You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

276 lines
9.3 KiB

4 months ago
  1. /*
  2. * IXWebSocketTransport.h
  3. * Author: Benjamin Sergeant
  4. * Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
  5. */
  6. #pragma once
  7. //
  8. // Adapted from https://github.com/dhbaird/easywsclient
  9. //
  10. #include "IXCancellationRequest.h"
  11. #include "IXProgressCallback.h"
  12. #include "IXSocketTLSOptions.h"
  13. #include "IXWebSocketCloseConstants.h"
  14. #include "IXWebSocketHandshake.h"
  15. #include "IXWebSocketHttpHeaders.h"
  16. #include "IXWebSocketPerMessageDeflate.h"
  17. #include "IXWebSocketPerMessageDeflateOptions.h"
  18. #include "IXWebSocketSendInfo.h"
  19. #include "IXWebSocketSendData.h"
  20. #include <atomic>
  21. #include <functional>
  22. #include <list>
  23. #include <memory>
  24. #include <mutex>
  25. #include <string>
  26. #include <vector>
  27. namespace ix
  28. {
  29. class Socket;
  30. enum class SendMessageKind
  31. {
  32. Text,
  33. Binary,
  34. Ping
  35. };
  36. class WebSocketTransport
  37. {
  38. public:
  39. enum class ReadyState
  40. {
  41. CLOSING,
  42. CLOSED,
  43. CONNECTING,
  44. OPEN
  45. };
  46. enum class MessageKind
  47. {
  48. MSG_TEXT,
  49. MSG_BINARY,
  50. PING,
  51. PONG,
  52. FRAGMENT
  53. };
  54. enum class PollResult
  55. {
  56. Succeeded,
  57. AbnormalClose,
  58. CannotFlushSendBuffer
  59. };
  60. using OnMessageCallback =
  61. std::function<void(const std::string&, size_t, bool, MessageKind)>;
  62. using OnCloseCallback = std::function<void(uint16_t, const std::string&, size_t, bool)>;
  63. WebSocketTransport();
  64. ~WebSocketTransport();
  65. void configure(const WebSocketPerMessageDeflateOptions& perMessageDeflateOptions,
  66. const SocketTLSOptions& socketTLSOptions,
  67. bool enablePong,
  68. int pingIntervalSecs);
  69. // Client
  70. WebSocketInitResult connectToUrl(const std::string& url,
  71. const WebSocketHttpHeaders& headers,
  72. int timeoutSecs);
  73. // Server
  74. WebSocketInitResult connectToSocket(std::unique_ptr<Socket> socket,
  75. int timeoutSecs,
  76. bool enablePerMessageDeflate);
  77. PollResult poll();
  78. WebSocketSendInfo sendBinary(const IXWebSocketSendData& message,
  79. const OnProgressCallback& onProgressCallback);
  80. WebSocketSendInfo sendText(const IXWebSocketSendData& message,
  81. const OnProgressCallback& onProgressCallback);
  82. WebSocketSendInfo sendPing(const IXWebSocketSendData& message);
  83. void close(uint16_t code = WebSocketCloseConstants::kNormalClosureCode,
  84. const std::string& reason = WebSocketCloseConstants::kNormalClosureMessage,
  85. size_t closeWireSize = 0,
  86. bool remote = false);
  87. void closeSocket();
  88. ReadyState getReadyState() const;
  89. void setReadyState(ReadyState readyState);
  90. void setOnCloseCallback(const OnCloseCallback& onCloseCallback);
  91. void dispatch(PollResult pollResult, const OnMessageCallback& onMessageCallback);
  92. size_t bufferedAmount() const;
  93. // internal
  94. WebSocketSendInfo sendHeartBeat();
  95. private:
  96. std::string _url;
  97. struct wsheader_type
  98. {
  99. unsigned header_size;
  100. bool fin;
  101. bool rsv1;
  102. bool rsv2;
  103. bool rsv3;
  104. bool mask;
  105. enum opcode_type
  106. {
  107. CONTINUATION = 0x0,
  108. TEXT_FRAME = 0x1,
  109. BINARY_FRAME = 0x2,
  110. CLOSE = 8,
  111. PING = 9,
  112. PONG = 0xa,
  113. } opcode;
  114. int N0;
  115. uint64_t N;
  116. uint8_t masking_key[4];
  117. };
  118. // Tells whether we should mask the data we send.
  119. // client should mask but server should not
  120. std::atomic<bool> _useMask;
  121. // Tells whether we should flush the send buffer before
  122. // saying that a send is complete. This is the mode for server code.
  123. std::atomic<bool> _blockingSend;
  124. // Buffer for reading from our socket. That buffer is never resized.
  125. std::vector<uint8_t> _readbuf;
  126. // Contains all messages that were fetched in the last socket read.
  127. // This could be a mix of control messages (Close, Ping, etc...) and
  128. // data messages. That buffer is resized
  129. std::vector<uint8_t> _rxbuf;
  130. // Contains all messages that are waiting to be sent
  131. std::vector<uint8_t> _txbuf;
  132. mutable std::mutex _txbufMutex;
  133. // Hold fragments for multi-fragments messages in a list. We support receiving very large
  134. // messages (tested messages up to 700M) and we cannot put them in a single
  135. // buffer that is resized, as this operation can be slow when a buffer has its
  136. // size increased 2 fold, while appending to a list has a fixed cost.
  137. std::list<std::string> _chunks;
  138. // Record the message kind (will be TEXT or BINARY) for a fragmented
  139. // message, present in the first chunk, since the final chunk will be a
  140. // CONTINUATION opcode and doesn't tell the full message kind
  141. MessageKind _fragmentedMessageKind;
  142. // Ditto for whether a message is compressed
  143. bool _receivedMessageCompressed;
  144. // Fragments are 32K long
  145. static constexpr size_t kChunkSize = 1 << 15;
  146. // Underlying TCP socket
  147. std::unique_ptr<Socket> _socket;
  148. std::mutex _socketMutex;
  149. // Hold the state of the connection (OPEN, CLOSED, etc...)
  150. std::atomic<ReadyState> _readyState;
  151. OnCloseCallback _onCloseCallback;
  152. std::string _closeReason;
  153. mutable std::mutex _closeReasonMutex;
  154. std::atomic<uint16_t> _closeCode;
  155. std::atomic<size_t> _closeWireSize;
  156. std::atomic<bool> _closeRemote;
  157. // Data used for Per Message Deflate compression (with zlib)
  158. WebSocketPerMessageDeflatePtr _perMessageDeflate;
  159. WebSocketPerMessageDeflateOptions _perMessageDeflateOptions;
  160. std::atomic<bool> _enablePerMessageDeflate;
  161. std::string _decompressedMessage;
  162. std::string _compressedMessage;
  163. // Used to control TLS connection behavior
  164. SocketTLSOptions _socketTLSOptions;
  165. // Used to cancel dns lookup + socket connect + http upgrade
  166. std::atomic<bool> _requestInitCancellation;
  167. mutable std::mutex _closingTimePointMutex;
  168. std::chrono::time_point<std::chrono::steady_clock> _closingTimePoint;
  169. static const int kClosingMaximumWaitingDelayInMs;
  170. // enable auto response to ping
  171. std::atomic<bool> _enablePong;
  172. static const bool kDefaultEnablePong;
  173. // Optional ping and pong timeout
  174. int _pingIntervalSecs;
  175. std::atomic<bool> _pongReceived;
  176. static const int kDefaultPingIntervalSecs;
  177. static const std::string kPingMessage;
  178. std::atomic<uint64_t> _pingCount;
  179. // We record when ping are being sent so that we can know when to send the next one
  180. mutable std::mutex _lastSendPingTimePointMutex;
  181. std::chrono::time_point<std::chrono::steady_clock> _lastSendPingTimePoint;
  182. // If this function returns true, it is time to send a new ping
  183. bool pingIntervalExceeded();
  184. void initTimePointsAfterConnect();
  185. // after calling close(), if no CLOSE frame answer is received back from the remote, we
  186. // should close the connexion
  187. bool closingDelayExceeded();
  188. void sendCloseFrame(uint16_t code, const std::string& reason);
  189. void closeSocketAndSwitchToClosedState(uint16_t code,
  190. const std::string& reason,
  191. size_t closeWireSize,
  192. bool remote);
  193. bool wakeUpFromPoll(uint64_t wakeUpCode);
  194. bool flushSendBuffer();
  195. bool sendOnSocket();
  196. bool receiveFromSocket();
  197. WebSocketSendInfo sendData(wsheader_type::opcode_type type,
  198. const IXWebSocketSendData& message,
  199. bool compress,
  200. const OnProgressCallback& onProgressCallback = nullptr);
  201. template<class Iterator>
  202. bool sendFragment(
  203. wsheader_type::opcode_type type, bool fin, Iterator begin, Iterator end, bool compress);
  204. void emitMessage(MessageKind messageKind,
  205. const std::string& message,
  206. bool compressedMessage,
  207. const OnMessageCallback& onMessageCallback);
  208. bool isSendBufferEmpty() const;
  209. template<class Iterator>
  210. void appendToSendBuffer(const std::vector<uint8_t>& header,
  211. Iterator begin,
  212. Iterator end,
  213. uint64_t message_size,
  214. uint8_t masking_key[4]);
  215. unsigned getRandomUnsigned();
  216. void unmaskReceiveBuffer(const wsheader_type& ws);
  217. std::string getMergedChunks() const;
  218. void setCloseReason(const std::string& reason);
  219. const std::string& getCloseReason() const;
  220. };
  221. } // namespace ix