WebSocket++  0.8.0-dev
C++ websocket client/server library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
processor.hpp
1 /*
2  * Copyright (c) 2015, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_PROCESSOR_HPP
29 #define WEBSOCKETPP_PROCESSOR_HPP
30 
31 #include <websocketpp/processors/base.hpp>
32 #include <websocketpp/common/system_error.hpp>
33 
34 #include <websocketpp/close.hpp>
35 #include <websocketpp/utilities.hpp>
36 #include <websocketpp/uri.hpp>
37 
38 #include <sstream>
39 #include <string>
40 #include <utility>
41 #include <vector>
42 
43 namespace websocketpp {
44 /// Processors encapsulate the protocol rules specific to each WebSocket version
45 /**
46  * The processors namespace includes a number of free functions that operate on
47  * various WebSocket related data structures and perform processing that is not
48  * related to specific versions of the protocol.
49  *
50  * It also includes the abstract interface for the protocol specific processing
51  * engines. These engines wrap all of the logic necessary for parsing and
52  * validating WebSocket handshakes and messages of specific protocol version
53  * and set of allowed extensions.
54  *
55  * An instance of a processor represents the state of a single WebSocket
56  * connection of the associated version. One processor instance is needed per
57  * logical WebSocket connection.
58  */
59 namespace processor {
60 
61 /// Determine whether or not a generic HTTP request is a WebSocket handshake
62 /**
63  * @param r The HTTP request to read.
64  *
65  * @return True if the request is a WebSocket handshake, false otherwise
66  */
67 template <typename request_type>
68 bool is_websocket_handshake(request_type& r) {
69  using utility::ci_find_substr;
70 
71  std::string const & upgrade_header = r.get_header("Upgrade");
72 
73  if (ci_find_substr(upgrade_header, constants::upgrade_token,
74  sizeof(constants::upgrade_token)-1) == upgrade_header.end())
75  {
76  return false;
77  }
78 
79  std::string const & con_header = r.get_header("Connection");
80 
81  if (ci_find_substr(con_header, constants::connection_token,
82  sizeof(constants::connection_token)-1) == con_header.end())
83  {
84  return false;
85  }
86 
87  return true;
88 }
89 
90 /// Extract the version from a WebSocket handshake request
91 /**
92  * A blank version header indicates a spec before versions were introduced.
93  * The only such versions in shipping products are Hixie Draft 75 and Hixie
94  * Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also
95  * known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As
96  * differentiating between these two sets of browsers is very difficult and
97  * Safari 5.0.1+ accounts for the vast majority of cases in the wild this
98  * function assumes that all handshakes without a valid version header are
99  * Hybi 00.
100  *
101  * @param r The WebSocket handshake request to read.
102  *
103  * @return The WebSocket handshake version or -1 if there was an extraction
104  * error.
105  */
106 template <typename request_type>
107 int get_websocket_version(request_type& r) {
108  if (!r.ready()) {
109  return -2;
110  }
111 
112  if (r.get_header("Sec-WebSocket-Version").empty()) {
113  return 0;
114  }
115 
116  int version;
117  std::istringstream ss(r.get_header("Sec-WebSocket-Version"));
118 
119  if ((ss >> version).fail()) {
120  return -1;
121  }
122 
123  return version;
124 }
125 
126 /// Extract a URI ptr from the host header of the request
127 /**
128  * @param request The request to extract the Host header from.
129  *
130  * @param scheme The scheme under which this request was received (ws, wss,
131  * http, https, etc)
132  *
133  * @return A uri_pointer that encodes the value of the host header.
134  */
135 template <typename request_type>
137  std::string h = request.get_header("Host");
138 
139  size_t last_colon = h.rfind(":");
140  size_t last_sbrace = h.rfind("]");
141 
142  // no : = hostname with no port
143  // last : before ] = ipv6 literal with no port
144  // : with no ] = hostname with port
145  // : after ] = ipv6 literal with port
146  if (last_colon == std::string::npos ||
147  (last_sbrace != std::string::npos && last_sbrace > last_colon))
148  {
149  return lib::make_shared<uri>(scheme, h, request.get_uri());
150  } else {
151  return lib::make_shared<uri>(scheme,
152  h.substr(0,last_colon),
153  h.substr(last_colon+1),
154  request.get_uri());
155  }
156 }
157 
158 /// WebSocket protocol processor abstract base class
159 template <typename config>
160 class processor {
161 public:
162  typedef processor<config> type;
163  typedef typename config::request_type request_type;
164  typedef typename config::response_type response_type;
165  typedef typename config::message_type::ptr message_ptr;
166  typedef std::pair<lib::error_code,std::string> err_str_pair;
167 
168  explicit processor(bool secure, bool p_is_server)
169  : m_secure(secure)
170  , m_server(p_is_server)
171  , m_max_message_size(config::max_message_size)
172  {}
173 
174  virtual ~processor() {}
175 
176  /// Get the protocol version of this processor
177  virtual int get_version() const = 0;
178 
179  /// Get maximum message size
180  /**
181  * Get maximum message size. Maximum message size determines the point at which the
182  * processor will fail a connection with the message_too_big protocol error.
183  *
184  * The default is retrieved from the max_message_size value from the template config
185  *
186  * @since 0.3.0
187  */
188  size_t get_max_message_size() const {
189  return m_max_message_size;
190  }
191 
192  /// Set maximum message size
193  /**
194  * Set maximum message size. Maximum message size determines the point at which the
195  * processor will fail a connection with the message_too_big protocol error.
196  *
197  * The default is retrieved from the max_message_size value from the template config
198  *
199  * @since 0.3.0
200  *
201  * @param new_value The value to set as the maximum message size.
202  */
203  void set_max_message_size(size_t new_value) {
204  m_max_message_size = new_value;
205  }
206 
207  /// Returns whether or not the permessage_compress extension is implemented
208  /**
209  * Compile time flag that indicates whether this processor has implemented
210  * the permessage_compress extension. By default this is false.
211  */
212  virtual bool has_permessage_compress() const {
213  return false;
214  }
215 
216  /// Initializes extensions based on the Sec-WebSocket-Extensions header
217  /**
218  * Reads the Sec-WebSocket-Extensions header and determines if any of the
219  * requested extensions are supported by this processor. If they are their
220  * settings data is initialized and an extension string to send to the
221  * is returned.
222  *
223  * @param request The request or response headers to look at.
224  */
225  virtual err_str_pair negotiate_extensions(request_type const &) {
226  return err_str_pair();
227  }
228 
229  /// Initializes extensions based on the Sec-WebSocket-Extensions header
230  /**
231  * Reads the Sec-WebSocket-Extensions header and determines if any of the
232  * requested extensions were accepted by the server. If they are their
233  * settings data is initialized. If they are not a list of required
234  * extensions (if any) is returned. This list may be sent back to the server
235  * as a part of the 1010/Extension required close code.
236  *
237  * @param response The request or response headers to look at.
238  */
239  virtual err_str_pair negotiate_extensions(response_type const &) {
240  return err_str_pair();
241  }
242 
243  /// validate a WebSocket handshake request for this version
244  /**
245  * @param request The WebSocket handshake request to validate.
246  * is_websocket_handshake(request) must be true and
247  * get_websocket_version(request) must equal this->get_version().
248  *
249  * @return A status code, 0 on success, non-zero for specific sorts of
250  * failure
251  */
252  virtual lib::error_code validate_handshake(request_type const & request) const = 0;
253 
254  /// Calculate the appropriate response for this websocket request
255  /**
256  * @param req The request to process
257  *
258  * @param subprotocol The subprotocol in use
259  *
260  * @param res The response to store the processed response in
261  *
262  * @return An error code, 0 on success, non-zero for other errors
263  */
265  std::string const & subprotocol, response_type& res) const = 0;
266 
267  /// Fill in an HTTP request for an outgoing connection handshake
268  /**
269  * @param req The request to process.
270  *
271  * @return An error code, 0 on success, non-zero for other errors
272  */
274  uri_ptr uri, std::vector<std::string> const & subprotocols) const = 0;
275 
276  /// Validate the server's response to an outgoing handshake request
277  /**
278  * @param req The original request sent
279  * @param res The reponse to generate
280  * @return An error code, 0 on success, non-zero for other errors
281  */
283  const & req, response_type & res) const = 0;
284 
285  /// Given a completed response, get the raw bytes to put on the wire
286  virtual std::string get_raw(response_type const & request) const = 0;
287 
288  /// Return the value of the header containing the CORS origin.
289  virtual std::string const & get_origin(request_type const & request) const = 0;
290 
291  /// Extracts requested subprotocols from a handshake request
292  /**
293  * Extracts a list of all subprotocols that the client has requested in the
294  * given opening handshake request.
295  *
296  * @param [in] req The request to extract from
297  * @param [out] subprotocol_list A reference to a vector of strings to store
298  * the results in.
299  */
302 
303  /// Extracts client uri from a handshake request
304  virtual uri_ptr get_uri(request_type const & request) const = 0;
305 
306  /// process new websocket connection bytes
307  /**
308  * WebSocket connections are a continous stream of bytes that must be
309  * interpreted by a protocol processor into discrete frames.
310  *
311  * @param buf Buffer from which bytes should be read.
312  * @param len Length of buffer
313  * @param ec Reference to an error code to return any errors in
314  * @return Number of bytes processed
315  */
316  virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0;
317 
318  /// Checks if there is a message ready
319  /**
320  * Checks if the most recent consume operation processed enough bytes to
321  * complete a new WebSocket message. The message can be retrieved by calling
322  * get_message() which will reset the internal state to not-ready and allow
323  * consume to read more bytes.
324  *
325  * @return Whether or not a message is ready.
326  */
327  virtual bool ready() const = 0;
328 
329  /// Retrieves the most recently processed message
330  /**
331  * Retrieves a shared pointer to the recently completed message if there is
332  * one. If ready() returns true then there is a message available.
333  * Retrieving the message with get_message will reset the state of ready.
334  * As such, each new message may be retrieved only once. Calling get_message
335  * when there is no message available will result in a null pointer being
336  * returned.
337  *
338  * @return A pointer to the most recently processed message or a null shared
339  * pointer.
340  */
341  virtual message_ptr get_message() = 0;
342 
343  /// Tests whether the processor is in a fatal error state
344  virtual bool get_error() const = 0;
345 
346  /// Retrieves the number of bytes presently needed by the processor
347  /// This value may be used as a hint to the transport layer as to how many
348  /// bytes to wait for before running consume again.
349  virtual size_t get_bytes_needed() const {
350  return 1;
351  }
352 
353  /// Prepare a data message for writing
354  /**
355  * Performs validation, masking, compression, etc. will return an error if
356  * there was an error, otherwise msg will be ready to be written
357  */
359 
360  /// Prepare a ping frame
361  /**
362  * Ping preparation is entirely state free. There is no payload validation
363  * other than length. Payload need not be UTF-8.
364  *
365  * @param in The string to use for the ping payload
366  * @param out The message buffer to prepare the ping in.
367  * @return Status code, zero on success, non-zero on failure
368  */
369  virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
370  = 0;
371 
372  /// Prepare a pong frame
373  /**
374  * Pong preparation is entirely state free. There is no payload validation
375  * other than length. Payload need not be UTF-8.
376  *
377  * @param in The string to use for the pong payload
378  * @param out The message buffer to prepare the pong in.
379  * @return Status code, zero on success, non-zero on failure
380  */
381  virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
382  = 0;
383 
384  /// Prepare a close frame
385  /**
386  * Close preparation is entirely state free. The code and reason are both
387  * subject to validation. Reason must be valid UTF-8. Code must be a valid
388  * un-reserved WebSocket close code. Use close::status::no_status to
389  * indicate no code. If no code is supplied a reason may not be specified.
390  *
391  * @param code The close code to send
392  * @param reason The reason string to send
393  * @param out The message buffer to prepare the fame in
394  * @return Status code, zero on success, non-zero on failure
395  */
397  std::string const & reason, message_ptr out) const = 0;
398 protected:
399  bool const m_secure;
400  bool const m_server;
401  size_t m_max_message_size;
402 };
403 
404 } // namespace processor
405 } // namespace websocketpp
406 
407 #endif //WEBSOCKETPP_PROCESSOR_HPP
virtual err_str_pair negotiate_extensions(response_type const &)
Initializes extensions based on the Sec-WebSocket-Extensions header.
Definition: processor.hpp:239
size_t get_max_message_size() const
Get maximum message size.
Definition: processor.hpp:188
uri_ptr get_uri_from_host(request_type &request, std::string scheme)
Extract a URI ptr from the host header of the request.
Definition: processor.hpp:136
bool is_websocket_handshake(request_type &r)
Determine whether or not a generic HTTP request is a WebSocket handshake.
Definition: processor.hpp:68
void set_max_message_size(size_t new_value)
Set maximum message size.
Definition: processor.hpp:203
WebSocket protocol processor abstract base class.
Definition: processor.hpp:160
int get_websocket_version(request_type &r)
Extract the version from a WebSocket handshake request.
Definition: processor.hpp:107
virtual lib::error_code validate_handshake(request_type const &request) const =0
validate a WebSocket handshake request for this version
virtual std::string const & get_origin(request_type const &request) const =0
Return the value of the header containing the CORS origin.
virtual bool has_permessage_compress() const
Returns whether or not the permessage_compress extension is implemented.
Definition: processor.hpp:212
virtual lib::error_code prepare_pong(std::string const &in, message_ptr out) const =0
Prepare a pong frame.
virtual std::string get_raw(response_type const &request) const =0
Given a completed response, get the raw bytes to put on the wire.
virtual lib::error_code prepare_ping(std::string const &in, message_ptr out) const =0
Prepare a ping frame.
virtual bool get_error() const =0
Tests whether the processor is in a fatal error state.
virtual size_t consume(uint8_t *buf, size_t len, lib::error_code &ec)=0
process new websocket connection bytes
virtual int get_version() const =0
Get the protocol version of this processor.
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)=0
Prepare a data message for writing.
virtual message_ptr get_message()=0
Retrieves the most recently processed message.
virtual uri_ptr get_uri(request_type const &request) const =0
Extracts client uri from a handshake request.
virtual lib::error_code client_handshake_request(request_type &req, uri_ptr uri, std::vector< std::string > const &subprotocols) const =0
Fill in an HTTP request for an outgoing connection handshake.
virtual bool ready() const =0
Checks if there is a message ready.
void handle_accept(connection_ptr con, lib::error_code const &ec)
Handler callback for start_accept.
Processors encapsulate the protocol rules specific to each WebSocket version.
Definition: base.hpp:41
virtual lib::error_code prepare_close(close::status::value code, std::string const &reason, message_ptr out) const =0
Prepare a close frame.
virtual size_t get_bytes_needed() const
Definition: processor.hpp:349
virtual lib::error_code validate_server_handshake_response(request_type const &req, response_type &res) const =0
Validate the server's response to an outgoing handshake request.
virtual lib::error_code process_handshake(request_type const &req, std::string const &subprotocol, response_type &res) const =0
Calculate the appropriate response for this websocket request.
virtual lib::error_code extract_subprotocols(const request_type &req, std::vector< std::string > &subprotocol_list)=0
Extracts requested subprotocols from a handshake request.