C++ Rest SDK
The C++ REST SDK is a Microsoft project for cloud-based client-server communication in native code using a modern asynchronous C++ API design. This project aims to help C++ developers connect to and interact with services.
ws_client.h
1 /***
2 * ==++==
3 *
4 * Copyright (c) Microsoft Corporation. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * ==--==
17 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
18 *
19 * Websocket client side implementation
20 *
21 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
22 ****/
23 #pragma once
24 
25 #ifndef _CASA_WS_CLIENT_H
26 #define _CASA_WS_CLIENT_H
27 
28 #include "cpprest/details/basic_types.h"
29 
30 #if !defined(CPPREST_EXCLUDE_WEBSOCKETS)
31 
32 #include <memory>
33 #include <limits>
34 #include <condition_variable>
35 #include <mutex>
36 
37 #include "pplx/pplxtasks.h"
38 #include "cpprest/uri.h"
39 #include "cpprest/details/web_utilities.h"
40 #include "cpprest/http_headers.h"
41 #include "cpprest/asyncrt_utils.h"
42 #include "cpprest/ws_msg.h"
43 
44 namespace web
45 {
46 
47 // For backwards compatibility for when in the experimental namespace.
48 // At next major release this should be deleted.
49 namespace experimental = web;
50 
51 // In the past namespace was accidentally called 'web_sockets'. To avoid breaking code
52 // alias it. At our next major release this should be deleted.
53 namespace web_sockets = websockets;
54 
55 namespace websockets
56 {
58 namespace client
59 {
60 
63 {
64  normal = 1000,
65  going_away = 1001,
66  protocol_error = 1002,
67  unsupported = 1003, //or data_mismatch
68  abnormal_close = 1006,
69  inconsistent_datatype = 1007,
70  policy_violation = 1008,
71  too_large = 1009,
72  negotiate_error = 1010,
73  server_terminate = 1011,
74 };
75 
81 {
82 public:
83 
87  websocket_client_config() : m_sni_enabled(true) {}
88 
93  const web_proxy& proxy() const
94  {
95  return m_proxy;
96  }
97 
102  void set_proxy(const web_proxy &proxy)
103  {
104  m_proxy = proxy;
105  }
106 
112  {
113  return m_credentials;
114  }
115 
121  {
122  m_credentials = cred;
123  }
124 
128  void disable_sni()
129  {
130  m_sni_enabled = false;
131  }
132 
137  bool is_sni_enabled() const
138  {
139  return m_sni_enabled;
140  }
141 
147  void set_server_name(const utf8string &name)
148  {
149  m_sni_hostname = name;
150  }
151 
156  const utf8string & server_name() const
157  {
158  return m_sni_hostname;
159  }
160 
168  web::http::http_headers &headers() { return m_headers; }
169 
174  const web::http::http_headers &headers() const { return m_headers; }
175 
181  _ASYNCRTIMP void add_subprotocol(const ::utility::string_t &name);
182 
189  _ASYNCRTIMP std::vector<::utility::string_t> subprotocols() const;
190 
191 private:
192  web::web_proxy m_proxy;
193  web::credentials m_credentials;
194  web::http::http_headers m_headers;
195  bool m_sni_enabled;
196  utf8string m_sni_hostname;
197 };
198 
202 class websocket_exception : public std::exception
203 {
204 public:
205 
210  websocket_exception(const utility::string_t &whatArg)
211  : m_msg(utility::conversions::to_utf8string(whatArg)) {}
212 
213 #ifdef _WIN32
214  websocket_exception(std::string whatArg) : m_msg(std::move(whatArg)) {}
219 #endif
220 
226  websocket_exception(int errorCode)
227  : m_errorCode(utility::details::create_error_code(errorCode))
228  {
229  m_msg = m_errorCode.message();
230  }
231 
237  websocket_exception(int errorCode, const utility::string_t &whatArg)
238  : m_errorCode(utility::details::create_error_code(errorCode)),
239  m_msg(utility::conversions::to_utf8string(whatArg))
240  {}
241 
242 #ifdef _WIN32
243  websocket_exception(int errorCode, std::string whatArg)
249  : m_errorCode(utility::details::create_error_code(errorCode)),
250  m_msg(std::move(whatArg))
251  {}
252 
258  websocket_exception(std::error_code code, std::string whatArg) :
259  m_errorCode(std::move(code)),
260  m_msg(std::move(whatArg))
261  {}
262 #endif
263 
270  websocket_exception(int errorCode, const std::error_category &cat) : m_errorCode(std::error_code(errorCode, cat))
271  {
272  m_msg = m_errorCode.message();
273  }
274 
280  websocket_exception(std::error_code code, const utility::string_t &whatArg) :
281  m_errorCode(std::move(code)),
282  m_msg(utility::conversions::to_utf8string(whatArg))
283  {}
284 
289  const char* what() const CPPREST_NOEXCEPT
290  {
291  return m_msg.c_str();
292  }
293 
298  const std::error_code & error_code() const CPPREST_NOEXCEPT
299  {
300  return m_errorCode;
301  }
302 
303 private:
304  std::error_code m_errorCode;
305  std::string m_msg;
306 };
307 
308 namespace details
309 {
310 
311 // Interface to be implemented by the websocket client callback implementations.
313 {
314 public:
315 
317  m_config(std::move(config)) {}
318 
319  virtual ~websocket_client_callback_impl() CPPREST_NOEXCEPT{}
320 
321  virtual pplx::task<void> connect() = 0;
322 
323  virtual pplx::task<void> send(websocket_outgoing_message &msg) = 0;
324 
325  virtual void set_message_handler(const std::function<void(const websocket_incoming_message&)>& handler) = 0;
326 
327  virtual pplx::task<void> close() = 0;
328 
329  virtual pplx::task<void> close(websocket_close_status close_status, const utility::string_t &close_reason = _XPLATSTR("")) = 0;
330 
331  virtual void set_close_handler(const std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)>& handler) = 0;
332 
333  const web::uri& uri() const
334  {
335  return m_uri;
336  }
337 
338  void set_uri(const web::uri &uri)
339  {
340  m_uri = uri;
341  }
342 
343  const websocket_client_config& config() const
344  {
345  return m_config;
346  }
347 
348  static void verify_uri(const web::uri& uri)
349  {
350  // Most of the URI schema validation is taken care by URI class.
351  // We only need to check certain things specific to websockets.
352  if (uri.scheme() != _XPLATSTR("ws") && uri.scheme() != _XPLATSTR("wss"))
353  {
354  throw std::invalid_argument("URI scheme must be 'ws' or 'wss'");
355  }
356 
357  if (uri.host().empty())
358  {
359  throw std::invalid_argument("URI must contain a hostname.");
360  }
361 
362  // Fragment identifiers are meaningless in the context of WebSocket URIs
363  // and MUST NOT be used on these URIs.
364  if (!uri.fragment().empty())
365  {
366  throw std::invalid_argument("WebSocket URI must not contain fragment identifiers");
367  }
368  }
369 
370 protected:
371  web::uri m_uri;
372  websocket_client_config m_config;
373 };
374 
375 // Interface to be implemented by the websocket client task implementations.
377 {
378 
379 public:
381 
382  _ASYNCRTIMP virtual ~websocket_client_task_impl() CPPREST_NOEXCEPT;
383 
384  _ASYNCRTIMP pplx::task<websocket_incoming_message> receive();
385 
386  _ASYNCRTIMP void close_pending_tasks_with_error(const websocket_exception &exc);
387 
388  const std::shared_ptr<websocket_client_callback_impl> & callback_client() const { return m_callback_client; };
389 
390 private:
391  void set_handler();
392 
393  // When a message arrives, if there are tasks waiting for a message, signal the topmost one.
394  // Else enqueue the message in a queue.
395  // m_receive_queue_lock : to guard access to the queue & m_client_closed
396  std::mutex m_receive_queue_lock;
397  // Queue to store incoming messages when there are no tasks waiting for a message
398  std::queue<websocket_incoming_message> m_receive_msg_queue;
399  // Queue to maintain the receive tasks when there are no messages(yet).
400  std::queue<pplx::task_completion_event<websocket_incoming_message>> m_receive_task_queue;
401 
402  // Initially set to false, becomes true if a close frame is received from the server or
403  // if the underlying connection is aborted or terminated.
404  bool m_client_closed;
405 
406  std::shared_ptr<websocket_client_callback_impl> m_callback_client;
407 };
408 }
409 
414 {
415 public:
420  m_client(std::make_shared<details::websocket_client_task_impl>(websocket_client_config()))
421  {}
422 
428  m_client(std::make_shared<details::websocket_client_task_impl>(std::move(config)))
429  {}
430 
438  {
439  m_client->callback_client()->verify_uri(uri);
440  m_client->callback_client()->set_uri(uri);
441  auto client = m_client;
442  return m_client->callback_client()->connect().then([client](pplx::task<void> result)
443  {
444  try
445  {
446  result.get();
447  }
448  catch (const websocket_exception& ex)
449  {
450  client->close_pending_tasks_with_error(ex);
451  throw;
452  }
453  });
454  }
455 
461  {
462  return m_client->callback_client()->send(msg);
463  }
464 
470  {
471  return m_client->receive();
472  }
473 
479  {
480  return m_client->callback_client()->close();
481  }
482 
489  pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason=_XPLATSTR(""))
490  {
491  return m_client->callback_client()->close(close_status, close_reason);
492  }
493 
498  const web::uri& uri() const
499  {
500  return m_client->callback_client()->uri();
501  }
502 
508  {
509  return m_client->callback_client()->config();
510  }
511 
512 private:
513  std::shared_ptr<details::websocket_client_task_impl> m_client;
514 };
515 
521 {
522 public:
526  _ASYNCRTIMP websocket_callback_client();
527 
532  _ASYNCRTIMP websocket_callback_client(websocket_client_config client_config);
533 
541  {
542  m_client->verify_uri(uri);
543  m_client->set_uri(uri);
544  return m_client->connect();
545  }
546 
552  {
553  return m_client->send(msg);
554  }
555 
563  void set_message_handler(const std::function<void(const websocket_incoming_message& msg)>& handler)
564  {
565  m_client->set_message_handler(handler);
566  }
567 
573  {
574  return m_client->close();
575  }
576 
583  pplx::task<void> close(websocket_close_status close_status, const utility::string_t& close_reason = _XPLATSTR(""))
584  {
585  return m_client->close(close_status, close_reason);
586  }
587 
596  void set_close_handler(const std::function<void(websocket_close_status close_status, const utility::string_t& reason, const std::error_code& error)>& handler)
597  {
598  m_client->set_close_handler(handler);
599  }
600 
605  const web::uri& uri() const
606  {
607  return m_client->uri();
608  }
609 
615  {
616  return m_client->config();
617  }
618 
619 private:
620  std::shared_ptr<details::websocket_client_callback_impl> m_client;
621 };
622 
623 }}}
624 
625 #endif
626 
627 #endif
pplx::task< websocket_incoming_message > receive()
Receive a websocket message.
Definition: ws_client.h:469
Represents a set of user credentials (user name and password) to be used for authentication.
Definition: web_utilities.h:80
Represents HTTP headers, acts like a map.
Definition: http_headers.h:75
pplx::task< void > connect(const web::uri &uri)
Connects to the remote network destination. The connect method initiates the websocket handshake with...
Definition: ws_client.h:540
_ASYNCRTIMP std::vector<::utility::string_t > subprotocols() const
Gets list of the specified subprotocols.
void set_credentials(const web::credentials &cred)
Set the client credentials
Definition: ws_client.h:120
const std::error_code & error_code() const CPPREST_NOEXCEPT
Gets the underlying error code for the cause of the exception.
Definition: ws_client.h:298
pplx::task< void > close(websocket_close_status close_status, const utility::string_t &close_reason=_XPLATSTR(""))
Closes a websocket client connection, sends a close frame to the server and waits for a close message...
Definition: ws_client.h:583
void disable_sni()
Disables Server Name Indication (SNI). Default is on.
Definition: ws_client.h:128
A flexible, protocol independent URI implementation.
Definition: base_uri.h:151
bool is_sni_enabled() const
Determines if Server Name Indication (SNI) is enabled.
Definition: ws_client.h:137
Represents an incoming websocket message
Definition: ws_msg.h:173
The web namespace contains functionality common to multiple protocols like HTTP and WebSockets...
Definition: base_uri.h:37
web::http::http_headers & headers()
Gets the headers of the HTTP request message used in the WebSocket protocol handshake.
Definition: ws_client.h:168
const utf8string & server_name() const
Gets the server host name to usefor TLS Server Name Indication (SNI).
Definition: ws_client.h:156
websocket_client()
Creates a new websocket_client.
Definition: ws_client.h:419
void get() const
Returns the result this task produced. If the task is not in a terminal state, a call to get will wai...
Definition: pplxtasks.h:4440
void set_proxy(const web_proxy &proxy)
Set the web proxy object
Definition: ws_client.h:102
const websocket_client_config & config() const
Gets the websocket client config object.
Definition: ws_client.h:507
pplx::task< void > connect(const web::uri &uri)
Connects to the remote network destination. The connect method initiates the websocket handshake with...
Definition: ws_client.h:437
websocket_close_status
Websocket close status values.
Definition: ws_client.h:62
void set_server_name(const utf8string &name)
Sets the server host name to use for TLS Server Name Indication (SNI).
Definition: ws_client.h:147
const web_proxy & proxy() const
Get the web proxy object
Definition: ws_client.h:93
pplx::task< void > send(websocket_outgoing_message msg)
Sends a websocket message to the server .
Definition: ws_client.h:460
web_proxy represents the concept of the web proxy, which can be auto-discovered, disabled, or specified explicitly by the user.
Definition: web_utilities.h:157
const web::http::http_headers & headers() const
Gets a const reference to the headers of the WebSocket protocol handshake HTTP message.
Definition: ws_client.h:174
websocket_client_config()
Creates a websocket client configuration with default settings.
Definition: ws_client.h:87
const char * what() const CPPREST_NOEXCEPT
Gets a string identifying the cause of the exception.
Definition: ws_client.h:289
websocket_exception(const utility::string_t &whatArg)
Creates an websocket_exception with just a string message and no error code.
Definition: ws_client.h:210
Websocket client configuration class, used to set the possible configuration options used to create a...
Definition: ws_client.h:80
The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed as...
Definition: pplxtasks.h:176
pplx::task< void > close()
Closes a websocket client connection, sends a close frame to the server and waits for a close message...
Definition: ws_client.h:572
const utility::string_t & host() const
Get the host component of the URI as an encoded string.
Definition: base_uri.h:298
Websocket client class, used to maintain a connection to a remote host for an extended session...
Definition: ws_client.h:413
void set_close_handler(const std::function< void(websocket_close_status close_status, const utility::string_t &reason, const std::error_code &error)> &handler)
Set the closed handler for notification of client websocket closing event.
Definition: ws_client.h:596
const web::credentials & credentials() const
Get the client credentials
Definition: ws_client.h:111
websocket_exception(int errorCode, const utility::string_t &whatArg)
Creates a websocket_exception from a error code using the current platform error category.
Definition: ws_client.h:237
const websocket_client_config & config() const
Gets the websocket client config object.
Definition: ws_client.h:614
Represents a websocket error. This class holds an error message and an optional error code...
Definition: ws_client.h:202
pplx::task< void > close()
Closes a websocket client connection, sends a close frame to the server and waits for a close message...
Definition: ws_client.h:478
websocket_client(websocket_client_config config)
Creates a new websocket_client.
Definition: ws_client.h:427
void set_message_handler(const std::function< void(const websocket_incoming_message &msg)> &handler)
Set the received handler for notification of client websocket messages.
Definition: ws_client.h:563
The Parallel Patterns Library (PPL) task class. A task object represents work that can be executed as...
Definition: pplxtasks.h:4173
Websocket client class, used to maintain a connection to a remote host for an extended session...
Definition: ws_client.h:520
const web::uri & uri() const
Gets the websocket client URI.
Definition: ws_client.h:498
websocket_exception(std::error_code code, const utility::string_t &whatArg)
Creates a websocket_exception from a error code and string message to use as the what() argument...
Definition: ws_client.h:280
Represents an outgoing websocket message
Definition: ws_msg.h:72
const utility::string_t & fragment() const
Get the fragment component of the URI as an encoded string.
Definition: base_uri.h:322
_ASYNCRTIMP void add_subprotocol(const ::utility::string_t &name)
Adds a subprotocol to the request headers.
websocket_exception(int errorCode, const std::error_category &cat)
Creates a websocket_exception from a error code and category. The message of the error code will be u...
Definition: ws_client.h:270
_ASYNCRTIMP websocket_callback_client()
Creates a new websocket_callback_client.
websocket_exception(int errorCode)
Creates a websocket_exception from a error code using the current platform error category. The message of the error code will be used as the what() string message.
Definition: ws_client.h:226
pplx::task< void > send(websocket_outgoing_message msg)
Sends a websocket message to the server .
Definition: ws_client.h:551
const web::uri & uri() const
Gets the websocket client URI.
Definition: ws_client.h:605
const utility::string_t & scheme() const
Get the scheme component of the URI as an encoded string.
Definition: base_uri.h:286
Various utilities for string conversions and date and time manipulation.
Definition: asyncrt_utils.h:50
pplx::task< void > close(websocket_close_status close_status, const utility::string_t &close_reason=_XPLATSTR(""))
Closes a websocket client connection, sends a close frame to the server and waits for a close message...
Definition: ws_client.h:489