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>
is_websocket_handshake(request_type & r)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>
get_websocket_version(request_type & r)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>
get_uri_from_host(request_type & request,std::string scheme)136 uri_ptr get_uri_from_host(request_type & request, std::string scheme) {
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
processor(bool secure,bool p_is_server)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
~processor()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 */
get_max_message_size() const188 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 */
set_max_message_size(size_t new_value)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 */
has_permessage_compress() const212 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 */
negotiate_extensions(request_type const &)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 */
negotiate_extensions(response_type const &)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 */
264 virtual lib::error_code process_handshake(request_type const & req,
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 */
273 virtual lib::error_code client_handshake_request(request_type & req,
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 */
282 virtual lib::error_code validate_server_handshake_response(request_type
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 */
300 virtual lib::error_code extract_subprotocols(const request_type & req,
301 std::vector<std::string> & subprotocol_list) = 0;
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.
get_bytes_needed() const349 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 */
358 virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
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 */
396 virtual lib::error_code prepare_close(close::status::value code,
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
408