WPILibC++  2020.3.2
WebSocket.h
1 /*----------------------------------------------------------------------------*/
2 /* Copyright (c) 2018-2019 FIRST. All Rights Reserved. */
3 /* Open Source Software - may be modified and shared by FRC teams. The code */
4 /* must be accompanied by the FIRST BSD license file in the root directory of */
5 /* the project. */
6 /*----------------------------------------------------------------------------*/
7 
8 #ifndef WPIUTIL_WPI_WEBSOCKET_H_
9 #define WPIUTIL_WPI_WEBSOCKET_H_
10 
11 #include <stdint.h>
12 
13 #include <functional>
14 #include <initializer_list>
15 #include <memory>
16 #include <string>
17 #include <utility>
18 
19 #include "wpi/ArrayRef.h"
20 #include "wpi/Signal.h"
21 #include "wpi/SmallVector.h"
22 #include "wpi/StringRef.h"
23 #include "wpi/Twine.h"
24 #include "wpi/uv/Buffer.h"
25 #include "wpi/uv/Error.h"
26 #include "wpi/uv/Timer.h"
27 
28 namespace wpi {
29 
30 namespace uv {
31 class Stream;
32 } // namespace uv
33 
37 class WebSocket : public std::enable_shared_from_this<WebSocket> {
38  struct private_init {};
39 
40  static constexpr uint8_t kOpCont = 0x00;
41  static constexpr uint8_t kOpText = 0x01;
42  static constexpr uint8_t kOpBinary = 0x02;
43  static constexpr uint8_t kOpClose = 0x08;
44  static constexpr uint8_t kOpPing = 0x09;
45  static constexpr uint8_t kOpPong = 0x0A;
46  static constexpr uint8_t kOpMask = 0x0F;
47  static constexpr uint8_t kFlagFin = 0x80;
48  static constexpr uint8_t kFlagMasking = 0x80;
49  static constexpr uint8_t kLenMask = 0x7f;
50 
51  public:
52  WebSocket(uv::Stream& stream, bool server, const private_init&);
53  WebSocket(const WebSocket&) = delete;
54  WebSocket(WebSocket&&) = delete;
55  WebSocket& operator=(const WebSocket&) = delete;
56  WebSocket& operator=(WebSocket&&) = delete;
57  ~WebSocket();
58 
62  enum State {
73  };
74 
78  struct ClientOptions {
79  ClientOptions() : handshakeTimeout{(uv::Timer::Time::max)()} {}
80 
82  uv::Timer::Time handshakeTimeout;
83 
86  };
87 
98  static std::shared_ptr<WebSocket> CreateClient(
99  uv::Stream& stream, const Twine& uri, const Twine& host,
101  const ClientOptions& options = ClientOptions{});
102 
113  static std::shared_ptr<WebSocket> CreateClient(
114  uv::Stream& stream, const Twine& uri, const Twine& host,
115  std::initializer_list<StringRef> protocols,
116  const ClientOptions& options = ClientOptions{}) {
117  return CreateClient(stream, uri, host,
118  makeArrayRef(protocols.begin(), protocols.end()),
119  options);
120  }
121 
135  static std::shared_ptr<WebSocket> CreateServer(
136  uv::Stream& stream, StringRef key, StringRef version,
137  StringRef protocol = StringRef{});
138 
142  State GetState() const { return m_state; }
143 
148  bool IsOpen() const { return m_state == OPEN; }
149 
153  uv::Stream& GetStream() const { return m_stream; }
154 
158  StringRef GetProtocol() const { return m_protocol; }
159 
166  void SetMaxMessageSize(size_t size) { m_maxMessageSize = size; }
167 
174  void SetCombineFragments(bool combine) { m_combineFragments = combine; }
175 
182  void Close(uint16_t code = 1005, const Twine& reason = Twine{});
183 
189  void SendText(
191  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
192  Send(kFlagFin | kOpText, data, callback);
193  }
194 
202  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
203  Send(kFlagFin | kOpBinary, data, callback);
204  }
205 
215  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
216  Send(kOpText, data, callback);
217  }
218 
228  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
229  Send(kOpBinary, data, callback);
230  }
231 
240  ArrayRef<uv::Buffer> data, bool fin,
241  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
242  Send(kOpCont | (fin ? kFlagFin : 0), data, callback);
243  }
244 
250  void SendPing(std::function<void(uv::Error)> callback = nullptr) {
251  SendPing(ArrayRef<uv::Buffer>{}, [callback](auto bufs, uv::Error err) {
252  if (callback) callback(err);
253  });
254  }
255 
262  void SendPing(
264  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
265  Send(kFlagFin | kOpPing, data, callback);
266  }
267 
273  void SendPong(std::function<void(uv::Error)> callback = nullptr) {
274  SendPong(ArrayRef<uv::Buffer>{}, [callback](auto bufs, uv::Error err) {
275  if (callback) callback(err);
276  });
277  }
278 
285  void SendPong(
287  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback) {
288  Send(kFlagFin | kOpPong, data, callback);
289  }
290 
294  void Fail(uint16_t code = 1002, const Twine& reason = "protocol error");
295 
299  void Terminate(uint16_t code = 1006, const Twine& reason = "terminated");
300 
305  template <typename T = void>
306  std::shared_ptr<T> GetData() const {
307  return std::static_pointer_cast<T>(m_data);
308  }
309 
314  void SetData(std::shared_ptr<void> data) { m_data = std::move(data); }
315 
321 
329 
336 
343 
348 
353 
354  private:
355  // user data
356  std::shared_ptr<void> m_data;
357 
358  // constructor parameters
359  uv::Stream& m_stream;
360  bool m_server;
361 
362  // subprotocol, set via constructor (server) or handshake (client)
363  std::string m_protocol;
364 
365  // user-settable configuration
366  size_t m_maxMessageSize = 128 * 1024;
367  bool m_combineFragments = true;
368 
369  // operating state
370  State m_state = CONNECTING;
371 
372  // incoming message buffers/state
373  SmallVector<uint8_t, 14> m_header;
374  size_t m_headerSize = 0;
375  SmallVector<uint8_t, 1024> m_payload;
376  size_t m_frameStart = 0;
377  uint64_t m_frameSize = UINT64_MAX;
378  uint8_t m_fragmentOpcode = 0;
379 
380  // temporary data used only during client handshake
381  class ClientHandshakeData;
382  std::unique_ptr<ClientHandshakeData> m_clientHandshake;
383 
384  void StartClient(const Twine& uri, const Twine& host,
385  ArrayRef<StringRef> protocols, const ClientOptions& options);
386  void StartServer(StringRef key, StringRef version, StringRef protocol);
387  void SendClose(uint16_t code, const Twine& reason);
388  void SetClosed(uint16_t code, const Twine& reason, bool failed = false);
389  void Shutdown();
390  void HandleIncoming(uv::Buffer& buf, size_t size);
391  void Send(
392  uint8_t opcode, ArrayRef<uv::Buffer> data,
393  std::function<void(MutableArrayRef<uv::Buffer>, uv::Error)> callback);
394 };
395 
396 } // namespace wpi
397 
398 #endif // WPIUTIL_WPI_WEBSOCKET_H_
wpi::WebSocket::SendPing
void SendPing(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a ping frame.
Definition: WebSocket.h:262
wpi::uv::Stream
Stream handle.
Definition: Stream.h:69
wpi::WebSocket::OPEN
The connection is open and ready to communicate.
Definition: WebSocket.h:66
wpi::WebSocket::SendBinaryFragment
void SendBinaryFragment(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a text message fragment.
Definition: WebSocket.h:226
wpi::WebSocket::CLOSED
The connection is closed.
Definition: WebSocket.h:72
wpi::WebSocket::CONNECTING
The connection is not yet open.
Definition: WebSocket.h:64
wpi::WebSocket::SetData
void SetData(std::shared_ptr< void > data)
Sets user-defined data.
Definition: WebSocket.h:314
wpi::WebSocket::CreateServer
static std::shared_ptr< WebSocket > CreateServer(uv::Stream &stream, StringRef key, StringRef version, StringRef protocol=StringRef{})
Starts a server connection by performing the initial server side handshake.
wpi::WebSocket::GetData
std::shared_ptr< T > GetData() const
Gets user-defined data.
Definition: WebSocket.h:306
wpi::ArrayRef
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:42
wpi::WebSocket::SendBinary
void SendBinary(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a binary message.
Definition: WebSocket.h:200
wpi::WebSocket::SendPong
void SendPong(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a pong frame.
Definition: WebSocket.h:285
wpi::WebSocket::GetState
State GetState() const
Get connection state.
Definition: WebSocket.h:142
wpi::WebSocket::pong
sig::Signal< ArrayRef< uint8_t > > pong
Pong event.
Definition: WebSocket.h:352
wpi::WebSocket::GetProtocol
StringRef GetProtocol() const
Get the selected sub-protocol.
Definition: WebSocket.h:158
wpi::uv::Buffer
Data buffer.
Definition: Buffer.h:27
wpi::makeArrayRef
ArrayRef< T > makeArrayRef(const T &OneElt)
Construct an ArrayRef from a single element.
Definition: ArrayRef.h:447
nt::StartServer
void StartServer(StringRef persist_filename, const char *listen_address, unsigned int port)
Starts a server using the specified filename, listening address, and port.
wpi::WebSocket
RFC 6455 compliant WebSocket client and server implementation.
Definition: WebSocket.h:37
wpi::WebSocket::FAILED
The connection failed.
Definition: WebSocket.h:70
wpi::WebSocket::SendPing
void SendPing(std::function< void(uv::Error)> callback=nullptr)
Send a ping frame with no data.
Definition: WebSocket.h:250
wpi
WPILib C++ utilities (wpiutil) namespace.
Definition: EventLoopRunner.h:17
wpi::WebSocket::open
sig::Signal< StringRef > open
Open event.
Definition: WebSocket.h:320
wpi::WebSocket::CreateClient
static std::shared_ptr< WebSocket > CreateClient(uv::Stream &stream, const Twine &uri, const Twine &host, std::initializer_list< StringRef > protocols, const ClientOptions &options=ClientOptions{})
Starts a client connection by performing the initial client handshake.
Definition: WebSocket.h:113
wpi::WebSocket::ClientOptions
Client connection options.
Definition: WebSocket.h:78
wpi::WebSocket::ClientOptions::handshakeTimeout
uv::Timer::Time handshakeTimeout
Timeout for the handshake request.
Definition: WebSocket.h:82
wpi::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:49
wpi::WebSocket::SendTextFragment
void SendTextFragment(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a text message fragment.
Definition: WebSocket.h:213
wpi::WebSocket::CreateClient
static std::shared_ptr< WebSocket > CreateClient(uv::Stream &stream, const Twine &uri, const Twine &host, ArrayRef< StringRef > protocols=ArrayRef< StringRef >{}, const ClientOptions &options=ClientOptions{})
Starts a client connection by performing the initial client handshake.
nt::StartClient
void StartClient()
Starts a client.
wpi::MutableArrayRef
MutableArrayRef - Represent a mutable reference to an array (0 or more elements consecutively in memo...
Definition: ArrayRef.h:287
wpi::WebSocket::SetMaxMessageSize
void SetMaxMessageSize(size_t size)
Set the maximum message size.
Definition: WebSocket.h:166
wpi::WebSocket::CLOSING
The connection is in the process of closing.
Definition: WebSocket.h:68
wpi::WebSocket::Fail
void Fail(uint16_t code=1002, const Twine &reason="protocol error")
Fail the connection.
wpi::WebSocket::Close
void Close(uint16_t code=1005, const Twine &reason=Twine{})
Initiate a closing handshake.
wpi::WebSocket::State
State
Connection states.
Definition: WebSocket.h:62
wpi::size
auto size(R &&Range, typename std::enable_if< std::is_same< typename std::iterator_traits< decltype(Range.begin())>::iterator_category, std::random_access_iterator_tag >::value, void >::type *=nullptr) -> decltype(std::distance(Range.begin(), Range.end()))
Get the size of a range.
Definition: STLExtras.h:1007
wpi::WebSocket::SendText
void SendText(ArrayRef< uv::Buffer > data, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a text message.
Definition: WebSocket.h:189
wpi::WebSocket::binary
sig::Signal< ArrayRef< uint8_t >, bool > binary
Binary message event.
Definition: WebSocket.h:342
wpi::WebSocket::Terminate
void Terminate(uint16_t code=1006, const Twine &reason="terminated")
Forcibly close the connection.
wpi::uv::Error
Error code.
Definition: Error.h:19
wpi::WebSocket::text
sig::Signal< StringRef, bool > text
Text message event.
Definition: WebSocket.h:335
wpi::WebSocket::SetCombineFragments
void SetCombineFragments(bool combine)
Set whether or not fragmented frames should be combined.
Definition: WebSocket.h:174
wpi::WebSocket::closed
sig::Signal< uint16_t, StringRef > closed
Close event.
Definition: WebSocket.h:328
wpi::WebSocket::SendPong
void SendPong(std::function< void(uv::Error)> callback=nullptr)
Send a pong frame with no data.
Definition: WebSocket.h:273
wpi::WebSocket::IsOpen
bool IsOpen() const
Return if the connection is open.
Definition: WebSocket.h:148
wpi::SmallVector< uint8_t, 14 >
wpi::WebSocket::ping
sig::Signal< ArrayRef< uint8_t > > ping
Ping event.
Definition: WebSocket.h:347
wpi::Twine
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:85
wpi::sig::SignalBase
SignalBase is an implementation of the observer pattern, through the use of an emitting object and sl...
Definition: Signal.h:495
wpi::WebSocket::SendFragment
void SendFragment(ArrayRef< uv::Buffer > data, bool fin, std::function< void(MutableArrayRef< uv::Buffer >, uv::Error)> callback)
Send a continuation frame.
Definition: WebSocket.h:239
wpi::WebSocket::GetStream
uv::Stream & GetStream() const
Get the underlying stream.
Definition: WebSocket.h:153
wpi::WebSocket::ClientOptions::extraHeaders
ArrayRef< std::pair< StringRef, StringRef > > extraHeaders
Additional headers to include in handshake.
Definition: WebSocket.h:85