summaryrefslogtreecommitdiffstats
path: root/websocketpp
diff options
context:
space:
mode:
Diffstat (limited to 'websocketpp')
-rw-r--r--websocketpp/base64/base64.hpp178
-rw-r--r--websocketpp/client.hpp33
-rw-r--r--websocketpp/close.hpp342
-rw-r--r--websocketpp/common/asio.hpp131
-rw-r--r--websocketpp/common/asio_ssl.hpp39
-rw-r--r--websocketpp/common/chrono.hpp68
-rw-r--r--websocketpp/common/connection_hdl.hpp52
-rw-r--r--websocketpp/common/cpp11.hpp162
-rw-r--r--websocketpp/common/functional.hpp105
-rw-r--r--websocketpp/common/md5.hpp448
-rw-r--r--websocketpp/common/memory.hpp89
-rw-r--r--websocketpp/common/network.hpp106
-rw-r--r--websocketpp/common/platforms.hpp46
-rw-r--r--websocketpp/common/random.hpp82
-rw-r--r--websocketpp/common/regex.hpp59
-rw-r--r--websocketpp/common/stdint.hpp73
-rw-r--r--websocketpp/common/system_error.hpp84
-rw-r--r--websocketpp/common/thread.hpp84
-rw-r--r--websocketpp/common/time.hpp56
-rw-r--r--websocketpp/common/type_traits.hpp65
-rw-r--r--websocketpp/concurrency/basic.hpp46
-rw-r--r--websocketpp/concurrency/none.hpp80
-rw-r--r--websocketpp/config/asio.hpp77
-rw-r--r--websocketpp/config/asio_client.hpp77
-rw-r--r--websocketpp/config/asio_no_tls.hpp73
-rw-r--r--websocketpp/config/asio_no_tls_client.hpp73
-rw-r--r--websocketpp/config/boost_config.hpp72
-rw-r--r--websocketpp/config/core.hpp285
-rw-r--r--websocketpp/config/core_client.hpp294
-rw-r--r--websocketpp/config/debug.hpp286
-rw-r--r--websocketpp/config/debug_asio.hpp77
-rw-r--r--websocketpp/config/debug_asio_no_tls.hpp73
-rw-r--r--websocketpp/config/minimal_client.hpp72
-rw-r--r--websocketpp/config/minimal_server.hpp312
-rw-r--r--websocketpp/connection.hpp1651
-rw-r--r--websocketpp/connection_base.hpp38
-rw-r--r--websocketpp/endpoint.hpp700
-rw-r--r--websocketpp/endpoint_base.hpp38
-rw-r--r--websocketpp/error.hpp277
-rw-r--r--websocketpp/extensions/extension.hpp102
-rw-r--r--websocketpp/extensions/permessage_deflate/disabled.hpp128
-rw-r--r--websocketpp/extensions/permessage_deflate/enabled.hpp752
-rw-r--r--websocketpp/frame.hpp861
-rw-r--r--websocketpp/http/constants.hpp308
-rw-r--r--websocketpp/http/impl/parser.hpp196
-rw-r--r--websocketpp/http/impl/request.hpp191
-rw-r--r--websocketpp/http/impl/response.hpp266
-rw-r--r--websocketpp/http/parser.hpp619
-rw-r--r--websocketpp/http/request.hpp124
-rw-r--r--websocketpp/http/response.hpp188
-rw-r--r--websocketpp/impl/connection_impl.hpp2372
-rw-r--r--websocketpp/impl/endpoint_impl.hpp269
-rw-r--r--websocketpp/impl/utilities_impl.hpp87
-rw-r--r--websocketpp/logger/basic.hpp199
-rw-r--r--websocketpp/logger/levels.hpp203
-rw-r--r--websocketpp/logger/stub.hpp119
-rw-r--r--websocketpp/logger/syslog.hpp146
-rw-r--r--websocketpp/message_buffer/alloc.hpp105
-rw-r--r--websocketpp/message_buffer/message.hpp340
-rw-r--r--websocketpp/message_buffer/pool.hpp229
-rw-r--r--websocketpp/processors/base.hpp299
-rw-r--r--websocketpp/processors/hybi00.hpp462
-rw-r--r--websocketpp/processors/hybi07.hpp78
-rw-r--r--websocketpp/processors/hybi08.hpp83
-rw-r--r--websocketpp/processors/hybi13.hpp1056
-rw-r--r--websocketpp/processors/processor.hpp407
-rw-r--r--websocketpp/random/none.hpp60
-rw-r--r--websocketpp/random/random_device.hpp80
-rw-r--r--websocketpp/roles/client_endpoint.hpp173
-rw-r--r--websocketpp/roles/server_endpoint.hpp190
-rw-r--r--websocketpp/server.hpp33
-rw-r--r--websocketpp/sha1/sha1.hpp189
-rw-r--r--websocketpp/transport/asio/base.hpp232
-rw-r--r--websocketpp/transport/asio/connection.hpp1204
-rw-r--r--websocketpp/transport/asio/endpoint.hpp1147
-rw-r--r--websocketpp/transport/asio/security/base.hpp159
-rw-r--r--websocketpp/transport/asio/security/none.hpp370
-rw-r--r--websocketpp/transport/asio/security/tls.hpp484
-rw-r--r--websocketpp/transport/base/connection.hpp238
-rw-r--r--websocketpp/transport/base/endpoint.hpp77
-rw-r--r--websocketpp/transport/debug/base.hpp104
-rw-r--r--websocketpp/transport/debug/connection.hpp412
-rw-r--r--websocketpp/transport/debug/endpoint.hpp140
-rw-r--r--websocketpp/transport/iostream/base.hpp133
-rw-r--r--websocketpp/transport/iostream/connection.hpp714
-rw-r--r--websocketpp/transport/iostream/endpoint.hpp222
-rw-r--r--websocketpp/transport/stub/base.hpp95
-rw-r--r--websocketpp/transport/stub/connection.hpp286
-rw-r--r--websocketpp/transport/stub/endpoint.hpp140
-rw-r--r--websocketpp/uri.hpp355
-rw-r--r--websocketpp/utf8_validator.hpp154
-rw-r--r--websocketpp/utilities.hpp182
-rw-r--r--websocketpp/version.hpp61
93 files changed, 24726 insertions, 0 deletions
diff --git a/websocketpp/base64/base64.hpp b/websocketpp/base64/base64.hpp
new file mode 100644
index 00000000..ff1561d1
--- /dev/null
+++ b/websocketpp/base64/base64.hpp
@@ -0,0 +1,178 @@
+/*
+ ******
+ base64.hpp is a repackaging of the base64.cpp and base64.h files into a
+ single header suitable for use as a header only library. This conversion was
+ done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to
+ the code are redistributed under the same license as the original, which is
+ listed below.
+ ******
+
+ base64.cpp and base64.h
+
+ Copyright (C) 2004-2008 René Nyffenegger
+
+ This source code is provided 'as-is', without any express or implied
+ warranty. In no event will the author be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented; you must not
+ claim that you wrote the original source code. If you use this source code
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original source code.
+
+ 3. This notice may not be removed or altered from any source distribution.
+
+ René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#ifndef _BASE64_HPP_
+#define _BASE64_HPP_
+
+#include <string>
+
+namespace websocketpp {
+
+static std::string const base64_chars =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+/// Test whether a character is a valid base64 character
+/**
+ * @param c The character to test
+ * @return true if c is a valid base64 character
+ */
+static inline bool is_base64(unsigned char c) {
+ return (c == 43 || // +
+ (c >= 47 && c <= 57) || // /-9
+ (c >= 65 && c <= 90) || // A-Z
+ (c >= 97 && c <= 122)); // a-z
+}
+
+/// Encode a char buffer into a base64 string
+/**
+ * @param input The input data
+ * @param len The length of input in bytes
+ * @return A base64 encoded string representing input
+ */
+inline std::string base64_encode(unsigned char const * input, size_t len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (len--) {
+ char_array_3[i++] = *(input++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
+ ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
+ ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for(i = 0; (i <4) ; i++) {
+ ret += base64_chars[char_array_4[i]];
+ }
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for(j = i; j < 3; j++) {
+ char_array_3[j] = '\0';
+ }
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
+ ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) +
+ ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++) {
+ ret += base64_chars[char_array_4[j]];
+ }
+
+ while((i++ < 3)) {
+ ret += '=';
+ }
+ }
+
+ return ret;
+}
+
+/// Encode a string into a base64 string
+/**
+ * @param input The input data
+ * @return A base64 encoded string representing input
+ */
+inline std::string base64_encode(std::string const & input) {
+ return base64_encode(
+ reinterpret_cast<const unsigned char *>(input.data()),
+ input.size()
+ );
+}
+
+/// Decode a base64 encoded string into a string of raw bytes
+/**
+ * @param input The base64 encoded input data
+ * @return A string representing the decoded raw bytes
+ */
+inline std::string base64_decode(std::string const & input) {
+ size_t in_len = input.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && ( input[in_] != '=') && is_base64(input[in_])) {
+ char_array_4[i++] = input[in_]; in_++;
+ if (i ==4) {
+ for (i = 0; i <4; i++) {
+ char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
+ }
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++) {
+ ret += char_array_3[i];
+ }
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j <4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j <4; j++)
+ char_array_4[j] = static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) {
+ ret += static_cast<std::string::value_type>(char_array_3[j]);
+ }
+ }
+
+ return ret;
+}
+
+} // namespace websocketpp
+
+#endif // _BASE64_HPP_
diff --git a/websocketpp/client.hpp b/websocketpp/client.hpp
new file mode 100644
index 00000000..8782d7e4
--- /dev/null
+++ b/websocketpp/client.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CLIENT_HPP
+#define WEBSOCKETPP_CLIENT_HPP
+
+#include <websocketpp/roles/client_endpoint.hpp>
+
+#endif //WEBSOCKETPP_CLIENT_HPP
diff --git a/websocketpp/close.hpp b/websocketpp/close.hpp
new file mode 100644
index 00000000..ded77657
--- /dev/null
+++ b/websocketpp/close.hpp
@@ -0,0 +1,342 @@
+
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CLOSE_HPP
+#define WEBSOCKETPP_CLOSE_HPP
+
+/** \file
+ * A package of types and methods for manipulating WebSocket close codes.
+ */
+
+#include <websocketpp/error.hpp>
+#include <websocketpp/common/network.hpp>
+#include <websocketpp/common/stdint.hpp>
+#include <websocketpp/utf8_validator.hpp>
+
+#include <string>
+
+namespace websocketpp {
+/// A package of types and methods for manipulating WebSocket close codes.
+namespace close {
+/// A package of types and methods for manipulating WebSocket close status'
+namespace status {
+ /// The type of a close code value.
+ typedef uint16_t value;
+
+ /// A blank value for internal use.
+ static value const blank = 0;
+
+ /// Close the connection without a WebSocket close handshake.
+ /**
+ * This special value requests that the WebSocket connection be closed
+ * without performing the WebSocket closing handshake. This does not comply
+ * with RFC6455, but should be safe to do if necessary. This could be useful
+ * for clients that need to disconnect quickly and cannot afford the
+ * complete handshake.
+ */
+ static value const omit_handshake = 1;
+
+ /// Close the connection with a forced TCP drop.
+ /**
+ * This special value requests that the WebSocket connection be closed by
+ * forcibly dropping the TCP connection. This will leave the other side of
+ * the connection with a broken connection and some expensive timeouts. this
+ * should not be done except in extreme cases or in cases of malicious
+ * remote endpoints.
+ */
+ static value const force_tcp_drop = 2;
+
+ /// Normal closure, meaning that the purpose for which the connection was
+ /// established has been fulfilled.
+ static value const normal = 1000;
+
+ /// The endpoint was "going away", such as a server going down or a browser
+ /// navigating away from a page.
+ static value const going_away = 1001;
+
+ /// A protocol error occurred.
+ static value const protocol_error = 1002;
+
+ /// The connection was terminated because an endpoint received a type of
+ /// data it cannot accept.
+ /**
+ * (e.g., an endpoint that understands only text data MAY send this if it
+ * receives a binary message).
+ */
+ static value const unsupported_data = 1003;
+
+ /// A dummy value to indicate that no status code was received.
+ /**
+ * This value is illegal on the wire.
+ */
+ static value const no_status = 1005;
+
+ /// A dummy value to indicate that the connection was closed abnormally.
+ /**
+ * In such a case there was no close frame to extract a value from. This
+ * value is illegal on the wire.
+ */
+ static value const abnormal_close = 1006;
+
+ /// An endpoint received message data inconsistent with its type.
+ /**
+ * For example: Invalid UTF8 bytes in a text message.
+ */
+ static value const invalid_payload = 1007;
+
+ /// An endpoint received a message that violated its policy.
+ /**
+ * This is a generic status code that can be returned when there is no other
+ * more suitable status code (e.g., 1003 or 1009) or if there is a need to
+ * hide specific details about the policy.
+ */
+ static value const policy_violation = 1008;
+
+ /// An endpoint received a message too large to process.
+ static value const message_too_big = 1009;
+
+ /// A client expected the server to accept a required extension request
+ /**
+ * The list of extensions that are needed SHOULD appear in the /reason/ part
+ * of the Close frame. Note that this status code is not used by the server,
+ * because it can fail the WebSocket handshake instead.
+ */
+ static value const extension_required = 1010;
+
+ /// An endpoint encountered an unexpected condition that prevented it from
+ /// fulfilling the request.
+ static value const internal_endpoint_error = 1011;
+
+ /// Indicates that the service is restarted. A client may reconnect and if
+ /// if it chooses to do so, should reconnect using a randomized delay of
+ /// 5-30s
+ static value const service_restart = 1012;
+
+ /// Indicates that the service is experiencing overload. A client should
+ /// only connect to a different IP (when there are multiple for the target)
+ /// or reconnect to the same IP upon user action.
+ static value const try_again_later = 1013;
+
+ /// An endpoint failed to perform a TLS handshake
+ /**
+ * Designated for use in applications expecting a status code to indicate
+ * that the connection was closed due to a failure to perform a TLS
+ * handshake (e.g., the server certificate can't be verified). This value is
+ * illegal on the wire.
+ */
+ static value const tls_handshake = 1015;
+
+ /// A generic subprotocol error
+ /**
+ * Indicates that a subprotocol error occurred. Typically this involves
+ * receiving a message that is not formatted as a valid message for the
+ * subprotocol in use.
+ */
+ static value const subprotocol_error = 3000;
+
+ /// A invalid subprotocol data
+ /**
+ * Indicates that data was received that violated the specification of the
+ * subprotocol in use.
+ */
+ static value const invalid_subprotocol_data = 3001;
+
+ /// First value in range reserved for future protocol use
+ static value const rsv_start = 1016;
+ /// Last value in range reserved for future protocol use
+ static value const rsv_end = 2999;
+
+ /// Test whether a close code is in a reserved range
+ /**
+ * @param [in] code The code to test
+ * @return Whether or not code is reserved
+ */
+ inline bool reserved(value code) {
+ return ((code >= rsv_start && code <= rsv_end) ||
+ code == 1004 || code == 1014);
+ }
+
+ /// First value in range that is always invalid on the wire
+ static value const invalid_low = 999;
+ /// Last value in range that is always invalid on the wire
+ static value const invalid_high = 5000;
+
+ /// Test whether a close code is invalid on the wire
+ /**
+ * @param [in] code The code to test
+ * @return Whether or not code is invalid on the wire
+ */
+ inline bool invalid(value code) {
+ return (code <= invalid_low || code >= invalid_high ||
+ code == no_status || code == abnormal_close ||
+ code == tls_handshake);
+ }
+
+ /// Determine if the code represents an unrecoverable error
+ /**
+ * There is a class of errors for which once they are discovered normal
+ * WebSocket functionality can no longer occur. This function determines
+ * if a given code is one of these values. This information is used to
+ * determine if the system has the capability of waiting for a close
+ * acknowledgement or if it should drop the TCP connection immediately
+ * after sending its close frame.
+ *
+ * @param [in] code The value to test.
+ * @return True if the code represents an unrecoverable error
+ */
+ inline bool terminal(value code) {
+ return (code == protocol_error || code == invalid_payload ||
+ code == policy_violation || code == message_too_big ||
+ code == internal_endpoint_error);
+ }
+
+ /// Return a human readable interpretation of a WebSocket close code
+ /**
+ * See https://tools.ietf.org/html/rfc6455#section-7.4 for more details.
+ *
+ * @since 0.3.0
+ *
+ * @param [in] code The code to look up.
+ * @return A human readable interpretation of the code.
+ */
+ inline std::string get_string(value code) {
+ switch (code) {
+ case normal:
+ return "Normal close";
+ case going_away:
+ return "Going away";
+ case protocol_error:
+ return "Protocol error";
+ case unsupported_data:
+ return "Unsupported data";
+ case no_status:
+ return "No status set";
+ case abnormal_close:
+ return "Abnormal close";
+ case invalid_payload:
+ return "Invalid payload";
+ case policy_violation:
+ return "Policy violoation";
+ case message_too_big:
+ return "Message too big";
+ case extension_required:
+ return "Extension required";
+ case internal_endpoint_error:
+ return "Internal endpoint error";
+ case tls_handshake:
+ return "TLS handshake failure";
+ case subprotocol_error:
+ return "Generic subprotocol error";
+ case invalid_subprotocol_data:
+ return "Invalid subprotocol data";
+ default:
+ return "Unknown";
+ }
+ }
+} // namespace status
+
+/// Type used to convert close statuses between integer and wire representations
+union code_converter {
+ uint16_t i;
+ char c[2];
+};
+
+/// Extract a close code value from a close payload
+/**
+ * If there is no close value (ie string is empty) status::no_status is
+ * returned. If a code couldn't be extracted (usually do to a short or
+ * otherwise mangled payload) status::protocol_error is returned and the ec
+ * value is flagged as an error. Note that this case is different than the case
+ * where protocol error is received over the wire.
+ *
+ * If the value is in an invalid or reserved range ec is set accordingly.
+ *
+ * @param [in] payload Close frame payload value received over the wire.
+ * @param [out] ec Set to indicate what error occurred, if any.
+ * @return The extracted value
+ */
+inline status::value extract_code(std::string const & payload, lib::error_code
+ & ec)
+{
+ ec = lib::error_code();
+
+ if (payload.size() == 0) {
+ return status::no_status;
+ } else if (payload.size() == 1) {
+ ec = make_error_code(error::bad_close_code);
+ return status::protocol_error;
+ }
+
+ code_converter val;
+
+ val.c[0] = payload[0];
+ val.c[1] = payload[1];
+
+ status::value code(ntohs(val.i));
+
+ if (status::invalid(code)) {
+ ec = make_error_code(error::invalid_close_code);
+ }
+
+ if (status::reserved(code)) {
+ ec = make_error_code(error::reserved_close_code);
+ }
+
+ return code;
+}
+
+/// Extract the reason string from a close payload
+/**
+ * The string should be a valid UTF8 message. error::invalid_utf8 will be set if
+ * the function extracts a reason that is not valid UTF8.
+ *
+ * @param [in] payload The payload string to extract a reason from.
+ * @param [out] ec Set to indicate what error occurred, if any.
+ * @return The reason string.
+ */
+inline std::string extract_reason(std::string const & payload, lib::error_code
+ & ec)
+{
+ std::string reason;
+ ec = lib::error_code();
+
+ if (payload.size() > 2) {
+ reason.append(payload.begin()+2,payload.end());
+ }
+
+ if (!websocketpp::utf8_validator::validate(reason)) {
+ ec = make_error_code(error::invalid_utf8);
+ }
+
+ return reason;
+}
+
+} // namespace close
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CLOSE_HPP
diff --git a/websocketpp/common/asio.hpp b/websocketpp/common/asio.hpp
new file mode 100644
index 00000000..ca483593
--- /dev/null
+++ b/websocketpp/common/asio.hpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_ASIO_HPP
+#define WEBSOCKETPP_COMMON_ASIO_HPP
+
+// This file goes to some length to preserve compatibility with versions of
+// boost older than 1.49 (where the first modern steady_timer timer based on
+// boost/std chrono was introduced.
+//
+// For the versions older than 1.49, the deadline_timer is used instead. this
+// brings in dependencies on boost date_time and it has a different interface
+// that is normalized by the `lib::asio::is_neg` and `lib::asio::milliseconds`
+// wrappers provided by this file.
+//
+// The primary reason for this continued support is that boost 1.48 is the
+// default and not easily changeable version of boost supplied by the package
+// manager of popular Linux distributions like Ubuntu 12.04 LTS. Once the need
+// for this has passed this should be cleaned up and simplified.
+
+#ifdef ASIO_STANDALONE
+ #include <asio/version.hpp>
+
+ #if (ASIO_VERSION/100000) == 1 && ((ASIO_VERSION/100)%1000) < 8
+ static_assert(false, "The minimum version of standalone Asio is 1.8.0");
+ #endif
+
+ #include <asio.hpp>
+ #include <asio/steady_timer.hpp>
+ #include <websocketpp/common/chrono.hpp>
+#else
+ #include <boost/version.hpp>
+
+ // See note above about boost <1.49 compatibility. If we are running on
+ // boost > 1.48 pull in the steady timer and chrono library
+ #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48
+ #include <boost/asio/steady_timer.hpp>
+ #include <websocketpp/common/chrono.hpp>
+ #endif
+
+ #include <boost/asio.hpp>
+ #include <boost/system/error_code.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef ASIO_STANDALONE
+ namespace asio {
+ using namespace ::asio;
+ // Here we assume that we will be using std::error_code with standalone
+ // Asio. This is probably a good assumption, but it is possible in rare
+ // cases that local Asio versions would be used.
+ using std::errc;
+
+ // See note above about boost <1.49 compatibility. Because we require
+ // a standalone Asio version of 1.8+ we are guaranteed to have
+ // steady_timer available. By convention we require the chrono library
+ // (either boost or std) for use with standalone Asio.
+ template <typename T>
+ bool is_neg(T duration) {
+ return duration.count() < 0;
+ }
+ inline lib::chrono::milliseconds milliseconds(long duration) {
+ return lib::chrono::milliseconds(duration);
+ }
+ } // namespace asio
+
+#else
+ namespace asio {
+ using namespace boost::asio;
+
+ // See note above about boost <1.49 compatibility
+ #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 48
+ // Using boost::asio >=1.49 so we use chrono and steady_timer
+ template <typename T>
+ bool is_neg(T duration) {
+ return duration.count() < 0;
+ }
+ inline lib::chrono::milliseconds milliseconds(long duration) {
+ return lib::chrono::milliseconds(duration);
+ }
+ #else
+ // Using boost::asio <1.49 we pretend a deadline timer is a steady
+ // timer and wrap the negative detection and duration conversion
+ // appropriately.
+ typedef boost::asio::deadline_timer steady_timer;
+
+ template <typename T>
+ bool is_neg(T duration) {
+ return duration.is_negative();
+ }
+ inline boost::posix_time::time_duration milliseconds(long duration) {
+ return boost::posix_time::milliseconds(duration);
+ }
+ #endif
+
+ using boost::system::error_code;
+ namespace errc = boost::system::errc;
+ } // namespace asio
+#endif
+
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_ASIO_HPP
diff --git a/websocketpp/common/asio_ssl.hpp b/websocketpp/common/asio_ssl.hpp
new file mode 100644
index 00000000..69892832
--- /dev/null
+++ b/websocketpp/common/asio_ssl.hpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_ASIO_SSL_HPP
+#define WEBSOCKETPP_COMMON_ASIO_SSL_HPP
+
+// NOTE: This file must be included before common/asio.hpp
+
+#ifdef ASIO_STANDALONE
+ #include <asio/ssl.hpp>
+#else
+ #include <boost/asio/ssl.hpp>
+#endif
+
+#endif // WEBSOCKETPP_COMMON_ASIO_SSL_HPP
diff --git a/websocketpp/common/chrono.hpp b/websocketpp/common/chrono.hpp
new file mode 100644
index 00000000..975ee043
--- /dev/null
+++ b/websocketpp/common/chrono.hpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_CHRONO_HPP
+#define WEBSOCKETPP_COMMON_CHRONO_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 functional header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_
+ #ifndef _WEBSOCKETPP_CPP11_CHRONO_
+ #define _WEBSOCKETPP_CPP11_CHRONO_
+ #endif
+#endif
+
+// If we're on Visual Studio 2012 or higher and haven't explicitly disabled
+// the use of C++11 chrono header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_
+ #ifndef _WEBSOCKETPP_CPP11_CHRONO_
+ #define _WEBSOCKETPP_CPP11_CHRONO_
+ #endif
+#endif
+
+#ifdef _WEBSOCKETPP_CPP11_CHRONO_
+ #include <chrono>
+#else
+ #include <boost/chrono.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_CHRONO_
+ namespace chrono = std::chrono;
+#else
+ namespace chrono = boost::chrono;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_CHRONO_HPP
diff --git a/websocketpp/common/connection_hdl.hpp b/websocketpp/common/connection_hdl.hpp
new file mode 100644
index 00000000..1044c88e
--- /dev/null
+++ b/websocketpp/common/connection_hdl.hpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP
+#define WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP
+
+#include <websocketpp/common/memory.hpp>
+
+namespace websocketpp {
+
+/// A handle to uniquely identify a connection.
+/**
+ * This type uniquely identifies a connection. It is implemented as a weak
+ * pointer to the connection in question. This provides uniqueness across
+ * multiple endpoints and ensures that IDs never conflict or run out.
+ *
+ * It is safe to make copies of this handle, store those copies in containers,
+ * and use them from other threads.
+ *
+ * This handle can be upgraded to a full shared_ptr using
+ * `endpoint::get_con_from_hdl()` from within a handler fired by the connection
+ * that owns the handler.
+ */
+typedef lib::weak_ptr<void> connection_hdl;
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_CONNECTION_HDL_HPP
diff --git a/websocketpp/common/cpp11.hpp b/websocketpp/common/cpp11.hpp
new file mode 100644
index 00000000..492a3b8f
--- /dev/null
+++ b/websocketpp/common/cpp11.hpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_CPP11_HPP
+#define WEBSOCKETPP_COMMON_CPP11_HPP
+
+/**
+ * This header sets up some constants based on the state of C++11 support
+ */
+
+// Hide clang feature detection from other compilers
+#ifndef __has_feature // Optional of course.
+ #define __has_feature(x) 0 // Compatibility with non-clang compilers.
+#endif
+#ifndef __has_extension
+ #define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
+#endif
+
+// The code below attempts to use information provided by the build system or
+// user supplied defines to selectively enable C++11 language and library
+// features. In most cases features that are targeted individually may also be
+// selectively disabled via an associated _WEBSOCKETPP_NOXXX_ define.
+
+#if defined(_WEBSOCKETPP_CPP11_STL_) || __cplusplus >= 201103L || defined(_WEBSOCKETPP_CPP11_STRICT_)
+ // This check tests for blanket c++11 coverage. It can be activated in one
+ // of three ways. Either the compiler itself reports that it is a full
+ // C++11 compiler via the __cplusplus macro or the user/build system
+ // supplies one of the two preprocessor defines below:
+
+ // This is defined to allow other WebSocket++ common headers to enable
+ // C++11 features when they are detected by this file rather than
+ // duplicating the above logic in every common header.
+ #define _WEBSOCKETPP_CPP11_INTERNAL_
+
+ // _WEBSOCKETPP_CPP11_STRICT_
+ //
+ // This define reports to WebSocket++ that 100% of the language and library
+ // features of C++11 are available. Using this define on a non-C++11
+ // compiler will result in problems.
+
+ // _WEBSOCKETPP_CPP11_STL_
+ //
+ // This define enables *most* C++11 options that were implemented early on
+ // by compilers. It is typically used for compilers that have many, but not
+ // all C++11 features. It should be safe to use on GCC 4.7-4.8 and perhaps
+ // earlier.
+ #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
+ #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
+ #endif
+ #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_
+ #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
+ #endif
+ #ifndef _WEBSOCKETPP_INITIALIZER_LISTS_
+ #define _WEBSOCKETPP_INITIALIZER_LISTS_
+ #endif
+ #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
+ #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
+ #endif
+ #ifndef _WEBSOCKETPP_MOVE_SEMANTICS_
+ #define _WEBSOCKETPP_MOVE_SEMANTICS_
+ #endif
+ #ifndef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ #define _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ #endif
+
+ #ifndef __GNUC__
+ // GCC as of version 4.9 (latest) does not support std::put_time yet.
+ // so ignore it
+ #define _WEBSOCKETPP_PUTTIME_
+ #endif
+#else
+ // In the absence of a blanket define, try to use compiler versions or
+ // feature testing macros to selectively enable what we can.
+
+ // Test for noexcept
+ #ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
+ #ifdef _WEBSOCKETPP_NOEXCEPT_
+ // build system says we have noexcept
+ #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
+ #else
+ #if __has_feature(cxx_noexcept)
+ // clang feature detect says we have noexcept
+ #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
+ #elif defined(_MSC_VER) && _MSC_VER >= 1900
+ // Visual Studio 2015+ has noexcept
+ #define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
+ #else
+ // assume we don't have noexcept
+ #define _WEBSOCKETPP_NOEXCEPT_TOKEN_
+ #endif
+ #endif
+ #endif
+
+ // Test for constexpr
+ #ifndef _WEBSOCKETPP_CONSTEXPR_TOKEN_
+ #ifdef _WEBSOCKETPP_CONSTEXPR_
+ // build system says we have constexpr
+ #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
+ #else
+ #if __has_feature(cxx_constexpr)
+ // clang feature detect says we have constexpr
+ #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
+ #elif defined(_MSC_VER) && _MSC_VER >= 1900
+ // Visual Studio 2015+ has constexpr
+ #define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
+ #else
+ // assume we don't have constexpr
+ #define _WEBSOCKETPP_CONSTEXPR_TOKEN_
+ #endif
+ #endif
+ #endif
+
+ // Enable initializer lists on clang when available.
+ #if __has_feature(cxx_generalized_initializers) && !defined(_WEBSOCKETPP_INITIALIZER_LISTS_)
+ #define _WEBSOCKETPP_INITIALIZER_LISTS_
+ #endif
+
+ // Test for nullptr
+ #ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
+ #ifdef _WEBSOCKETPP_NULLPTR_
+ // build system says we have nullptr
+ #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
+ #else
+ #if __has_feature(cxx_nullptr)
+ // clang feature detect says we have nullptr
+ #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
+ #elif defined(_MSC_VER) &&_MSC_VER >= 1600
+ // Visual Studio version that has nullptr
+ #define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
+ #else
+ // assume we don't have nullptr
+ #define _WEBSOCKETPP_NULLPTR_TOKEN_ 0
+ #endif
+ #endif
+ #endif
+#endif
+
+#endif // WEBSOCKETPP_COMMON_CPP11_HPP
diff --git a/websocketpp/common/functional.hpp b/websocketpp/common/functional.hpp
new file mode 100644
index 00000000..d332dd15
--- /dev/null
+++ b/websocketpp/common/functional.hpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
+#define WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 functional header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
+ #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ #define _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ #endif
+#endif
+
+// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
+// the use of C++11 functional header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
+ #ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ #define _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ #endif
+#endif
+
+
+
+#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ #include <functional>
+#else
+ #include <boost/bind.hpp>
+ #include <boost/function.hpp>
+ #include <boost/ref.hpp>
+#endif
+
+
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_
+ using std::function;
+ using std::bind;
+ using std::ref;
+ namespace placeholders = std::placeholders;
+
+ // There are some cases where a C++11 compiler balks at using std::ref
+ // but a C++03 compiler using boost function requires boost::ref. As such
+ // lib::ref is not useful in these cases. Instead this macro allows the use
+ // of boost::ref in the case of a boost compile or no reference wrapper at
+ // all in the case of a C++11 compile
+ #define _WEBSOCKETPP_REF(x) x
+
+ template <typename T>
+ void clear_function(T & x) {
+ x = nullptr;
+ }
+#else
+ using boost::function;
+ using boost::bind;
+ using boost::ref;
+ namespace placeholders {
+ /// \todo this feels hacky, is there a better way?
+ using ::_1;
+ using ::_2;
+ using ::_3;
+ }
+
+ // See above definition for more details on what this is and why it exists
+ #define _WEBSOCKETPP_REF(x) boost::ref(x)
+
+ template <typename T>
+ void clear_function(T & x) {
+ x.clear();
+ }
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
diff --git a/websocketpp/common/md5.hpp b/websocketpp/common/md5.hpp
new file mode 100644
index 00000000..279725f4
--- /dev/null
+++ b/websocketpp/common/md5.hpp
@@ -0,0 +1,448 @@
+/*
+ md5.hpp is a reformulation of the md5.h and md5.c code from
+ http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to
+ function as a component of a header only library. This conversion was done by
+ Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The
+ changes are released under the same license as the original (listed below)
+*/
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef WEBSOCKETPP_COMMON_MD5_HPP
+#define WEBSOCKETPP_COMMON_MD5_HPP
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+#include <stddef.h>
+#include <string>
+#include <cstring>
+
+namespace websocketpp {
+/// Provides MD5 hashing functionality
+namespace md5 {
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+inline void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+inline void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes);
+
+/* Finish the message and return the digest. */
+inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define ZSW_MD5_BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define ZSW_MD5_BYTE_ORDER 0
+#endif
+
+#define ZSW_MD5_T_MASK ((md5_word_t)~0)
+#define ZSW_MD5_T1 /* 0xd76aa478 */ (ZSW_MD5_T_MASK ^ 0x28955b87)
+#define ZSW_MD5_T2 /* 0xe8c7b756 */ (ZSW_MD5_T_MASK ^ 0x173848a9)
+#define ZSW_MD5_T3 0x242070db
+#define ZSW_MD5_T4 /* 0xc1bdceee */ (ZSW_MD5_T_MASK ^ 0x3e423111)
+#define ZSW_MD5_T5 /* 0xf57c0faf */ (ZSW_MD5_T_MASK ^ 0x0a83f050)
+#define ZSW_MD5_T6 0x4787c62a
+#define ZSW_MD5_T7 /* 0xa8304613 */ (ZSW_MD5_T_MASK ^ 0x57cfb9ec)
+#define ZSW_MD5_T8 /* 0xfd469501 */ (ZSW_MD5_T_MASK ^ 0x02b96afe)
+#define ZSW_MD5_T9 0x698098d8
+#define ZSW_MD5_T10 /* 0x8b44f7af */ (ZSW_MD5_T_MASK ^ 0x74bb0850)
+#define ZSW_MD5_T11 /* 0xffff5bb1 */ (ZSW_MD5_T_MASK ^ 0x0000a44e)
+#define ZSW_MD5_T12 /* 0x895cd7be */ (ZSW_MD5_T_MASK ^ 0x76a32841)
+#define ZSW_MD5_T13 0x6b901122
+#define ZSW_MD5_T14 /* 0xfd987193 */ (ZSW_MD5_T_MASK ^ 0x02678e6c)
+#define ZSW_MD5_T15 /* 0xa679438e */ (ZSW_MD5_T_MASK ^ 0x5986bc71)
+#define ZSW_MD5_T16 0x49b40821
+#define ZSW_MD5_T17 /* 0xf61e2562 */ (ZSW_MD5_T_MASK ^ 0x09e1da9d)
+#define ZSW_MD5_T18 /* 0xc040b340 */ (ZSW_MD5_T_MASK ^ 0x3fbf4cbf)
+#define ZSW_MD5_T19 0x265e5a51
+#define ZSW_MD5_T20 /* 0xe9b6c7aa */ (ZSW_MD5_T_MASK ^ 0x16493855)
+#define ZSW_MD5_T21 /* 0xd62f105d */ (ZSW_MD5_T_MASK ^ 0x29d0efa2)
+#define ZSW_MD5_T22 0x02441453
+#define ZSW_MD5_T23 /* 0xd8a1e681 */ (ZSW_MD5_T_MASK ^ 0x275e197e)
+#define ZSW_MD5_T24 /* 0xe7d3fbc8 */ (ZSW_MD5_T_MASK ^ 0x182c0437)
+#define ZSW_MD5_T25 0x21e1cde6
+#define ZSW_MD5_T26 /* 0xc33707d6 */ (ZSW_MD5_T_MASK ^ 0x3cc8f829)
+#define ZSW_MD5_T27 /* 0xf4d50d87 */ (ZSW_MD5_T_MASK ^ 0x0b2af278)
+#define ZSW_MD5_T28 0x455a14ed
+#define ZSW_MD5_T29 /* 0xa9e3e905 */ (ZSW_MD5_T_MASK ^ 0x561c16fa)
+#define ZSW_MD5_T30 /* 0xfcefa3f8 */ (ZSW_MD5_T_MASK ^ 0x03105c07)
+#define ZSW_MD5_T31 0x676f02d9
+#define ZSW_MD5_T32 /* 0x8d2a4c8a */ (ZSW_MD5_T_MASK ^ 0x72d5b375)
+#define ZSW_MD5_T33 /* 0xfffa3942 */ (ZSW_MD5_T_MASK ^ 0x0005c6bd)
+#define ZSW_MD5_T34 /* 0x8771f681 */ (ZSW_MD5_T_MASK ^ 0x788e097e)
+#define ZSW_MD5_T35 0x6d9d6122
+#define ZSW_MD5_T36 /* 0xfde5380c */ (ZSW_MD5_T_MASK ^ 0x021ac7f3)
+#define ZSW_MD5_T37 /* 0xa4beea44 */ (ZSW_MD5_T_MASK ^ 0x5b4115bb)
+#define ZSW_MD5_T38 0x4bdecfa9
+#define ZSW_MD5_T39 /* 0xf6bb4b60 */ (ZSW_MD5_T_MASK ^ 0x0944b49f)
+#define ZSW_MD5_T40 /* 0xbebfbc70 */ (ZSW_MD5_T_MASK ^ 0x4140438f)
+#define ZSW_MD5_T41 0x289b7ec6
+#define ZSW_MD5_T42 /* 0xeaa127fa */ (ZSW_MD5_T_MASK ^ 0x155ed805)
+#define ZSW_MD5_T43 /* 0xd4ef3085 */ (ZSW_MD5_T_MASK ^ 0x2b10cf7a)
+#define ZSW_MD5_T44 0x04881d05
+#define ZSW_MD5_T45 /* 0xd9d4d039 */ (ZSW_MD5_T_MASK ^ 0x262b2fc6)
+#define ZSW_MD5_T46 /* 0xe6db99e5 */ (ZSW_MD5_T_MASK ^ 0x1924661a)
+#define ZSW_MD5_T47 0x1fa27cf8
+#define ZSW_MD5_T48 /* 0xc4ac5665 */ (ZSW_MD5_T_MASK ^ 0x3b53a99a)
+#define ZSW_MD5_T49 /* 0xf4292244 */ (ZSW_MD5_T_MASK ^ 0x0bd6ddbb)
+#define ZSW_MD5_T50 0x432aff97
+#define ZSW_MD5_T51 /* 0xab9423a7 */ (ZSW_MD5_T_MASK ^ 0x546bdc58)
+#define ZSW_MD5_T52 /* 0xfc93a039 */ (ZSW_MD5_T_MASK ^ 0x036c5fc6)
+#define ZSW_MD5_T53 0x655b59c3
+#define ZSW_MD5_T54 /* 0x8f0ccc92 */ (ZSW_MD5_T_MASK ^ 0x70f3336d)
+#define ZSW_MD5_T55 /* 0xffeff47d */ (ZSW_MD5_T_MASK ^ 0x00100b82)
+#define ZSW_MD5_T56 /* 0x85845dd1 */ (ZSW_MD5_T_MASK ^ 0x7a7ba22e)
+#define ZSW_MD5_T57 0x6fa87e4f
+#define ZSW_MD5_T58 /* 0xfe2ce6e0 */ (ZSW_MD5_T_MASK ^ 0x01d3191f)
+#define ZSW_MD5_T59 /* 0xa3014314 */ (ZSW_MD5_T_MASK ^ 0x5cfebceb)
+#define ZSW_MD5_T60 0x4e0811a1
+#define ZSW_MD5_T61 /* 0xf7537e82 */ (ZSW_MD5_T_MASK ^ 0x08ac817d)
+#define ZSW_MD5_T62 /* 0xbd3af235 */ (ZSW_MD5_T_MASK ^ 0x42c50dca)
+#define ZSW_MD5_T63 0x2ad7d2bb
+#define ZSW_MD5_T64 /* 0xeb86d391 */ (ZSW_MD5_T_MASK ^ 0x14792c6e)
+
+static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) {
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if ZSW_MD5_BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ md5_word_t const * X;
+#endif
+
+ {
+#if ZSW_MD5_BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static int const w = 1;
+
+ if (*((md5_byte_t const *)&w)) /* dynamic little-endian */
+#endif
+#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (md5_byte_t const *)0) & 3)) {
+ /* data are properly aligned */
+ X = (md5_word_t const *)data;
+ } else {
+ /* not aligned */
+ std::memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if ZSW_MD5_BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if ZSW_MD5_BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ZSW_MD5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define ZSW_MD5_F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + ZSW_MD5_F(b,c,d) + X[k] + Ti;\
+ a = ZSW_MD5_ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, ZSW_MD5_T1);
+ SET(d, a, b, c, 1, 12, ZSW_MD5_T2);
+ SET(c, d, a, b, 2, 17, ZSW_MD5_T3);
+ SET(b, c, d, a, 3, 22, ZSW_MD5_T4);
+ SET(a, b, c, d, 4, 7, ZSW_MD5_T5);
+ SET(d, a, b, c, 5, 12, ZSW_MD5_T6);
+ SET(c, d, a, b, 6, 17, ZSW_MD5_T7);
+ SET(b, c, d, a, 7, 22, ZSW_MD5_T8);
+ SET(a, b, c, d, 8, 7, ZSW_MD5_T9);
+ SET(d, a, b, c, 9, 12, ZSW_MD5_T10);
+ SET(c, d, a, b, 10, 17, ZSW_MD5_T11);
+ SET(b, c, d, a, 11, 22, ZSW_MD5_T12);
+ SET(a, b, c, d, 12, 7, ZSW_MD5_T13);
+ SET(d, a, b, c, 13, 12, ZSW_MD5_T14);
+ SET(c, d, a, b, 14, 17, ZSW_MD5_T15);
+ SET(b, c, d, a, 15, 22, ZSW_MD5_T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define ZSW_MD5_G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + ZSW_MD5_G(b,c,d) + X[k] + Ti;\
+ a = ZSW_MD5_ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, ZSW_MD5_T17);
+ SET(d, a, b, c, 6, 9, ZSW_MD5_T18);
+ SET(c, d, a, b, 11, 14, ZSW_MD5_T19);
+ SET(b, c, d, a, 0, 20, ZSW_MD5_T20);
+ SET(a, b, c, d, 5, 5, ZSW_MD5_T21);
+ SET(d, a, b, c, 10, 9, ZSW_MD5_T22);
+ SET(c, d, a, b, 15, 14, ZSW_MD5_T23);
+ SET(b, c, d, a, 4, 20, ZSW_MD5_T24);
+ SET(a, b, c, d, 9, 5, ZSW_MD5_T25);
+ SET(d, a, b, c, 14, 9, ZSW_MD5_T26);
+ SET(c, d, a, b, 3, 14, ZSW_MD5_T27);
+ SET(b, c, d, a, 8, 20, ZSW_MD5_T28);
+ SET(a, b, c, d, 13, 5, ZSW_MD5_T29);
+ SET(d, a, b, c, 2, 9, ZSW_MD5_T30);
+ SET(c, d, a, b, 7, 14, ZSW_MD5_T31);
+ SET(b, c, d, a, 12, 20, ZSW_MD5_T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define ZSW_MD5_H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + ZSW_MD5_H(b,c,d) + X[k] + Ti;\
+ a = ZSW_MD5_ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, ZSW_MD5_T33);
+ SET(d, a, b, c, 8, 11, ZSW_MD5_T34);
+ SET(c, d, a, b, 11, 16, ZSW_MD5_T35);
+ SET(b, c, d, a, 14, 23, ZSW_MD5_T36);
+ SET(a, b, c, d, 1, 4, ZSW_MD5_T37);
+ SET(d, a, b, c, 4, 11, ZSW_MD5_T38);
+ SET(c, d, a, b, 7, 16, ZSW_MD5_T39);
+ SET(b, c, d, a, 10, 23, ZSW_MD5_T40);
+ SET(a, b, c, d, 13, 4, ZSW_MD5_T41);
+ SET(d, a, b, c, 0, 11, ZSW_MD5_T42);
+ SET(c, d, a, b, 3, 16, ZSW_MD5_T43);
+ SET(b, c, d, a, 6, 23, ZSW_MD5_T44);
+ SET(a, b, c, d, 9, 4, ZSW_MD5_T45);
+ SET(d, a, b, c, 12, 11, ZSW_MD5_T46);
+ SET(c, d, a, b, 15, 16, ZSW_MD5_T47);
+ SET(b, c, d, a, 2, 23, ZSW_MD5_T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define ZSW_MD5_I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + ZSW_MD5_I(b,c,d) + X[k] + Ti;\
+ a = ZSW_MD5_ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, ZSW_MD5_T49);
+ SET(d, a, b, c, 7, 10, ZSW_MD5_T50);
+ SET(c, d, a, b, 14, 15, ZSW_MD5_T51);
+ SET(b, c, d, a, 5, 21, ZSW_MD5_T52);
+ SET(a, b, c, d, 12, 6, ZSW_MD5_T53);
+ SET(d, a, b, c, 3, 10, ZSW_MD5_T54);
+ SET(c, d, a, b, 10, 15, ZSW_MD5_T55);
+ SET(b, c, d, a, 1, 21, ZSW_MD5_T56);
+ SET(a, b, c, d, 8, 6, ZSW_MD5_T57);
+ SET(d, a, b, c, 15, 10, ZSW_MD5_T58);
+ SET(c, d, a, b, 6, 15, ZSW_MD5_T59);
+ SET(b, c, d, a, 13, 21, ZSW_MD5_T60);
+ SET(a, b, c, d, 4, 6, ZSW_MD5_T61);
+ SET(d, a, b, c, 11, 10, ZSW_MD5_T62);
+ SET(c, d, a, b, 2, 15, ZSW_MD5_T63);
+ SET(b, c, d, a, 9, 21, ZSW_MD5_T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void md5_init(md5_state_t *pms) {
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ ZSW_MD5_T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ ZSW_MD5_T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes) {
+ md5_byte_t const * p = data;
+ size_t left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
+
+ std::memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ std::memcpy(pms->buf, p, left);
+}
+
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) {
+ static md5_byte_t const pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+// some convenience c++ functions
+inline std::string md5_hash_string(std::string const & s) {
+ char digest[16];
+
+ md5_state_t state;
+
+ md5_init(&state);
+ md5_append(&state, (md5_byte_t const *)s.c_str(), s.size());
+ md5_finish(&state, (md5_byte_t *)digest);
+
+ std::string ret;
+ ret.resize(16);
+ std::copy(digest,digest+16,ret.begin());
+
+ return ret;
+}
+
+const char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+inline std::string md5_hash_hex(std::string const & input) {
+ std::string hash = md5_hash_string(input);
+ std::string hex;
+
+ for (size_t i = 0; i < hash.size(); i++) {
+ hex.push_back(hexval[((hash[i] >> 4) & 0xF)]);
+ hex.push_back(hexval[(hash[i]) & 0x0F]);
+ }
+
+ return hex;
+}
+
+} // md5
+} // websocketpp
+
+#endif // WEBSOCKETPP_COMMON_MD5_HPP
diff --git a/websocketpp/common/memory.hpp b/websocketpp/common/memory.hpp
new file mode 100644
index 00000000..581aa559
--- /dev/null
+++ b/websocketpp/common/memory.hpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_MEMORY_HPP
+#define WEBSOCKETPP_COMMON_MEMORY_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 memory header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
+ #ifndef _WEBSOCKETPP_CPP11_MEMORY_
+ #define _WEBSOCKETPP_CPP11_MEMORY_
+ #endif
+#endif
+
+// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
+// the use of C++11 functional header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
+ #ifndef _WEBSOCKETPP_CPP11_MEMORY_
+ #define _WEBSOCKETPP_CPP11_MEMORY_
+ #endif
+#endif
+
+
+
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ #include <memory>
+#else
+ #include <boost/shared_ptr.hpp>
+ #include <boost/make_shared.hpp>
+ #include <boost/scoped_array.hpp>
+ #include <boost/enable_shared_from_this.hpp>
+ #include <boost/pointer_cast.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ using std::shared_ptr;
+ using std::weak_ptr;
+ using std::auto_ptr;
+ using std::enable_shared_from_this;
+ using std::static_pointer_cast;
+ using std::make_shared;
+ using std::unique_ptr;
+
+ typedef std::unique_ptr<unsigned char[]> unique_ptr_uchar_array;
+#else
+ using boost::shared_ptr;
+ using boost::weak_ptr;
+ using std::auto_ptr;
+ using boost::enable_shared_from_this;
+ using boost::static_pointer_cast;
+ using boost::make_shared;
+
+ typedef boost::scoped_array<unsigned char> unique_ptr_uchar_array;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_MEMORY_HPP
diff --git a/websocketpp/common/network.hpp b/websocketpp/common/network.hpp
new file mode 100644
index 00000000..26a4cb9e
--- /dev/null
+++ b/websocketpp/common/network.hpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_NETWORK_HPP
+#define WEBSOCKETPP_COMMON_NETWORK_HPP
+
+// For ntohs and htons
+#if defined(_WIN32)
+ #include <winsock2.h>
+#else
+ //#include <arpa/inet.h>
+ #include <netinet/in.h>
+#endif
+
+#include <websocketpp/common/stdint.hpp>
+
+namespace websocketpp {
+namespace lib {
+namespace net {
+
+inline bool is_little_endian() {
+ short int val = 0x1;
+ char *ptr = reinterpret_cast<char *>(&val);
+ return (ptr[0] == 1);
+}
+
+#define TYP_INIT 0
+#define TYP_SMLE 1
+#define TYP_BIGE 2
+
+/// Convert 64 bit value to network byte order
+/**
+ * This method is prefixed to avoid conflicts with operating system level
+ * macros for this functionality.
+ *
+ * TODO: figure out if it would be beneficial to use operating system level
+ * macros for this.
+ *
+ * @param src The integer in host byte order
+ * @return src converted to network byte order
+ */
+inline uint64_t _htonll(uint64_t src) {
+ static int typ = TYP_INIT;
+ unsigned char c;
+ union {
+ uint64_t ull;
+ unsigned char c[8];
+ } x;
+ if (typ == TYP_INIT) {
+ x.ull = 0x01;
+ typ = (x.c[7] == 0x01ULL) ? TYP_BIGE : TYP_SMLE;
+ }
+ if (typ == TYP_BIGE)
+ return src;
+ x.ull = src;
+ c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
+ c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
+ c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c;
+ c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c;
+ return x.ull;
+}
+
+/// Convert 64 bit value to host byte order
+/**
+ * This method is prefixed to avoid conflicts with operating system level
+ * macros for this functionality.
+ *
+ * TODO: figure out if it would be beneficial to use operating system level
+ * macros for this.
+ *
+ * @param src The integer in network byte order
+ * @return src converted to host byte order
+ */
+inline uint64_t _ntohll(uint64_t src) {
+ return _htonll(src);
+}
+
+} // net
+} // lib
+} // websocketpp
+
+#endif // WEBSOCKETPP_COMMON_NETWORK_HPP
diff --git a/websocketpp/common/platforms.hpp b/websocketpp/common/platforms.hpp
new file mode 100644
index 00000000..87798577
--- /dev/null
+++ b/websocketpp/common/platforms.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_PLATFORMS_HPP
+#define WEBSOCKETPP_COMMON_PLATFORMS_HPP
+
+/**
+ * This header contains any platform specific preprocessor adjustments that
+ * don't fit somewhere else better.
+ */
+
+#if defined(_WIN32) && !defined(NOMINMAX)
+ // don't define min and max macros that conflict with std::min and std::max
+ #define NOMINMAX
+#endif
+
+// Bump up the variadic parameter max for Visual Studio 2012
+#if defined(_MSC_VER) && _MSC_VER == 1700
+ #define _VARIADIC_MAX 8
+#endif
+
+#endif // WEBSOCKETPP_COMMON_PLATFORMS_HPP
diff --git a/websocketpp/common/random.hpp b/websocketpp/common/random.hpp
new file mode 100644
index 00000000..ddf99694
--- /dev/null
+++ b/websocketpp/common/random.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
+#define WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 random header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_
+ #ifndef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
+ #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
+ #endif
+#endif
+
+
+// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
+// the use of C++11 random header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
+ #ifndef _WEBSOCKETPP_CPP11_MEMORY_
+ #define _WEBSOCKETPP_CPP11_MEMORY_
+ #endif
+#endif
+
+
+
+#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
+ #include <random>
+#else
+ #include <boost/version.hpp>
+
+ #if (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) > 46
+ #include <boost/random/uniform_int_distribution.hpp>
+ #include <boost/random/random_device.hpp>
+ #elif (BOOST_VERSION/100000) == 1 && ((BOOST_VERSION/100)%1000) >= 43
+ #include <boost/nondet_random.hpp>
+ #else
+ // TODO: static_assert(false, "Could not find a suitable random_device")
+ #endif
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
+ using std::random_device;
+ using std::uniform_int_distribution;
+#else
+ using boost::random::random_device;
+ using boost::random::uniform_int_distribution;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
diff --git a/websocketpp/common/regex.hpp b/websocketpp/common/regex.hpp
new file mode 100644
index 00000000..326635de
--- /dev/null
+++ b/websocketpp/common/regex.hpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_REGEX_HPP
+#define WEBSOCKETPP_COMMON_REGEX_HPP
+
+#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_REGEX_
+ #ifndef _WEBSOCKETPP_CPP11_REGEX_
+ #define _WEBSOCKETPP_CPP11_REGEX_
+ #endif
+#endif
+
+#ifdef _WEBSOCKETPP_CPP11_REGEX_
+ #include <regex>
+#else
+ #include <boost/regex.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_REGEX_
+ using std::cmatch;
+ using std::regex;
+ using std::regex_match;
+#else
+ using boost::cmatch;
+ using boost::regex;
+ using boost::regex_match;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_REGEX_HPP
diff --git a/websocketpp/common/stdint.hpp b/websocketpp/common/stdint.hpp
new file mode 100644
index 00000000..ec48ea75
--- /dev/null
+++ b/websocketpp/common/stdint.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_STDINT_HPP
+#define WEBSOCKETPP_COMMON_STDINT_HPP
+
+#ifndef __STDC_LIMIT_MACROS
+ #define __STDC_LIMIT_MACROS 1
+#endif
+
+#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600)
+ #include <boost/cstdint.hpp>
+
+ using boost::int8_t;
+ using boost::int_least8_t;
+ using boost::int_fast8_t;
+ using boost::uint8_t;
+ using boost::uint_least8_t;
+ using boost::uint_fast8_t;
+
+ using boost::int16_t;
+ using boost::int_least16_t;
+ using boost::int_fast16_t;
+ using boost::uint16_t;
+ using boost::uint_least16_t;
+ using boost::uint_fast16_t;
+
+ using boost::int32_t;
+ using boost::int_least32_t;
+ using boost::int_fast32_t;
+ using boost::uint32_t;
+ using boost::uint_least32_t;
+ using boost::uint_fast32_t;
+
+ #ifndef BOOST_NO_INT64_T
+ using boost::int64_t;
+ using boost::int_least64_t;
+ using boost::int_fast64_t;
+ using boost::uint64_t;
+ using boost::uint_least64_t;
+ using boost::uint_fast64_t;
+ #endif
+ using boost::intmax_t;
+ using boost::uintmax_t;
+#else
+ #include <stdint.h>
+#endif
+
+#endif // WEBSOCKETPP_COMMON_STDINT_HPP
diff --git a/websocketpp/common/system_error.hpp b/websocketpp/common/system_error.hpp
new file mode 100644
index 00000000..e5aea254
--- /dev/null
+++ b/websocketpp/common/system_error.hpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
+#define WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
+
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 system_error header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
+ #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ #endif
+#endif
+
+// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
+// the use of C++11 system_error header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
+ #ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ #endif
+#endif
+
+
+
+#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ #include <system_error>
+#else
+ #include <boost/system/error_code.hpp>
+ #include <boost/system/system_error.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+ using std::errc;
+ using std::error_code;
+ using std::error_category;
+ using std::error_condition;
+ using std::system_error;
+ #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace std {
+ #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ }
+#else
+ namespace errc = boost::system::errc;
+ using boost::system::error_code;
+ using boost::system::error_category;
+ using boost::system::error_condition;
+ using boost::system::system_error;
+ #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace boost { namespace system {
+ #define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ }}
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
diff --git a/websocketpp/common/thread.hpp b/websocketpp/common/thread.hpp
new file mode 100644
index 00000000..09f6b3c5
--- /dev/null
+++ b/websocketpp/common/thread.hpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_THREAD_HPP
+#define WEBSOCKETPP_COMMON_THREAD_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we autodetect C++11 and haven't been explicitly instructed to not use
+// C++11 threads, then set the defines that instructs the rest of this header
+// to use C++11 <thread> and <mutex>
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
+ // MinGW by default does not support C++11 thread/mutex so even if the
+ // internal check for C++11 passes, ignore it if we are on MinGW
+ #if (!defined(__MINGW32__) && !defined(__MINGW64__))
+ #ifndef _WEBSOCKETPP_CPP11_THREAD_
+ #define _WEBSOCKETPP_CPP11_THREAD_
+ #endif
+ #endif
+#endif
+
+// If we're on Visual Studio 2013 or higher and haven't explicitly disabled
+// the use of C++11 thread header then prefer it to boost.
+#if defined(_MSC_VER) && _MSC_VER >= 1800 && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
+ #ifndef _WEBSOCKETPP_CPP11_THREAD_
+ #define _WEBSOCKETPP_CPP11_THREAD_
+ #endif
+#endif
+
+#ifdef _WEBSOCKETPP_CPP11_THREAD_
+ #include <thread>
+ #include <mutex>
+ #include <condition_variable>
+#else
+ #include <boost/thread.hpp>
+ #include <boost/thread/mutex.hpp>
+ #include <boost/thread/condition_variable.hpp>
+#endif
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_THREAD_
+ using std::mutex;
+ using std::lock_guard;
+ using std::thread;
+ using std::unique_lock;
+ using std::condition_variable;
+#else
+ using boost::mutex;
+ using boost::lock_guard;
+ using boost::thread;
+ using boost::unique_lock;
+ using boost::condition_variable;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_THREAD_HPP
diff --git a/websocketpp/common/time.hpp b/websocketpp/common/time.hpp
new file mode 100644
index 00000000..571688e1
--- /dev/null
+++ b/websocketpp/common/time.hpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_TIME_HPP
+#define WEBSOCKETPP_COMMON_TIME_HPP
+
+#include <ctime>
+
+namespace websocketpp {
+namespace lib {
+
+// Code in this header was inspired by the following article and includes some
+// code from the related project g2log. The g2log code is public domain licensed
+// http://kjellkod.wordpress.com/2013/01/22/exploring-c11-part-2-localtime-and-time-again/
+
+/// Thread safe cross platform localtime
+inline std::tm localtime(std::time_t const & time) {
+ std::tm tm_snapshot;
+#if (defined(__MINGW32__) || defined(__MINGW64__))
+ memcpy(&tm_snapshot, ::localtime(&time), sizeof(std::tm));
+#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
+ localtime_s(&tm_snapshot, &time);
+#else
+ localtime_r(&time, &tm_snapshot); // POSIX
+#endif
+ return tm_snapshot;
+}
+
+} // lib
+} // websocketpp
+
+#endif // WEBSOCKETPP_COMMON_TIME_HPP
diff --git a/websocketpp/common/type_traits.hpp b/websocketpp/common/type_traits.hpp
new file mode 100644
index 00000000..c89b82b3
--- /dev/null
+++ b/websocketpp/common/type_traits.hpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP
+#define WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+
+// If we've determined that we're in full C++11 mode and the user hasn't
+// explicitly disabled the use of C++11 functional header, then prefer it to
+// boost.
+#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_TYPE_TRAITS_
+ #ifndef _WEBSOCKETPP_CPP11_TYPE_TRAITS_
+ #define _WEBSOCKETPP_CPP11_TYPE_TRAITS_
+ #endif
+#endif
+
+
+#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_
+ #include <type_traits>
+#else
+ #include <boost/aligned_storage.hpp>
+#endif
+
+
+
+namespace websocketpp {
+namespace lib {
+
+#ifdef _WEBSOCKETPP_CPP11_TYPE_TRAITS_
+ using std::aligned_storage;
+ using std::is_same;
+#else
+ using boost::aligned_storage;
+ using boost::is_same;
+#endif
+
+} // namespace lib
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_COMMON_TYPE_TRAITS_HPP
diff --git a/websocketpp/concurrency/basic.hpp b/websocketpp/concurrency/basic.hpp
new file mode 100644
index 00000000..1943ad77
--- /dev/null
+++ b/websocketpp/concurrency/basic.hpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONCURRENCY_BASIC_HPP
+#define WEBSOCKETPP_CONCURRENCY_BASIC_HPP
+
+#include <websocketpp/common/thread.hpp>
+
+namespace websocketpp {
+namespace concurrency {
+
+/// Concurrency policy that uses std::mutex / boost::mutex
+class basic {
+public:
+ typedef lib::mutex mutex_type;
+ typedef lib::lock_guard<mutex_type> scoped_lock_type;
+};
+
+} // namespace concurrency
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONCURRENCY_BASIC_HPP
diff --git a/websocketpp/concurrency/none.hpp b/websocketpp/concurrency/none.hpp
new file mode 100644
index 00000000..da9aa411
--- /dev/null
+++ b/websocketpp/concurrency/none.hpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONCURRENCY_NONE_HPP
+#define WEBSOCKETPP_CONCURRENCY_NONE_HPP
+
+namespace websocketpp {
+
+/// Concurrency handling support
+namespace concurrency {
+
+/// Implementation for no-op locking primitives
+namespace none_impl {
+/// A fake mutex implementation that does nothing
+class fake_mutex {
+public:
+ fake_mutex() {}
+ ~fake_mutex() {}
+};
+
+/// A fake lock guard implementation that does nothing
+class fake_lock_guard {
+public:
+ explicit fake_lock_guard(fake_mutex) {}
+ ~fake_lock_guard() {}
+};
+} // namespace none_impl
+
+/// Stub concurrency policy that implements the interface using no-ops.
+/**
+ * This policy documents the concurrency policy interface using no-ops. It can
+ * be used as a reference or base for building a new concurrency policy. It can
+ * also be used as is to disable all locking for endpoints used in purely single
+ * threaded programs.
+ */
+class none {
+public:
+ /// The type of a mutex primitive
+ /**
+ * std::mutex is an example.
+ */
+ typedef none_impl::fake_mutex mutex_type;
+
+ /// The type of a scoped/RAII lock primitive.
+ /**
+ * The scoped lock constructor should take a mutex_type as a parameter,
+ * acquire that lock, and release it in its destructor. std::lock_guard is
+ * an example.
+ */
+ typedef none_impl::fake_lock_guard scoped_lock_type;
+};
+
+} // namespace concurrency
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONCURRENCY_ASYNC_HPP
diff --git a/websocketpp/config/asio.hpp b/websocketpp/config/asio.hpp
new file mode 100644
index 00000000..d28d0fb8
--- /dev/null
+++ b/websocketpp/config/asio.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_TLS_HPP
+
+#include <websocketpp/config/core.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+#include <websocketpp/transport/asio/security/tls.hpp>
+
+// Pull in non-tls config
+#include <websocketpp/config/asio_no_tls.hpp>
+
+// Define TLS config
+namespace websocketpp {
+namespace config {
+
+/// Server config with asio transport and TLS enabled
+struct asio_tls : public core {
+ typedef asio_tls type;
+ typedef core base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_HPP
diff --git a/websocketpp/config/asio_client.hpp b/websocketpp/config/asio_client.hpp
new file mode 100644
index 00000000..1cb594d0
--- /dev/null
+++ b/websocketpp/config/asio_client.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP
+
+#include <websocketpp/config/core_client.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+#include <websocketpp/transport/asio/security/tls.hpp>
+
+// Pull in non-tls config
+#include <websocketpp/config/asio_no_tls_client.hpp>
+
+// Define TLS config
+namespace websocketpp {
+namespace config {
+
+/// Client config with asio transport and TLS enabled
+struct asio_tls_client : public core_client {
+ typedef asio_tls_client type;
+ typedef core_client base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_CLIENT_HPP
diff --git a/websocketpp/config/asio_no_tls.hpp b/websocketpp/config/asio_no_tls.hpp
new file mode 100644
index 00000000..6c1357fb
--- /dev/null
+++ b/websocketpp/config/asio_no_tls.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_HPP
+
+#include <websocketpp/config/core.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Server config with asio transport and TLS disabled
+struct asio : public core {
+ typedef asio type;
+ typedef core base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::basic_socket::endpoint
+ socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_HPP
diff --git a/websocketpp/config/asio_no_tls_client.hpp b/websocketpp/config/asio_no_tls_client.hpp
new file mode 100644
index 00000000..6e3f7ba0
--- /dev/null
+++ b/websocketpp/config/asio_no_tls_client.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP
+
+#include <websocketpp/config/core_client.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Client config with asio transport and TLS disabled
+struct asio_client : public core_client {
+ typedef asio_client type;
+ typedef core_client base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::basic_socket::endpoint
+ socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_CLIENT_HPP
diff --git a/websocketpp/config/boost_config.hpp b/websocketpp/config/boost_config.hpp
new file mode 100644
index 00000000..57671ccd
--- /dev/null
+++ b/websocketpp/config/boost_config.hpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+ // This header defines WebSocket++ macros for C++11 compatibility based on the
+ // Boost.Config library. This will correctly configure most target platforms
+ // simply by including this header before any other WebSocket++ header.
+
+#ifndef WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
+#define WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
+
+#include <boost/config.hpp>
+
+// _WEBSOCKETPP_CPP11_MEMORY_ and _WEBSOCKETPP_CPP11_FUNCTIONAL_ presently
+// only work if either both or neither is defined.
+#if !defined BOOST_NO_CXX11_SMART_PTR && !defined BOOST_NO_CXX11_HDR_FUNCTIONAL
+ #define _WEBSOCKETPP_CPP11_MEMORY_
+ #define _WEBSOCKETPP_CPP11_FUNCTIONAL_
+#endif
+
+#ifdef BOOST_ASIO_HAS_STD_CHRONO
+ #define _WEBSOCKETPP_CPP11_CHRONO_
+#endif
+
+#ifndef BOOST_NO_CXX11_HDR_RANDOM
+ #define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
+#endif
+
+#ifndef BOOST_NO_CXX11_HDR_REGEX
+ #define _WEBSOCKETPP_CPP11_REGEX_
+#endif
+
+#ifndef BOOST_NO_CXX11_HDR_SYSTEM_ERROR
+ #define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
+#endif
+
+#ifndef BOOST_NO_CXX11_HDR_THREAD
+ #define _WEBSOCKETPP_CPP11_THREAD_
+#endif
+
+#ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
+ #define _WEBSOCKETPP_INITIALIZER_LISTS_
+#endif
+
+#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ BOOST_NOEXCEPT
+#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ BOOST_CONSTEXPR
+// TODO: nullptr support
+
+#endif // WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
diff --git a/websocketpp/config/core.hpp b/websocketpp/config/core.hpp
new file mode 100644
index 00000000..a95b4021
--- /dev/null
+++ b/websocketpp/config/core.hpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_CORE_HPP
+#define WEBSOCKETPP_CONFIG_CORE_HPP
+
+// Non-Policy common stuff
+#include <websocketpp/common/platforms.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/stdint.hpp>
+
+// Concurrency
+#include <websocketpp/concurrency/basic.hpp>
+
+// Transport
+#include <websocketpp/transport/iostream/endpoint.hpp>
+
+// HTTP
+#include <websocketpp/http/request.hpp>
+#include <websocketpp/http/response.hpp>
+
+// Messages
+#include <websocketpp/message_buffer/message.hpp>
+#include <websocketpp/message_buffer/alloc.hpp>
+
+// Loggers
+#include <websocketpp/logger/basic.hpp>
+
+// RNG
+#include <websocketpp/random/none.hpp>
+
+// User stub base classes
+#include <websocketpp/endpoint_base.hpp>
+#include <websocketpp/connection_base.hpp>
+
+// Extensions
+#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Server config with iostream transport
+struct core {
+ typedef core type;
+
+ // Concurrency policy
+ typedef websocketpp::concurrency::basic concurrency_type;
+
+ // HTTP Parser Policies
+ typedef http::parser::request request_type;
+ typedef http::parser::response response_type;
+
+ // Message Policies
+ typedef message_buffer::message<message_buffer::alloc::con_msg_manager>
+ message_type;
+ typedef message_buffer::alloc::con_msg_manager<message_type>
+ con_msg_manager_type;
+ typedef message_buffer::alloc::endpoint_msg_manager<con_msg_manager_type>
+ endpoint_msg_manager_type;
+
+ /// Logging policies
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::elevel> elog_type;
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::alevel> alog_type;
+
+ /// RNG policies
+ typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ struct transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::elog_type elog_type;
+ typedef type::alog_type alog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ /// Default timer values (in ms)
+
+ /// Length of time to wait for socket pre-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use
+ */
+ static const long timeout_socket_pre_init = 5000;
+
+ /// Length of time to wait before a proxy handshake is aborted
+ static const long timeout_proxy = 5000;
+
+ /// Length of time to wait for socket post-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use.
+ * Often this means the TLS handshake
+ */
+ static const long timeout_socket_post_init = 5000;
+
+ /// Length of time to wait for dns resolution
+ static const long timeout_dns_resolve = 5000;
+
+ /// Length of time to wait for TCP connect
+ static const long timeout_connect = 5000;
+
+ /// Length of time to wait for socket shutdown
+ static const long timeout_socket_shutdown = 5000;
+ };
+
+ /// Transport Endpoint Component
+ typedef websocketpp::transport::iostream::endpoint<transport_config>
+ transport_type;
+
+ /// User overridable Endpoint base class
+ typedef websocketpp::endpoint_base endpoint_base;
+ /// User overridable Connection base class
+ typedef websocketpp::connection_base connection_base;
+
+ /// Default timer values (in ms)
+
+ /// Length of time before an opening handshake is aborted
+ static const long timeout_open_handshake = 5000;
+ /// Length of time before a closing handshake is aborted
+ static const long timeout_close_handshake = 5000;
+ /// Length of time to wait for a pong after a ping
+ static const long timeout_pong = 5000;
+
+ /// WebSocket Protocol version to use as a client
+ /**
+ * What version of the WebSocket Protocol to use for outgoing client
+ * connections. Setting this to a value other than 13 (RFC6455) is not
+ * recommended.
+ */
+ static const int client_version = 13; // RFC6455
+
+ /// Default static error logging channels
+ /**
+ * Which error logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level errors
+ */
+ static const websocketpp::log::level elog_level =
+ websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel;
+
+ /// Default static access logging channels
+ /**
+ * Which access logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level access messages
+ */
+ static const websocketpp::log::level alog_level =
+ websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel;
+
+ ///
+ static const size_t connection_read_buffer_size = 16384;
+
+ /// Drop connections immediately on protocol error.
+ /**
+ * Drop connections on protocol error rather than sending a close frame.
+ * Off by default. This may result in legit messages near the error being
+ * dropped as well. It may free up resources otherwise spent dealing with
+ * misbehaving clients.
+ */
+ static const bool drop_on_protocol_error = false;
+
+ /// Suppresses the return of detailed connection close information
+ /**
+ * Silence close suppresses the return of detailed connection close
+ * information during the closing handshake. This information is useful
+ * for debugging and presenting useful errors to end users but may be
+ * undesirable for security reasons in some production environments.
+ * Close reasons could be used by an attacker to confirm that the endpoint
+ * is out of resources or be used to identify the WebSocket implementation
+ * in use.
+ *
+ * Note: this will suppress *all* close codes, including those explicitly
+ * sent by local applications.
+ */
+ static const bool silent_close = false;
+
+ /// Default maximum message size
+ /**
+ * Default value for the processor's maximum message size. Maximum message size
+ * determines the point at which the library will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.3.0
+ */
+ static const size_t max_message_size = 32000000;
+
+ /// Default maximum http body size
+ /**
+ * Default value for the http parser's maximum body size. Maximum body size
+ * determines the point at which the library will abort reading an HTTP
+ * connection with the 413/request entity too large error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.5.0
+ */
+ static const size_t max_http_body_size = 32000000;
+
+ /// Global flag for enabling/disabling extensions
+ static const bool enable_extensions = true;
+
+ /// Extension specific settings:
+
+ /// permessage_compress extension
+ struct permessage_deflate_config {
+ typedef core::request_type request_type;
+
+ /// If the remote endpoint requests that we reset the compression
+ /// context after each message should we honor the request?
+ static const bool allow_disabling_context_takeover = true;
+
+ /// If the remote endpoint requests that we reduce the size of the
+ /// LZ77 sliding window size this is the lowest value that will be
+ /// allowed. Values range from 8 to 15. A value of 8 means we will
+ /// allow any possible window size. A value of 15 means do not allow
+ /// negotiation of the window size (ie require the default).
+ static const uint8_t minimum_outgoing_window_bits = 8;
+ };
+
+ typedef websocketpp::extensions::permessage_deflate::disabled
+ <permessage_deflate_config> permessage_deflate_type;
+
+ /// Autonegotiate permessage-deflate
+ /**
+ * Automatically enables the permessage-deflate extension.
+ *
+ * For clients this results in a permessage-deflate extension request being
+ * sent with every request rather than requiring it to be requested manually
+ *
+ * For servers this results in accepting the first set of extension settings
+ * requested by the client that we understand being used. The alternative is
+ * requiring the extension to be manually negotiated in `validate`. With
+ * auto-negotiate on, you may still override the auto-negotiate manually if
+ * needed.
+ */
+ //static const bool autonegotiate_compression = false;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_CORE_HPP
diff --git a/websocketpp/config/core_client.hpp b/websocketpp/config/core_client.hpp
new file mode 100644
index 00000000..dadf8a4e
--- /dev/null
+++ b/websocketpp/config/core_client.hpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP
+#define WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP
+
+// Non-Policy common stuff
+#include <websocketpp/common/platforms.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/stdint.hpp>
+
+// Concurrency
+#ifndef _WEBSOCKETPP_NO_THREADING_
+#include <websocketpp/concurrency/basic.hpp>
+#else
+#include <websocketpp/concurrency/none.hpp>
+#endif
+
+// Transport
+#include <websocketpp/transport/iostream/endpoint.hpp>
+
+// HTTP
+#include <websocketpp/http/request.hpp>
+#include <websocketpp/http/response.hpp>
+
+// Messages
+#include <websocketpp/message_buffer/message.hpp>
+#include <websocketpp/message_buffer/alloc.hpp>
+
+// Loggers
+#include <websocketpp/logger/basic.hpp>
+
+// RNG
+#include <websocketpp/random/random_device.hpp>
+
+// User stub base classes
+#include <websocketpp/endpoint_base.hpp>
+#include <websocketpp/connection_base.hpp>
+
+// Extensions
+#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Client config with iostream transport
+struct core_client {
+ typedef core_client type;
+
+ // Concurrency policy
+#ifndef _WEBSOCKETPP_NO_THREADING_
+ typedef websocketpp::concurrency::basic concurrency_type;
+#else
+ typedef websocketpp::concurrency::none concurrency_type;
+#endif
+
+ // HTTP Parser Policies
+ typedef http::parser::request request_type;
+ typedef http::parser::response response_type;
+
+ // Message Policies
+ typedef message_buffer::message<message_buffer::alloc::con_msg_manager>
+ message_type;
+ typedef message_buffer::alloc::con_msg_manager<message_type>
+ con_msg_manager_type;
+ typedef message_buffer::alloc::endpoint_msg_manager<con_msg_manager_type>
+ endpoint_msg_manager_type;
+
+ /// Logging policies
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::elevel> elog_type;
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::alevel> alog_type;
+
+ /// RNG policies
+ typedef websocketpp::random::random_device::int_generator<uint32_t,
+ concurrency_type> rng_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization code
+ /// Disabling can provide a minor performance improvement to single threaded
+ /// applications
+ static bool const enable_multithreading = true;
+
+ struct transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::elog_type elog_type;
+ typedef type::alog_type alog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ /// Default timer values (in ms)
+
+ /// Length of time to wait for socket pre-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use
+ */
+ static const long timeout_socket_pre_init = 5000;
+
+ /// Length of time to wait before a proxy handshake is aborted
+ static const long timeout_proxy = 5000;
+
+ /// Length of time to wait for socket post-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use.
+ * Often this means the TLS handshake
+ */
+ static const long timeout_socket_post_init = 5000;
+
+ /// Length of time to wait for dns resolution
+ static const long timeout_dns_resolve = 5000;
+
+ /// Length of time to wait for TCP connect
+ static const long timeout_connect = 5000;
+
+ /// Length of time to wait for socket shutdown
+ static const long timeout_socket_shutdown = 5000;
+ };
+
+ /// Transport Endpoint Component
+ typedef websocketpp::transport::iostream::endpoint<transport_config>
+ transport_type;
+
+ /// User overridable Endpoint base class
+ typedef websocketpp::endpoint_base endpoint_base;
+ /// User overridable Connection base class
+ typedef websocketpp::connection_base connection_base;
+
+ /// Default timer values (in ms)
+
+ /// Length of time before an opening handshake is aborted
+ static const long timeout_open_handshake = 5000;
+ /// Length of time before a closing handshake is aborted
+ static const long timeout_close_handshake = 5000;
+ /// Length of time to wait for a pong after a ping
+ static const long timeout_pong = 5000;
+
+ /// WebSocket Protocol version to use as a client
+ /**
+ * What version of the WebSocket Protocol to use for outgoing client
+ * connections. Setting this to a value other than 13 (RFC6455) is not
+ * recommended.
+ */
+ static const int client_version = 13; // RFC6455
+
+ /// Default static error logging channels
+ /**
+ * Which error logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level errors
+ */
+ static const websocketpp::log::level elog_level =
+ websocketpp::log::elevel::all ^ websocketpp::log::elevel::devel;
+
+ /// Default static access logging channels
+ /**
+ * Which access logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level access messages
+ */
+ static const websocketpp::log::level alog_level =
+ websocketpp::log::alevel::all ^ websocketpp::log::alevel::devel;
+
+ ///
+ static const size_t connection_read_buffer_size = 16384;
+
+ /// Drop connections immediately on protocol error.
+ /**
+ * Drop connections on protocol error rather than sending a close frame.
+ * Off by default. This may result in legit messages near the error being
+ * dropped as well. It may free up resources otherwise spent dealing with
+ * misbehaving clients.
+ */
+ static const bool drop_on_protocol_error = false;
+
+ /// Suppresses the return of detailed connection close information
+ /**
+ * Silence close suppresses the return of detailed connection close
+ * information during the closing handshake. This information is useful
+ * for debugging and presenting useful errors to end users but may be
+ * undesirable for security reasons in some production environments.
+ * Close reasons could be used by an attacker to confirm that the endpoint
+ * is out of resources or be used to identify the WebSocket implementation
+ * in use.
+ *
+ * Note: this will suppress *all* close codes, including those explicitly
+ * sent by local applications.
+ */
+ static const bool silent_close = false;
+
+ /// Default maximum message size
+ /**
+ * Default value for the processor's maximum message size. Maximum message size
+ * determines the point at which the library will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.3.0
+ */
+ static const size_t max_message_size = 32000000;
+
+ /// Default maximum http body size
+ /**
+ * Default value for the http parser's maximum body size. Maximum body size
+ * determines the point at which the library will abort reading an HTTP
+ * connection with the 413/request entity too large error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.5.0
+ */
+ static const size_t max_http_body_size = 32000000;
+
+ /// Global flag for enabling/disabling extensions
+ static const bool enable_extensions = true;
+
+ /// Extension specific settings:
+
+ /// permessage_deflate extension
+ struct permessage_deflate_config {
+ typedef core_client::request_type request_type;
+
+ /// If the remote endpoint requests that we reset the compression
+ /// context after each message should we honor the request?
+ static const bool allow_disabling_context_takeover = true;
+
+ /// If the remote endpoint requests that we reduce the size of the
+ /// LZ77 sliding window size this is the lowest value that will be
+ /// allowed. Values range from 8 to 15. A value of 8 means we will
+ /// allow any possible window size. A value of 15 means do not allow
+ /// negotiation of the window size (ie require the default).
+ static const uint8_t minimum_outgoing_window_bits = 8;
+ };
+
+ typedef websocketpp::extensions::permessage_deflate::disabled
+ <permessage_deflate_config> permessage_deflate_type;
+
+ /// Autonegotiate permessage-compress
+ /**
+ * Automatically enables the permessage-compress extension.
+ *
+ * For clients this results in a permessage-compress extension request being
+ * sent with every request rather than requiring it to be requested manually
+ *
+ * For servers this results in accepting the first set of extension settings
+ * requested by the client that we understand being used. The alternative is
+ * requiring the extension to be manually negotiated in `validate`. With
+ * auto-negotiate on, you may still override the auto-negotiate manually if
+ * needed.
+ */
+ //static const bool autonegotiate_compression = false;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_CORE_CLIENT_HPP
diff --git a/websocketpp/config/debug.hpp b/websocketpp/config/debug.hpp
new file mode 100644
index 00000000..223f72fb
--- /dev/null
+++ b/websocketpp/config/debug.hpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_DEBUG_HPP
+#define WEBSOCKETPP_CONFIG_DEBUG_HPP
+
+
+
+// Non-Policy common stuff
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/stdint.hpp>
+
+// Concurrency
+#include <websocketpp/concurrency/basic.hpp>
+
+// Transport
+#include <websocketpp/transport/iostream/endpoint.hpp>
+
+// HTTP
+#include <websocketpp/http/request.hpp>
+#include <websocketpp/http/response.hpp>
+
+// Messages
+#include <websocketpp/message_buffer/message.hpp>
+#include <websocketpp/message_buffer/alloc.hpp>
+
+// Loggers
+#include <websocketpp/logger/basic.hpp>
+
+// RNG
+#include <websocketpp/random/none.hpp>
+
+// User stub base classes
+#include <websocketpp/endpoint_base.hpp>
+#include <websocketpp/connection_base.hpp>
+
+// Extensions
+#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Client/Server debug config with iostream transport
+struct debug_core {
+ typedef debug_core type;
+
+ // Concurrency policy
+ typedef websocketpp::concurrency::basic concurrency_type;
+
+ // HTTP Parser Policies
+ typedef http::parser::request request_type;
+ typedef http::parser::response response_type;
+
+ // Message Policies
+ typedef message_buffer::message<message_buffer::alloc::con_msg_manager>
+ message_type;
+ typedef message_buffer::alloc::con_msg_manager<message_type>
+ con_msg_manager_type;
+ typedef message_buffer::alloc::endpoint_msg_manager<con_msg_manager_type>
+ endpoint_msg_manager_type;
+
+ /// Logging policies
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::elevel> elog_type;
+ typedef websocketpp::log::basic<concurrency_type,
+ websocketpp::log::alevel> alog_type;
+
+ /// RNG policies
+ typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ struct transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::elog_type elog_type;
+ typedef type::alog_type alog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ /// Default timer values (in ms)
+
+ /// Length of time to wait for socket pre-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use
+ */
+ static const long timeout_socket_pre_init = 5000;
+
+ /// Length of time to wait before a proxy handshake is aborted
+ static const long timeout_proxy = 5000;
+
+ /// Length of time to wait for socket post-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use.
+ * Often this means the TLS handshake
+ */
+ static const long timeout_socket_post_init = 5000;
+
+ /// Length of time to wait for dns resolution
+ static const long timeout_dns_resolve = 5000;
+
+ /// Length of time to wait for TCP connect
+ static const long timeout_connect = 5000;
+
+ /// Length of time to wait for socket shutdown
+ static const long timeout_socket_shutdown = 5000;
+ };
+
+ /// Transport Endpoint Component
+ typedef websocketpp::transport::iostream::endpoint<transport_config>
+ transport_type;
+
+ /// User overridable Endpoint base class
+ typedef websocketpp::endpoint_base endpoint_base;
+ /// User overridable Connection base class
+ typedef websocketpp::connection_base connection_base;
+
+ /// Default timer values (in ms)
+
+ /// Length of time before an opening handshake is aborted
+ static const long timeout_open_handshake = 5000;
+ /// Length of time before a closing handshake is aborted
+ static const long timeout_close_handshake = 5000;
+ /// Length of time to wait for a pong after a ping
+ static const long timeout_pong = 5000;
+
+ /// WebSocket Protocol version to use as a client
+ /**
+ * What version of the WebSocket Protocol to use for outgoing client
+ * connections. Setting this to a value other than 13 (RFC6455) is not
+ * recommended.
+ */
+ static const int client_version = 13; // RFC6455
+
+ /// Default static error logging channels
+ /**
+ * Which error logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level errors
+ */
+ static const websocketpp::log::level elog_level =
+ websocketpp::log::elevel::all;
+
+ /// Default static access logging channels
+ /**
+ * Which access logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level access messages
+ */
+ static const websocketpp::log::level alog_level =
+ websocketpp::log::alevel::all;
+
+ ///
+ static const size_t connection_read_buffer_size = 16384;
+
+ /// Drop connections immediately on protocol error.
+ /**
+ * Drop connections on protocol error rather than sending a close frame.
+ * Off by default. This may result in legit messages near the error being
+ * dropped as well. It may free up resources otherwise spent dealing with
+ * misbehaving clients.
+ */
+ static const bool drop_on_protocol_error = false;
+
+ /// Suppresses the return of detailed connection close information
+ /**
+ * Silence close suppresses the return of detailed connection close
+ * information during the closing handshake. This information is useful
+ * for debugging and presenting useful errors to end users but may be
+ * undesirable for security reasons in some production environments.
+ * Close reasons could be used by an attacker to confirm that the endpoint
+ * is out of resources or be used to identify the WebSocket implementation
+ * in use.
+ *
+ * Note: this will suppress *all* close codes, including those explicitly
+ * sent by local applications.
+ */
+ static const bool silent_close = false;
+
+ /// Default maximum message size
+ /**
+ * Default value for the processor's maximum message size. Maximum message size
+ * determines the point at which the library will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.3.0
+ */
+ static const size_t max_message_size = 32000000;
+
+ /// Default maximum http body size
+ /**
+ * Default value for the http parser's maximum body size. Maximum body size
+ * determines the point at which the library will abort reading an HTTP
+ * connection with the 413/request entity too large error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.5.0
+ */
+ static const size_t max_http_body_size = 32000000;
+
+ /// Global flag for enabling/disabling extensions
+ static const bool enable_extensions = true;
+
+ /// Extension specific settings:
+
+ /// permessage_compress extension
+ struct permessage_deflate_config {
+ typedef type::request_type request_type;
+
+ /// If the remote endpoint requests that we reset the compression
+ /// context after each message should we honor the request?
+ static const bool allow_disabling_context_takeover = true;
+
+ /// If the remote endpoint requests that we reduce the size of the
+ /// LZ77 sliding window size this is the lowest value that will be
+ /// allowed. Values range from 8 to 15. A value of 8 means we will
+ /// allow any possible window size. A value of 15 means do not allow
+ /// negotiation of the window size (ie require the default).
+ static const uint8_t minimum_outgoing_window_bits = 8;
+ };
+
+ typedef websocketpp::extensions::permessage_deflate::disabled
+ <permessage_deflate_config> permessage_deflate_type;
+
+ /// Autonegotiate permessage-deflate
+ /**
+ * Automatically enables the permessage-deflate extension.
+ *
+ * For clients this results in a permessage-deflate extension request being
+ * sent with every request rather than requiring it to be requested manually
+ *
+ * For servers this results in accepting the first set of extension settings
+ * requested by the client that we understand being used. The alternative is
+ * requiring the extension to be manually negotiated in `validate`. With
+ * auto-negotiate on, you may still override the auto-negotiate manually if
+ * needed.
+ */
+ //static const bool autonegotiate_compression = false;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_CORE_HPP
diff --git a/websocketpp/config/debug_asio.hpp b/websocketpp/config/debug_asio.hpp
new file mode 100644
index 00000000..a57c736c
--- /dev/null
+++ b/websocketpp/config/debug_asio.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP
+
+#include <websocketpp/config/debug.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+#include <websocketpp/transport/asio/security/tls.hpp>
+
+// Pull in non-tls config
+#include <websocketpp/config/debug_asio_no_tls.hpp>
+
+// Define TLS config
+namespace websocketpp {
+namespace config {
+
+/// Client/Server debug config with asio transport and TLS enabled
+struct debug_asio_tls : public debug_core {
+ typedef debug_asio_tls type;
+ typedef debug_core base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_TLS_DEBUG_HPP
diff --git a/websocketpp/config/debug_asio_no_tls.hpp b/websocketpp/config/debug_asio_no_tls.hpp
new file mode 100644
index 00000000..b3dc83b4
--- /dev/null
+++ b/websocketpp/config/debug_asio_no_tls.hpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP
+#define WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP
+
+#include <websocketpp/config/debug.hpp>
+#include <websocketpp/transport/asio/endpoint.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Client/Server debug config with asio transport and TLS disabled
+struct debug_asio : public debug_core {
+ typedef debug_asio type;
+ typedef debug_core base;
+
+ typedef base::concurrency_type concurrency_type;
+
+ typedef base::request_type request_type;
+ typedef base::response_type response_type;
+
+ typedef base::message_type message_type;
+ typedef base::con_msg_manager_type con_msg_manager_type;
+ typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
+
+ typedef base::alog_type alog_type;
+ typedef base::elog_type elog_type;
+
+ typedef base::rng_type rng_type;
+
+ struct transport_config : public base::transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::alog_type alog_type;
+ typedef type::elog_type elog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+ typedef websocketpp::transport::asio::basic_socket::endpoint
+ socket_type;
+ };
+
+ typedef websocketpp::transport::asio::endpoint<transport_config>
+ transport_type;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_ASIO_DEBUG_HPP
diff --git a/websocketpp/config/minimal_client.hpp b/websocketpp/config/minimal_client.hpp
new file mode 100644
index 00000000..72528cde
--- /dev/null
+++ b/websocketpp/config/minimal_client.hpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP
+#define WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP
+
+#include <websocketpp/config/minimal_server.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Client config with minimal dependencies
+/**
+ * This config strips out as many dependencies as possible. It is suitable for
+ * use as a base class for custom configs that want to implement or choose their
+ * own policies for components that even the core config includes.
+ *
+ * NOTE: this config stubs out enough that it cannot be used directly. You must
+ * supply at least a transport policy and a cryptographically secure random
+ * number generation policy for a config based on `minimal_client` to do
+ * anything useful.
+ *
+ * Present dependency list for minimal_server config:
+ *
+ * C++98 STL:
+ * <algorithm>
+ * <map>
+ * <sstream>
+ * <string>
+ * <vector>
+ *
+ * C++11 STL or Boost
+ * <memory>
+ * <functional>
+ * <system_error>
+ *
+ * Operating System:
+ * <stdint.h> or <boost/cstdint.hpp>
+ * <netinet/in.h> or <winsock2.h> (for ntohl.. could potentially bundle this)
+ *
+ * @since 0.4.0-dev
+ */
+typedef minimal_server minimal_client;
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_MINIMAL_CLIENT_HPP
diff --git a/websocketpp/config/minimal_server.hpp b/websocketpp/config/minimal_server.hpp
new file mode 100644
index 00000000..dd1aedb9
--- /dev/null
+++ b/websocketpp/config/minimal_server.hpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONFIG_MINIMAL_HPP
+#define WEBSOCKETPP_CONFIG_MINIMAL_HPP
+
+// Non-Policy common stuff
+#include <websocketpp/common/platforms.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/stdint.hpp>
+
+// Concurrency
+#include <websocketpp/concurrency/none.hpp>
+
+// Transport
+#include <websocketpp/transport/stub/endpoint.hpp>
+
+// HTTP
+#include <websocketpp/http/request.hpp>
+#include <websocketpp/http/response.hpp>
+
+// Messages
+#include <websocketpp/message_buffer/message.hpp>
+#include <websocketpp/message_buffer/alloc.hpp>
+
+// Loggers
+#include <websocketpp/logger/stub.hpp>
+
+// RNG
+#include <websocketpp/random/none.hpp>
+
+// User stub base classes
+#include <websocketpp/endpoint_base.hpp>
+#include <websocketpp/connection_base.hpp>
+
+// Extensions
+#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
+
+namespace websocketpp {
+namespace config {
+
+/// Server config with minimal dependencies
+/**
+ * This config strips out as many dependencies as possible. It is suitable for
+ * use as a base class for custom configs that want to implement or choose their
+ * own policies for components that even the core config includes.
+ *
+ * NOTE: this config stubs out enough that it cannot be used directly. You must
+ * supply at least a transport policy for a config based on `minimal_server` to
+ * do anything useful.
+ *
+ * Present dependency list for minimal_server config:
+ *
+ * C++98 STL:
+ * <algorithm>
+ * <map>
+ * <sstream>
+ * <string>
+ * <vector>
+ *
+ * C++11 STL or Boost
+ * <memory>
+ * <functional>
+ * <system_error>
+ *
+ * Operating System:
+ * <stdint.h> or <boost/cstdint.hpp>
+ * <netinet/in.h> or <winsock2.h> (for ntohl.. could potentially bundle this)
+ *
+ * @since 0.4.0-dev
+ */
+struct minimal_server {
+ typedef minimal_server type;
+
+ // Concurrency policy
+ typedef websocketpp::concurrency::none concurrency_type;
+
+ // HTTP Parser Policies
+ typedef http::parser::request request_type;
+ typedef http::parser::response response_type;
+
+ // Message Policies
+ typedef message_buffer::message<message_buffer::alloc::con_msg_manager>
+ message_type;
+ typedef message_buffer::alloc::con_msg_manager<message_type>
+ con_msg_manager_type;
+ typedef message_buffer::alloc::endpoint_msg_manager<con_msg_manager_type>
+ endpoint_msg_manager_type;
+
+ /// Logging policies
+ typedef websocketpp::log::stub elog_type;
+ typedef websocketpp::log::stub alog_type;
+
+ /// RNG policies
+ typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ struct transport_config {
+ typedef type::concurrency_type concurrency_type;
+ typedef type::elog_type elog_type;
+ typedef type::alog_type alog_type;
+ typedef type::request_type request_type;
+ typedef type::response_type response_type;
+
+ /// Controls compile time enabling/disabling of thread syncronization
+ /// code Disabling can provide a minor performance improvement to single
+ /// threaded applications
+ static bool const enable_multithreading = true;
+
+ /// Default timer values (in ms)
+
+ /// Length of time to wait for socket pre-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use
+ */
+ static const long timeout_socket_pre_init = 5000;
+
+ /// Length of time to wait before a proxy handshake is aborted
+ static const long timeout_proxy = 5000;
+
+ /// Length of time to wait for socket post-initialization
+ /**
+ * Exactly what this includes depends on the socket policy in use.
+ * Often this means the TLS handshake
+ */
+ static const long timeout_socket_post_init = 5000;
+
+ /// Length of time to wait for dns resolution
+ static const long timeout_dns_resolve = 5000;
+
+ /// Length of time to wait for TCP connect
+ static const long timeout_connect = 5000;
+
+ /// Length of time to wait for socket shutdown
+ static const long timeout_socket_shutdown = 5000;
+ };
+
+ /// Transport Endpoint Component
+ typedef websocketpp::transport::stub::endpoint<transport_config>
+ transport_type;
+
+ /// User overridable Endpoint base class
+ typedef websocketpp::endpoint_base endpoint_base;
+ /// User overridable Connection base class
+ typedef websocketpp::connection_base connection_base;
+
+ /// Default timer values (in ms)
+
+ /// Length of time before an opening handshake is aborted
+ static const long timeout_open_handshake = 5000;
+ /// Length of time before a closing handshake is aborted
+ static const long timeout_close_handshake = 5000;
+ /// Length of time to wait for a pong after a ping
+ static const long timeout_pong = 5000;
+
+ /// WebSocket Protocol version to use as a client
+ /**
+ * What version of the WebSocket Protocol to use for outgoing client
+ * connections. Setting this to a value other than 13 (RFC6455) is not
+ * recommended.
+ */
+ static const int client_version = 13; // RFC6455
+
+ /// Default static error logging channels
+ /**
+ * Which error logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level errors
+ */
+ static const websocketpp::log::level elog_level =
+ websocketpp::log::elevel::none;
+
+ /// Default static access logging channels
+ /**
+ * Which access logging channels to enable at compile time. Channels not
+ * enabled here will be unable to be selected by programs using the library.
+ * This option gives an optimizing compiler the ability to remove entirely
+ * code to test whether or not to print out log messages on a certain
+ * channel
+ *
+ * Default is all except for development/debug level access messages
+ */
+ static const websocketpp::log::level alog_level =
+ websocketpp::log::alevel::none;
+
+ ///
+ static const size_t connection_read_buffer_size = 16384;
+
+ /// Drop connections immediately on protocol error.
+ /**
+ * Drop connections on protocol error rather than sending a close frame.
+ * Off by default. This may result in legit messages near the error being
+ * dropped as well. It may free up resources otherwise spent dealing with
+ * misbehaving clients.
+ */
+ static const bool drop_on_protocol_error = false;
+
+ /// Suppresses the return of detailed connection close information
+ /**
+ * Silence close suppresses the return of detailed connection close
+ * information during the closing handshake. This information is useful
+ * for debugging and presenting useful errors to end users but may be
+ * undesirable for security reasons in some production environments.
+ * Close reasons could be used by an attacker to confirm that the endpoint
+ * is out of resources or be used to identify the WebSocket implementation
+ * in use.
+ *
+ * Note: this will suppress *all* close codes, including those explicitly
+ * sent by local applications.
+ */
+ static const bool silent_close = false;
+
+ /// Default maximum message size
+ /**
+ * Default value for the processor's maximum message size. Maximum message size
+ * determines the point at which the library will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.4.0-alpha1
+ */
+ static const size_t max_message_size = 32000000;
+
+ /// Default maximum http body size
+ /**
+ * Default value for the http parser's maximum body size. Maximum body size
+ * determines the point at which the library will abort reading an HTTP
+ * connection with the 413/request entity too large error.
+ *
+ * The default is 32MB
+ *
+ * @since 0.5.0
+ */
+ static const size_t max_http_body_size = 32000000;
+
+ /// Global flag for enabling/disabling extensions
+ static const bool enable_extensions = true;
+
+ /// Extension specific settings:
+
+ /// permessage_compress extension
+ struct permessage_deflate_config {
+ typedef core::request_type request_type;
+
+ /// If the remote endpoint requests that we reset the compression
+ /// context after each message should we honor the request?
+ static const bool allow_disabling_context_takeover = true;
+
+ /// If the remote endpoint requests that we reduce the size of the
+ /// LZ77 sliding window size this is the lowest value that will be
+ /// allowed. Values range from 8 to 15. A value of 8 means we will
+ /// allow any possible window size. A value of 15 means do not allow
+ /// negotiation of the window size (ie require the default).
+ static const uint8_t minimum_outgoing_window_bits = 8;
+ };
+
+ typedef websocketpp::extensions::permessage_deflate::disabled
+ <permessage_deflate_config> permessage_deflate_type;
+
+ /// Autonegotiate permessage-deflate
+ /**
+ * Automatically enables the permessage-deflate extension.
+ *
+ * For clients this results in a permessage-deflate extension request being
+ * sent with every request rather than requiring it to be requested manually
+ *
+ * For servers this results in accepting the first set of extension settings
+ * requested by the client that we understand being used. The alternative is
+ * requiring the extension to be manually negotiated in `validate`. With
+ * auto-negotiate on, you may still override the auto-negotiate manually if
+ * needed.
+ */
+ //static const bool autonegotiate_compression = false;
+};
+
+} // namespace config
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONFIG_MINIMAL_HPP
diff --git a/websocketpp/connection.hpp b/websocketpp/connection.hpp
new file mode 100644
index 00000000..3bbbbb31
--- /dev/null
+++ b/websocketpp/connection.hpp
@@ -0,0 +1,1651 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONNECTION_HPP
+#define WEBSOCKETPP_CONNECTION_HPP
+
+#include <websocketpp/close.hpp>
+#include <websocketpp/error.hpp>
+#include <websocketpp/frame.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+#include <websocketpp/processors/processor.hpp>
+#include <websocketpp/transport/base/connection.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/functional.hpp>
+
+#include <queue>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+
+/// The type and function signature of an open handler
+/**
+ * The open handler is called once for every successful WebSocket connection
+ * attempt. Either the fail handler or the open handler will be called for each
+ * WebSocket connection attempt. HTTP Connections that did not attempt to
+ * upgrade the connection to the WebSocket protocol will trigger the http
+ * handler instead of fail/open.
+ */
+typedef lib::function<void(connection_hdl)> open_handler;
+
+/// The type and function signature of a close handler
+/**
+ * The close handler is called once for every successfully established
+ * connection after it is no longer capable of sending or receiving new messages
+ *
+ * The close handler will be called exactly once for every connection for which
+ * the open handler was called.
+ */
+typedef lib::function<void(connection_hdl)> close_handler;
+
+/// The type and function signature of a fail handler
+/**
+ * The fail handler is called once for every unsuccessful WebSocket connection
+ * attempt. Either the fail handler or the open handler will be called for each
+ * WebSocket connection attempt. HTTP Connections that did not attempt to
+ * upgrade the connection to the WebSocket protocol will trigger the http
+ * handler instead of fail/open.
+ */
+typedef lib::function<void(connection_hdl)> fail_handler;
+
+/// The type and function signature of an interrupt handler
+/**
+ * The interrupt handler is called when a connection receives an interrupt
+ * request from the application. Interrupts allow the application to trigger a
+ * handler to be run in the absense of a WebSocket level handler trigger (like
+ * a new message).
+ *
+ * This is typically used by another application thread to schedule some tasks
+ * that can only be run from within the handler chain for thread safety reasons.
+ */
+typedef lib::function<void(connection_hdl)> interrupt_handler;
+
+/// The type and function signature of a ping handler
+/**
+ * The ping handler is called when the connection receives a WebSocket ping
+ * control frame. The string argument contains the ping payload. The payload is
+ * a binary string up to 126 bytes in length. The ping handler returns a bool,
+ * true if a pong response should be sent, false if the pong response should be
+ * suppressed.
+ */
+typedef lib::function<bool(connection_hdl,std::string)> ping_handler;
+
+/// The type and function signature of a pong handler
+/**
+ * The pong handler is called when the connection receives a WebSocket pong
+ * control frame. The string argument contains the pong payload. The payload is
+ * a binary string up to 126 bytes in length.
+ */
+typedef lib::function<void(connection_hdl,std::string)> pong_handler;
+
+/// The type and function signature of a pong timeout handler
+/**
+ * The pong timeout handler is called when a ping goes unanswered by a pong for
+ * longer than the locally specified timeout period.
+ */
+typedef lib::function<void(connection_hdl,std::string)> pong_timeout_handler;
+
+/// The type and function signature of a validate handler
+/**
+ * The validate handler is called after a WebSocket handshake has been received
+ * and processed but before it has been accepted. This gives the application a
+ * chance to implement connection details specific policies for accepting
+ * connections and the ability to negotiate extensions and subprotocols.
+ *
+ * The validate handler return value indicates whether or not the connection
+ * should be accepted. Additional methods may be called during the function to
+ * set response headers, set HTTP return/error codes, etc.
+ */
+typedef lib::function<bool(connection_hdl)> validate_handler;
+
+/// The type and function signature of a http handler
+/**
+ * The http handler is called when an HTTP connection is made that does not
+ * attempt to upgrade the connection to the WebSocket protocol. This allows
+ * WebSocket++ servers to respond to these requests with regular HTTP responses.
+ *
+ * This can be used to deliver error pages & dashboards and to deliver static
+ * files such as the base HTML & JavaScript for an otherwise single page
+ * WebSocket application.
+ *
+ * Note: WebSocket++ is designed to be a high performance WebSocket server. It
+ * is not tuned to provide a full featured, high performance, HTTP web server
+ * solution. The HTTP handler is appropriate only for low volume HTTP traffic.
+ * If you expect to serve high volumes of HTTP traffic a dedicated HTTP web
+ * server is strongly recommended.
+ *
+ * The default HTTP handler will return a 426 Upgrade Required error. Custom
+ * handlers may override the response status code to deliver any type of
+ * response.
+ */
+typedef lib::function<void(connection_hdl)> http_handler;
+
+//
+typedef lib::function<void(lib::error_code const & ec, size_t bytes_transferred)> read_handler;
+typedef lib::function<void(lib::error_code const & ec)> write_frame_handler;
+
+// constants related to the default WebSocket protocol versions available
+#ifdef _WEBSOCKETPP_INITIALIZER_LISTS_ // simplified C++11 version
+ /// Container that stores the list of protocol versions supported
+ /**
+ * @todo Move this to configs to allow compile/runtime disabling or enabling
+ * of protocol versions
+ */
+ static std::vector<int> const versions_supported = {0,7,8,13};
+#else
+ /// Helper array to get around lack of initializer lists pre C++11
+ static int const helper[] = {0,7,8,13};
+ /// Container that stores the list of protocol versions supported
+ /**
+ * @todo Move this to configs to allow compile/runtime disabling or enabling
+ * of protocol versions
+ */
+ static std::vector<int> const versions_supported(helper,helper+4);
+#endif
+
+namespace session {
+namespace state {
+ // externally visible session state (states based on the RFC)
+ enum value {
+ connecting = 0,
+ open = 1,
+ closing = 2,
+ closed = 3
+ };
+} // namespace state
+
+
+namespace fail {
+namespace status {
+ enum value {
+ GOOD = 0, // no failure yet!
+ SYSTEM = 1, // system call returned error, check that code
+ WEBSOCKET = 2, // websocket close codes contain error
+ UNKNOWN = 3, // No failure information is available
+ TIMEOUT_TLS = 4, // TLS handshake timed out
+ TIMEOUT_WS = 5 // WS handshake timed out
+ };
+} // namespace status
+} // namespace fail
+
+namespace internal_state {
+ // More granular internal states. These are used for multi-threaded
+ // connection synchronization and preventing values that are not yet or no
+ // longer available from being used.
+
+ enum value {
+ USER_INIT = 0,
+ TRANSPORT_INIT = 1,
+ READ_HTTP_REQUEST = 2,
+ WRITE_HTTP_REQUEST = 3,
+ READ_HTTP_RESPONSE = 4,
+ WRITE_HTTP_RESPONSE = 5,
+ PROCESS_HTTP_REQUEST = 6,
+ PROCESS_CONNECTION = 7
+ };
+} // namespace internal_state
+
+
+namespace http_state {
+ // states to keep track of the progress of http connections
+
+ enum value {
+ init = 0,
+ deferred = 1,
+ headers_written = 2,
+ body_written = 3,
+ closed = 4
+ };
+} // namespace http_state
+
+} // namespace session
+
+/// Represents an individual WebSocket connection
+template <typename config>
+class connection
+ : public config::transport_type::transport_con_type
+ , public config::connection_base
+{
+public:
+ /// Type of this connection
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection
+ typedef lib::shared_ptr<type> ptr;
+ /// Type of a weak pointer to this connection
+ typedef lib::weak_ptr<type> weak_ptr;
+
+ /// Type of the concurrency component of this connection
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of the access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of the error logging policy
+ typedef typename config::elog_type elog_type;
+
+ /// Type of the transport component of this connection
+ typedef typename config::transport_type::transport_con_type
+ transport_con_type;
+ /// Type of a shared pointer to the transport component of this connection
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ typedef lib::function<void(ptr)> termination_handler;
+
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+
+ typedef typename config::message_type message_type;
+ typedef typename message_type::ptr message_ptr;
+
+ typedef typename config::con_msg_manager_type con_msg_manager_type;
+ typedef typename con_msg_manager_type::ptr con_msg_manager_ptr;
+
+ /// Type of RNG
+ typedef typename config::rng_type rng_type;
+
+ typedef processor::processor<config> processor_type;
+ typedef lib::shared_ptr<processor_type> processor_ptr;
+
+ // Message handler (needs to know message type)
+ typedef lib::function<void(connection_hdl,message_ptr)> message_handler;
+
+ /// Type of a pointer to a transport timer handle
+ typedef typename transport_con_type::timer_ptr timer_ptr;
+
+ // Misc Convenience Types
+ typedef session::internal_state::value istate_type;
+
+private:
+ enum terminate_status {
+ failed = 1,
+ closed,
+ unknown
+ };
+public:
+
+ explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
+ elog_type& elog, rng_type & rng)
+ : transport_con_type(p_is_server, alog, elog)
+ , m_handle_read_frame(lib::bind(
+ &type::handle_read_frame,
+ this,
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ ))
+ , m_write_frame_handler(lib::bind(
+ &type::handle_write_frame,
+ this,
+ lib::placeholders::_1
+ ))
+ , m_user_agent(ua)
+ , m_open_handshake_timeout_dur(config::timeout_open_handshake)
+ , m_close_handshake_timeout_dur(config::timeout_close_handshake)
+ , m_pong_timeout_dur(config::timeout_pong)
+ , m_max_message_size(config::max_message_size)
+ , m_state(session::state::connecting)
+ , m_internal_state(session::internal_state::USER_INIT)
+ , m_msg_manager(new con_msg_manager_type())
+ , m_send_buffer_size(0)
+ , m_write_flag(false)
+ , m_read_flag(true)
+ , m_is_server(p_is_server)
+ , m_alog(alog)
+ , m_elog(elog)
+ , m_rng(rng)
+ , m_local_close_code(close::status::abnormal_close)
+ , m_remote_close_code(close::status::abnormal_close)
+ , m_is_http(false)
+ , m_http_state(session::http_state::init)
+ , m_was_clean(false)
+ {
+ m_alog.write(log::alevel::devel,"connection constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return lib::static_pointer_cast<type>(transport_con_type::get_shared());
+ }
+
+ ///////////////////////////
+ // Set Handler Callbacks //
+ ///////////////////////////
+
+ /// Set open handler
+ /**
+ * The open handler is called after the WebSocket handshake is complete and
+ * the connection is considered OPEN.
+ *
+ * @param h The new open_handler
+ */
+ void set_open_handler(open_handler h) {
+ m_open_handler = h;
+ }
+
+ /// Set close handler
+ /**
+ * The close handler is called immediately after the connection is closed.
+ *
+ * @param h The new close_handler
+ */
+ void set_close_handler(close_handler h) {
+ m_close_handler = h;
+ }
+
+ /// Set fail handler
+ /**
+ * The fail handler is called whenever the connection fails while the
+ * handshake is bring processed.
+ *
+ * @param h The new fail_handler
+ */
+ void set_fail_handler(fail_handler h) {
+ m_fail_handler = h;
+ }
+
+ /// Set ping handler
+ /**
+ * The ping handler is called whenever the connection receives a ping
+ * control frame. The ping payload is included.
+ *
+ * The ping handler's return time controls whether or not a pong is
+ * sent in response to this ping. Returning false will suppress the
+ * return pong. If no ping handler is set a pong will be sent.
+ *
+ * @param h The new ping_handler
+ */
+ void set_ping_handler(ping_handler h) {
+ m_ping_handler = h;
+ }
+
+ /// Set pong handler
+ /**
+ * The pong handler is called whenever the connection receives a pong
+ * control frame. The pong payload is included.
+ *
+ * @param h The new pong_handler
+ */
+ void set_pong_handler(pong_handler h) {
+ m_pong_handler = h;
+ }
+
+ /// Set pong timeout handler
+ /**
+ * If the transport component being used supports timers, the pong timeout
+ * handler is called whenever a pong control frame is not received with the
+ * configured timeout period after the application sends a ping.
+ *
+ * The config setting `timeout_pong` controls the length of the timeout
+ * period. It is specified in milliseconds.
+ *
+ * This can be used to probe the health of the remote endpoint's WebSocket
+ * implementation. This does not guarantee that the remote application
+ * itself is still healthy but can be a useful diagnostic.
+ *
+ * Note: receipt of this callback doesn't mean the pong will never come.
+ * This functionality will not suppress delivery of the pong in question
+ * should it arrive after the timeout.
+ *
+ * @param h The new pong_timeout_handler
+ */
+ void set_pong_timeout_handler(pong_timeout_handler h) {
+ m_pong_timeout_handler = h;
+ }
+
+ /// Set interrupt handler
+ /**
+ * The interrupt handler is called whenever the connection is manually
+ * interrupted by the application.
+ *
+ * @param h The new interrupt_handler
+ */
+ void set_interrupt_handler(interrupt_handler h) {
+ m_interrupt_handler = h;
+ }
+
+ /// Set http handler
+ /**
+ * The http handler is called after an HTTP request other than a WebSocket
+ * upgrade request is received. It allows a WebSocket++ server to respond
+ * to regular HTTP requests on the same port as it processes WebSocket
+ * connections. This can be useful for hosting error messages, flash
+ * policy files, status pages, and other simple HTTP responses. It is not
+ * intended to be used as a primary web server.
+ *
+ * @param h The new http_handler
+ */
+ void set_http_handler(http_handler h) {
+ m_http_handler = h;
+ }
+
+ /// Set validate handler
+ /**
+ * The validate handler is called after a WebSocket handshake has been
+ * parsed but before a response is returned. It provides the application
+ * a chance to examine the request and determine whether or not it wants
+ * to accept the connection.
+ *
+ * Returning false from the validate handler will reject the connection.
+ * If no validate handler is present, all connections will be allowed.
+ *
+ * @param h The new validate_handler
+ */
+ void set_validate_handler(validate_handler h) {
+ m_validate_handler = h;
+ }
+
+ /// Set message handler
+ /**
+ * The message handler is called after a new message has been received.
+ *
+ * @param h The new message_handler
+ */
+ void set_message_handler(message_handler h) {
+ m_message_handler = h;
+ }
+
+ //////////////////////////////////////////
+ // Connection timeouts and other limits //
+ //////////////////////////////////////////
+
+ /// Set open handshake timeout
+ /**
+ * Sets the length of time the library will wait after an opening handshake
+ * has been initiated before cancelling it. This can be used to prevent
+ * excessive wait times for outgoing clients or excessive resource usage
+ * from broken clients or DoS attacks on servers.
+ *
+ * Connections that time out will have their fail handlers called with the
+ * open_handshake_timeout error code.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_open_handshake'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the open handshake timeout in ms
+ */
+ void set_open_handshake_timeout(long dur) {
+ m_open_handshake_timeout_dur = dur;
+ }
+
+ /// Set close handshake timeout
+ /**
+ * Sets the length of time the library will wait after a closing handshake
+ * has been initiated before cancelling it. This can be used to prevent
+ * excessive wait times for outgoing clients or excessive resource usage
+ * from broken clients or DoS attacks on servers.
+ *
+ * Connections that time out will have their close handlers called with the
+ * close_handshake_timeout error code.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_close_handshake'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the close handshake timeout in ms
+ */
+ void set_close_handshake_timeout(long dur) {
+ m_close_handshake_timeout_dur = dur;
+ }
+
+ /// Set pong timeout
+ /**
+ * Sets the length of time the library will wait for a pong response to a
+ * ping. This can be used as a keepalive or to detect broken connections.
+ *
+ * Pong responses that time out will have the pong timeout handler called.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_pong'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the pong timeout in ms
+ */
+ void set_pong_timeout(long dur) {
+ m_pong_timeout_dur = dur;
+ }
+
+ /// Get maximum message size
+ /**
+ * Get maximum message size. Maximum message size determines the point at
+ * which the connection will fail with the message_too_big protocol error.
+ *
+ * The default is set by the endpoint that creates the connection.
+ *
+ * @since 0.3.0
+ */
+ size_t get_max_message_size() const {
+ return m_max_message_size;
+ }
+
+ /// Set maximum message size
+ /**
+ * Set maximum message size. Maximum message size determines the point at
+ * which the connection will fail with the message_too_big protocol error.
+ * This value may be changed during the connection.
+ *
+ * The default is set by the endpoint that creates the connection.
+ *
+ * @since 0.3.0
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_message_size(size_t new_value) {
+ m_max_message_size = new_value;
+ if (m_processor) {
+ m_processor->set_max_message_size(new_value);
+ }
+ }
+
+ /// Get maximum HTTP message body size
+ /**
+ * Get maximum HTTP message body size. Maximum message body size determines
+ * the point at which the connection will stop reading an HTTP request whose
+ * body is too large.
+ *
+ * The default is set by the endpoint that creates the connection.
+ *
+ * @since 0.5.0
+ *
+ * @return The maximum HTTP message body size
+ */
+ size_t get_max_http_body_size() const {
+ return m_request.get_max_body_size();
+ }
+
+ /// Set maximum HTTP message body size
+ /**
+ * Set maximum HTTP message body size. Maximum message body size determines
+ * the point at which the connection will stop reading an HTTP request whose
+ * body is too large.
+ *
+ * The default is set by the endpoint that creates the connection.
+ *
+ * @since 0.5.0
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_http_body_size(size_t new_value) {
+ m_request.set_max_body_size(new_value);
+ }
+
+ //////////////////////////////////
+ // Uncategorized public methods //
+ //////////////////////////////////
+
+ /// Get the size of the outgoing write buffer (in payload bytes)
+ /**
+ * Retrieves the number of bytes in the outgoing write buffer that have not
+ * already been dispatched to the transport layer. This represents the bytes
+ * that are presently cancelable without uncleanly ending the websocket
+ * connection
+ *
+ * This method invokes the m_write_lock mutex
+ *
+ * @return The current number of bytes in the outgoing send buffer.
+ */
+ size_t get_buffered_amount() const;
+
+ /// Get the size of the outgoing write buffer (in payload bytes)
+ /**
+ * @deprecated use `get_buffered_amount` instead
+ */
+ size_t buffered_amount() const {
+ return get_buffered_amount();
+ }
+
+ ////////////////////
+ // Action Methods //
+ ////////////////////
+
+ /// Create a message and then add it to the outgoing send queue
+ /**
+ * Convenience method to send a message given a payload string and
+ * optionally an opcode. Default opcode is utf8 text.
+ *
+ * This method locks the m_write_lock mutex
+ *
+ * @param payload The payload string to generated the message with
+ *
+ * @param op The opcode to generated the message with. Default is
+ * frame::opcode::text
+ */
+ lib::error_code send(std::string const & payload, frame::opcode::value op =
+ frame::opcode::text);
+
+ /// Send a message (raw array overload)
+ /**
+ * Convenience method to send a message given a raw array and optionally an
+ * opcode. Default opcode is binary.
+ *
+ * This method locks the m_write_lock mutex
+ *
+ * @param payload A pointer to the array containing the bytes to send.
+ *
+ * @param len Length of the array.
+ *
+ * @param op The opcode to generated the message with. Default is
+ * frame::opcode::binary
+ */
+ lib::error_code send(void const * payload, size_t len, frame::opcode::value
+ op = frame::opcode::binary);
+
+ /// Add a message to the outgoing send queue
+ /**
+ * If presented with a prepared message it is added without validation or
+ * framing. If presented with an unprepared message it is validated, framed,
+ * and then added
+ *
+ * Errors are returned via an exception
+ * \todo make exception system_error rather than error_code
+ *
+ * This method invokes the m_write_lock mutex
+ *
+ * @param msg A message_ptr to the message to send.
+ */
+ lib::error_code send(message_ptr msg);
+
+ /// Asyncronously invoke handler::on_inturrupt
+ /**
+ * Signals to the connection to asyncronously invoke the on_inturrupt
+ * callback for this connection's handler once it is safe to do so.
+ *
+ * When the on_inturrupt handler callback is called it will be from
+ * within the transport event loop with all the thread safety features
+ * guaranteed by the transport to regular handlers
+ *
+ * Multiple inturrupt signals can be active at once on the same connection
+ *
+ * @return An error code
+ */
+ lib::error_code interrupt();
+
+ /// Transport inturrupt callback
+ void handle_interrupt();
+
+ /// Pause reading of new data
+ /**
+ * Signals to the connection to halt reading of new data. While reading is paused,
+ * the connection will stop reading from its associated socket. In turn this will
+ * result in TCP based flow control kicking in and slowing data flow from the remote
+ * endpoint.
+ *
+ * This is useful for applications that push new requests to a queue to be processed
+ * by another thread and need a way to signal when their request queue is full without
+ * blocking the network processing thread.
+ *
+ * Use `resume_reading()` to resume.
+ *
+ * If supported by the transport this is done asynchronously. As such reading may not
+ * stop until the current read operation completes. Typically you can expect to
+ * receive no more bytes after initiating a read pause than the size of the read
+ * buffer.
+ *
+ * If reading is paused for this connection already nothing is changed.
+ */
+ lib::error_code pause_reading();
+
+ /// Pause reading callback
+ void handle_pause_reading();
+
+ /// Resume reading of new data
+ /**
+ * Signals to the connection to resume reading of new data after it was paused by
+ * `pause_reading()`.
+ *
+ * If reading is not paused for this connection already nothing is changed.
+ */
+ lib::error_code resume_reading();
+
+ /// Resume reading callback
+ void handle_resume_reading();
+
+ /// Send a ping
+ /**
+ * Initiates a ping with the given payload/
+ *
+ * There is no feedback directly from ping except in cases of immediately
+ * detectable errors. Feedback will be provided via on_pong or
+ * on_pong_timeout callbacks.
+ *
+ * Ping locks the m_write_lock mutex
+ *
+ * @param payload Payload to be used for the ping
+ */
+ void ping(std::string const & payload);
+
+ /// exception free variant of ping
+ void ping(std::string const & payload, lib::error_code & ec);
+
+ /// Utility method that gets called back when the ping timer expires
+ void handle_pong_timeout(std::string payload, lib::error_code const & ec);
+
+ /// Send a pong
+ /**
+ * Initiates a pong with the given payload.
+ *
+ * There is no feedback from a pong once sent.
+ *
+ * Pong locks the m_write_lock mutex
+ *
+ * @param payload Payload to be used for the pong
+ */
+ void pong(std::string const & payload);
+
+ /// exception free variant of pong
+ void pong(std::string const & payload, lib::error_code & ec);
+
+ /// Close the connection
+ /**
+ * Initiates the close handshake process.
+ *
+ * If close returns successfully the connection will be in the closing
+ * state and no additional messages may be sent. All messages sent prior
+ * to calling close will be written out before the connection is closed.
+ *
+ * If no reason is specified none will be sent. If no code is specified
+ * then no code will be sent.
+ *
+ * The handler's on_close callback will be called once the close handshake
+ * is complete.
+ *
+ * Reasons will be automatically truncated to the maximum length (123 bytes)
+ * if necessary.
+ *
+ * @param code The close code to send
+ * @param reason The close reason to send
+ */
+ void close(close::status::value const code, std::string const & reason);
+
+ /// exception free variant of close
+ void close(close::status::value const code, std::string const & reason,
+ lib::error_code & ec);
+
+ ////////////////////////////////////////////////
+ // Pass-through access to the uri information //
+ ////////////////////////////////////////////////
+
+ /// Returns the secure flag from the connection URI
+ /**
+ * This value is available after the HTTP request has been fully read and
+ * may be called from any thread.
+ *
+ * @return Whether or not the connection URI is flagged secure.
+ */
+ bool get_secure() const;
+
+ /// Returns the host component of the connection URI
+ /**
+ * This value is available after the HTTP request has been fully read and
+ * may be called from any thread.
+ *
+ * @return The host component of the connection URI
+ */
+ std::string const & get_host() const;
+
+ /// Returns the resource component of the connection URI
+ /**
+ * This value is available after the HTTP request has been fully read and
+ * may be called from any thread.
+ *
+ * @return The resource component of the connection URI
+ */
+ std::string const & get_resource() const;
+
+ /// Returns the port component of the connection URI
+ /**
+ * This value is available after the HTTP request has been fully read and
+ * may be called from any thread.
+ *
+ * @return The port component of the connection URI
+ */
+ uint16_t get_port() const;
+
+ /// Gets the connection URI
+ /**
+ * This should really only be called by internal library methods unless you
+ * really know what you are doing.
+ *
+ * @return A pointer to the connection's URI
+ */
+ uri_ptr get_uri() const;
+
+ /// Sets the connection URI
+ /**
+ * This should really only be called by internal library methods unless you
+ * really know what you are doing.
+ *
+ * @param uri The new URI to set
+ */
+ void set_uri(uri_ptr uri);
+
+ /////////////////////////////
+ // Subprotocol negotiation //
+ /////////////////////////////
+
+ /// Gets the negotated subprotocol
+ /**
+ * Retrieves the subprotocol that was negotiated during the handshake. This
+ * method is valid in the open handler and later.
+ *
+ * @return The negotiated subprotocol
+ */
+ std::string const & get_subprotocol() const;
+
+ /// Gets all of the subprotocols requested by the client
+ /**
+ * Retrieves the subprotocols that were requested during the handshake. This
+ * method is valid in the validate handler and later.
+ *
+ * @return A vector of the requested subprotocol
+ */
+ std::vector<std::string> const & get_requested_subprotocols() const;
+
+ /// Adds the given subprotocol string to the request list (exception free)
+ /**
+ * Adds a subprotocol to the list to send with the opening handshake. This
+ * may be called multiple times to request more than one. If the server
+ * supports one of these, it may choose one. If so, it will return it
+ * in it's handshake reponse and the value will be available via
+ * get_subprotocol(). Subprotocol requests should be added in order of
+ * preference.
+ *
+ * @param request The subprotocol to request
+ * @param ec A reference to an error code that will be filled in the case of
+ * errors
+ */
+ void add_subprotocol(std::string const & request, lib::error_code & ec);
+
+ /// Adds the given subprotocol string to the request list
+ /**
+ * Adds a subprotocol to the list to send with the opening handshake. This
+ * may be called multiple times to request more than one. If the server
+ * supports one of these, it may choose one. If so, it will return it
+ * in it's handshake reponse and the value will be available via
+ * get_subprotocol(). Subprotocol requests should be added in order of
+ * preference.
+ *
+ * @param request The subprotocol to request
+ */
+ void add_subprotocol(std::string const & request);
+
+ /// Select a subprotocol to use (exception free)
+ /**
+ * Indicates which subprotocol should be used for this connection. Valid
+ * only during the validate handler callback. Subprotocol selected must have
+ * been requested by the client. Consult get_requested_subprotocols() for a
+ * list of valid subprotocols.
+ *
+ * This member function is valid on server endpoints/connections only
+ *
+ * @param value The subprotocol to select
+ * @param ec A reference to an error code that will be filled in the case of
+ * errors
+ */
+ void select_subprotocol(std::string const & value, lib::error_code & ec);
+
+ /// Select a subprotocol to use
+ /**
+ * Indicates which subprotocol should be used for this connection. Valid
+ * only during the validate handler callback. Subprotocol selected must have
+ * been requested by the client. Consult get_requested_subprotocols() for a
+ * list of valid subprotocols.
+ *
+ * This member function is valid on server endpoints/connections only
+ *
+ * @param value The subprotocol to select
+ */
+ void select_subprotocol(std::string const & value);
+
+ /////////////////////////////////////////////////////////////
+ // Pass-through access to the request and response objects //
+ /////////////////////////////////////////////////////////////
+
+ /// Retrieve a request header
+ /**
+ * Retrieve the value of a header from the handshake HTTP request.
+ *
+ * @param key Name of the header to get
+ * @return The value of the header
+ */
+ std::string const & get_request_header(std::string const & key) const;
+
+ /// Retrieve a request body
+ /**
+ * Retrieve the value of the request body. This value is typically used with
+ * PUT and POST requests to upload files or other data. Only HTTP
+ * connections will ever have bodies. WebSocket connection's will always
+ * have blank bodies.
+ *
+ * @return The value of the request body.
+ */
+ std::string const & get_request_body() const;
+
+ /// Retrieve a response header
+ /**
+ * Retrieve the value of a header from the handshake HTTP request.
+ *
+ * @param key Name of the header to get
+ * @return The value of the header
+ */
+ std::string const & get_response_header(std::string const & key) const;
+
+ /// Get response HTTP status code
+ /**
+ * Gets the response status code
+ *
+ * @since 0.7.0
+ *
+ * @return The response status code sent
+ */
+ http::status_code::value get_response_code() const {
+ return m_response.get_status_code();
+ }
+
+ /// Get response HTTP status message
+ /**
+ * Gets the response status message
+ *
+ * @since 0.7.0
+ *
+ * @return The response status message sent
+ */
+ std::string const & get_response_msg() const {
+ return m_response.get_status_msg();
+ }
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code to `code` and looks up the corresponding
+ * message for standard codes. Non-standard codes will be entered as Unknown
+ * use set_status(status_code::value,std::string) overload to set both
+ * values explicitly.
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ * @see websocketpp::http::response::set_status
+ */
+ void set_status(http::status_code::value code);
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code and message to independent custom values.
+ * use set_status(status_code::value) to set the code and have the standard
+ * message be automatically set.
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ * @see websocketpp::http::response::set_status
+ */
+ void set_status(http::status_code::value code, std::string const & msg);
+
+ /// Set response body content
+ /**
+ * Set the body content of the HTTP response to the parameter string. Note
+ * set_body will also set the Content-Length HTTP header to the appropriate
+ * value. If you want the Content-Length header to be something else set it
+ * to something else after calling set_body
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks.
+ *
+ * @param value String data to include as the body content.
+ * @see websocketpp::http::response::set_body
+ */
+ void set_body(std::string const & value);
+
+ /// Append a header
+ /**
+ * If a header with this name already exists the value will be appended to
+ * the existing header to form a comma separated list of values. Use
+ * `connection::replace_header` to overwrite existing values.
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks, or to a client connection before connect has been called.
+ *
+ * @param key Name of the header to set
+ * @param val Value to add
+ * @see replace_header
+ * @see websocketpp::http::parser::append_header
+ */
+ void append_header(std::string const & key, std::string const & val);
+
+ /// Replace a header
+ /**
+ * If a header with this name already exists the old value will be replaced
+ * Use `connection::append_header` to append to a list of existing values.
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks, or to a client connection before connect has been called.
+ *
+ * @param key Name of the header to set
+ * @param val Value to set
+ * @see append_header
+ * @see websocketpp::http::parser::replace_header
+ */
+ void replace_header(std::string const & key, std::string const & val);
+
+ /// Remove a header
+ /**
+ * Removes a header from the response.
+ *
+ * This member function is valid only from the http() and validate() handler
+ * callbacks, or to a client connection before connect has been called.
+ *
+ * @param key The name of the header to remove
+ * @see websocketpp::http::parser::remove_header
+ */
+ void remove_header(std::string const & key);
+
+ /// Get request object
+ /**
+ * Direct access to request object. This can be used to call methods of the
+ * request object that are not part of the standard request API that
+ * connection wraps.
+ *
+ * Note use of this method involves using behavior specific to the
+ * configured HTTP policy. Such behavior may not work with alternate HTTP
+ * policies.
+ *
+ * @since 0.3.0-alpha3
+ *
+ * @return A const reference to the raw request object
+ */
+ request_type const & get_request() const {
+ return m_request;
+ }
+
+ /// Get response object
+ /**
+ * Direct access to the HTTP response sent or received as a part of the
+ * opening handshake. This can be used to call methods of the response
+ * object that are not part of the standard request API that connection
+ * wraps.
+ *
+ * Note use of this method involves using behavior specific to the
+ * configured HTTP policy. Such behavior may not work with alternate HTTP
+ * policies.
+ *
+ * @since 0.7.0
+ *
+ * @return A const reference to the raw response object
+ */
+ response_type const & get_response() const {
+ return m_response;
+ }
+
+ /// Defer HTTP Response until later (Exception free)
+ /**
+ * Used in the http handler to defer the HTTP response for this connection
+ * until later. Handshake timers will be canceled and the connection will be
+ * left open until `send_http_response` or an equivalent is called.
+ *
+ * Warning: deferred connections won't time out and as a result can tie up
+ * resources.
+ *
+ * @since 0.6.0
+ *
+ * @return A status code, zero on success, non-zero otherwise
+ */
+ lib::error_code defer_http_response();
+
+ /// Send deferred HTTP Response (exception free)
+ /**
+ * Sends an http response to an HTTP connection that was deferred. This will
+ * send a complete response including all headers, status line, and body
+ * text. The connection will be closed afterwards.
+ *
+ * @since 0.6.0
+ *
+ * @param ec A status code, zero on success, non-zero otherwise
+ */
+ void send_http_response(lib::error_code & ec);
+
+ /// Send deferred HTTP Response
+ void send_http_response();
+
+ // TODO HTTPNBIO: write_headers
+ // function that processes headers + status so far and writes it to the wire
+ // beginning the HTTP response body state. This method will ignore anything
+ // in the response body.
+
+ // TODO HTTPNBIO: write_body_message
+ // queues the specified message_buffer for async writing
+
+ // TODO HTTPNBIO: finish connection
+ //
+
+ // TODO HTTPNBIO: write_response
+ // Writes the whole response, headers + body and closes the connection
+
+
+
+ /////////////////////////////////////////////////////////////
+ // Pass-through access to the other connection information //
+ /////////////////////////////////////////////////////////////
+
+ /// Get Connection Handle
+ /**
+ * The connection handle is a token that can be shared outside the
+ * WebSocket++ core for the purposes of identifying a connection and
+ * sending it messages.
+ *
+ * @return A handle to the connection
+ */
+ connection_hdl get_handle() const {
+ return m_connection_hdl;
+ }
+
+ /// Get whether or not this connection is part of a server or client
+ /**
+ * @return whether or not the connection is attached to a server endpoint
+ */
+ bool is_server() const {
+ return m_is_server;
+ }
+
+ /// Return the same origin policy origin value from the opening request.
+ /**
+ * This value is available after the HTTP request has been fully read and
+ * may be called from any thread.
+ *
+ * @return The connection's origin value from the opening handshake.
+ */
+ std::string const & get_origin() const;
+
+ /// Return the connection state.
+ /**
+ * Values can be connecting, open, closing, and closed
+ *
+ * @return The connection's current state.
+ */
+ session::state::value get_state() const;
+
+
+ /// Get the WebSocket close code sent by this endpoint.
+ /**
+ * @return The WebSocket close code sent by this endpoint.
+ */
+ close::status::value get_local_close_code() const {
+ return m_local_close_code;
+ }
+
+ /// Get the WebSocket close reason sent by this endpoint.
+ /**
+ * @return The WebSocket close reason sent by this endpoint.
+ */
+ std::string const & get_local_close_reason() const {
+ return m_local_close_reason;
+ }
+
+ /// Get the WebSocket close code sent by the remote endpoint.
+ /**
+ * @return The WebSocket close code sent by the remote endpoint.
+ */
+ close::status::value get_remote_close_code() const {
+ return m_remote_close_code;
+ }
+
+ /// Get the WebSocket close reason sent by the remote endpoint.
+ /**
+ * @return The WebSocket close reason sent by the remote endpoint.
+ */
+ std::string const & get_remote_close_reason() const {
+ return m_remote_close_reason;
+ }
+
+ /// Get the internal error code for a closed/failed connection
+ /**
+ * Retrieves a machine readable detailed error code indicating the reason
+ * that the connection was closed or failed. Valid only after the close or
+ * fail handler is called.
+ *
+ * @return Error code indicating the reason the connection was closed or
+ * failed
+ */
+ lib::error_code get_ec() const {
+ return m_ec;
+ }
+
+ /// Get a message buffer
+ /**
+ * Warning: The API related to directly sending message buffers may change
+ * before the 1.0 release. If you plan to use it, please keep an eye on any
+ * breaking changes notifications in future release notes. Also if you have
+ * any feedback about usage and capabilities now is a great time to provide
+ * it.
+ *
+ * Message buffers are used to store message payloads and other message
+ * metadata.
+ *
+ * The size parameter is a hint only. Your final payload does not need to
+ * match it. There may be some performance benefits if the initial size
+ * guess is equal to or slightly higher than the final payload size.
+ *
+ * @param op The opcode for the new message
+ * @param size A hint to optimize the initial allocation of payload space.
+ * @return A new message buffer
+ */
+ message_ptr get_message(websocketpp::frame::opcode::value op, size_t size)
+ const
+ {
+ return m_msg_manager->get_message(op, size);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // The remaining public member functions are for internal/policy use //
+ // only. Do not call from application code unless you understand what //
+ // you are doing. //
+ ////////////////////////////////////////////////////////////////////////
+
+
+
+ void read_handshake(size_t num_bytes);
+
+ void handle_read_handshake(lib::error_code const & ec,
+ size_t bytes_transferred);
+ void handle_read_http_response(lib::error_code const & ec,
+ size_t bytes_transferred);
+
+
+ void handle_write_http_response(lib::error_code const & ec);
+ void handle_send_http_request(lib::error_code const & ec);
+
+ void handle_open_handshake_timeout(lib::error_code const & ec);
+ void handle_close_handshake_timeout(lib::error_code const & ec);
+
+ void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred);
+ void read_frame();
+
+ /// Get array of WebSocket protocol versions that this connection supports.
+ std::vector<int> const & get_supported_versions() const;
+
+ /// Sets the handler for a terminating connection. Should only be used
+ /// internally by the endpoint class.
+ void set_termination_handler(termination_handler new_handler);
+
+ void terminate(lib::error_code const & ec);
+ void handle_terminate(terminate_status tstat, lib::error_code const & ec);
+
+ /// Checks if there are frames in the send queue and if there are sends one
+ /**
+ * \todo unit tests
+ *
+ * This method locks the m_write_lock mutex
+ */
+ void write_frame();
+
+ /// Process the results of a frame write operation and start the next write
+ /**
+ * \todo unit tests
+ *
+ * This method locks the m_write_lock mutex
+ *
+ * @param terminate Whether or not to terminate the connection upon
+ * completion of this write.
+ *
+ * @param ec A status code from the transport layer, zero on success,
+ * non-zero otherwise.
+ */
+ void handle_write_frame(lib::error_code const & ec);
+// protected:
+ // This set of methods would really like to be protected, but doing so
+ // requires that the endpoint be able to friend the connection. This is
+ // allowed with C++11, but not prior versions
+
+ /// Start the connection state machine
+ void start();
+
+ /// Set Connection Handle
+ /**
+ * The connection handle is a token that can be shared outside the
+ * WebSocket++ core for the purposes of identifying a connection and
+ * sending it messages.
+ *
+ * @param hdl A connection_hdl that the connection will use to refer
+ * to itself.
+ */
+ void set_handle(connection_hdl hdl) {
+ m_connection_hdl = hdl;
+ transport_con_type::set_handle(hdl);
+ }
+protected:
+ void handle_transport_init(lib::error_code const & ec);
+
+ /// Set m_processor based on information in m_request. Set m_response
+ /// status and return an error code indicating status.
+ lib::error_code initialize_processor();
+
+ /// Perform WebSocket handshake validation of m_request using m_processor.
+ /// set m_response and return an error code indicating status.
+ lib::error_code process_handshake_request();
+private:
+
+
+ /// Completes m_response, serializes it, and sends it out on the wire.
+ void write_http_response(lib::error_code const & ec);
+
+ /// Sends an opening WebSocket connect request
+ void send_http_request();
+
+ /// Alternate path for write_http_response in error conditions
+ void write_http_response_error(lib::error_code const & ec);
+
+ /// Process control message
+ /**
+ *
+ */
+ void process_control_frame(message_ptr msg);
+
+ /// Send close acknowledgement
+ /**
+ * If no arguments are present no close code/reason will be specified.
+ *
+ * Note: the close code/reason values provided here may be overrided by
+ * other settings (such as silent close).
+ *
+ * @param code The close code to send
+ * @param reason The close reason to send
+ * @return A status code, zero on success, non-zero otherwise
+ */
+ lib::error_code send_close_ack(close::status::value code =
+ close::status::blank, std::string const & reason = std::string());
+
+ /// Send close frame
+ /**
+ * If no arguments are present no close code/reason will be specified.
+ *
+ * Note: the close code/reason values provided here may be overrided by
+ * other settings (such as silent close).
+ *
+ * The ack flag determines what to do in the case of a blank status and
+ * whether or not to terminate the TCP connection after sending it.
+ *
+ * @param code The close code to send
+ * @param reason The close reason to send
+ * @param ack Whether or not this is an acknowledgement close frame
+ * @return A status code, zero on success, non-zero otherwise
+ */
+ lib::error_code send_close_frame(close::status::value code =
+ close::status::blank, std::string const & reason = std::string(), bool ack = false,
+ bool terminal = false);
+
+ /// Get a pointer to a new WebSocket protocol processor for a given version
+ /**
+ * @param version Version number of the WebSocket protocol to get a
+ * processor for. Negative values indicate invalid/unknown versions and will
+ * always return a null ptr
+ *
+ * @return A shared_ptr to a new instance of the appropriate processor or a
+ * null ptr if there is no installed processor that matches the version
+ * number.
+ */
+ processor_ptr get_processor(int version) const;
+
+ /// Add a message to the write queue
+ /**
+ * Adds a message to the write queue and updates any associated shared state
+ *
+ * Must be called while holding m_write_lock
+ *
+ * @todo unit tests
+ *
+ * @param msg The message to push
+ */
+ void write_push(message_ptr msg);
+
+ /// Pop a message from the write queue
+ /**
+ * Removes and returns a message from the write queue and updates any
+ * associated shared state.
+ *
+ * Must be called while holding m_write_lock
+ *
+ * @todo unit tests
+ *
+ * @return the message_ptr at the front of the queue
+ */
+ message_ptr write_pop();
+
+ /// Prints information about the incoming connection to the access log
+ /**
+ * Prints information about the incoming connection to the access log.
+ * Includes: connection type, websocket version, remote endpoint, user agent
+ * path, status code.
+ */
+ void log_open_result();
+
+ /// Prints information about a connection being closed to the access log
+ /**
+ * Includes: local and remote close codes and reasons
+ */
+ void log_close_result();
+
+ /// Prints information about a connection being failed to the access log
+ /**
+ * Includes: error code and message for why it was failed
+ */
+ void log_fail_result();
+
+ /// Prints information about HTTP connections
+ /**
+ * Includes: TODO
+ */
+ void log_http_result();
+
+ /// Prints information about an arbitrary error code on the specified channel
+ template <typename error_type>
+ void log_err(log::level l, char const * msg, error_type const & ec) {
+ std::stringstream s;
+ s << msg << " error: " << ec << " (" << ec.message() << ")";
+ m_elog.write(l, s.str());
+ }
+
+ // internal handler functions
+ read_handler m_handle_read_frame;
+ write_frame_handler m_write_frame_handler;
+
+ // static settings
+ std::string const m_user_agent;
+
+ /// Pointer to the connection handle
+ connection_hdl m_connection_hdl;
+
+ /// Handler objects
+ open_handler m_open_handler;
+ close_handler m_close_handler;
+ fail_handler m_fail_handler;
+ ping_handler m_ping_handler;
+ pong_handler m_pong_handler;
+ pong_timeout_handler m_pong_timeout_handler;
+ interrupt_handler m_interrupt_handler;
+ http_handler m_http_handler;
+ validate_handler m_validate_handler;
+ message_handler m_message_handler;
+
+ /// constant values
+ long m_open_handshake_timeout_dur;
+ long m_close_handshake_timeout_dur;
+ long m_pong_timeout_dur;
+ size_t m_max_message_size;
+
+ /// External connection state
+ /**
+ * Lock: m_connection_state_lock
+ */
+ session::state::value m_state;
+
+ /// Internal connection state
+ /**
+ * Lock: m_connection_state_lock
+ */
+ istate_type m_internal_state;
+
+ mutable mutex_type m_connection_state_lock;
+
+ /// The lock used to protect the message queue
+ /**
+ * Serializes access to the write queue as well as shared state within the
+ * processor.
+ */
+ mutex_type m_write_lock;
+
+ // connection resources
+ char m_buf[config::connection_read_buffer_size];
+ size_t m_buf_cursor;
+ termination_handler m_termination_handler;
+ con_msg_manager_ptr m_msg_manager;
+ timer_ptr m_handshake_timer;
+ timer_ptr m_ping_timer;
+
+ /// @todo this is not memory efficient. this value is not used after the
+ /// handshake.
+ std::string m_handshake_buffer;
+
+ /// Pointer to the processor object for this connection
+ /**
+ * The processor provides functionality that is specific to the WebSocket
+ * protocol version that the client has negotiated. It also contains all of
+ * the state necessary to encode and decode the incoming and outgoing
+ * WebSocket byte streams
+ *
+ * Use of the prepare_data_frame method requires lock: m_write_lock
+ */
+ processor_ptr m_processor;
+
+ /// Queue of unsent outgoing messages
+ /**
+ * Lock: m_write_lock
+ */
+ std::queue<message_ptr> m_send_queue;
+
+ /// Size in bytes of the outstanding payloads in the write queue
+ /**
+ * Lock: m_write_lock
+ */
+ size_t m_send_buffer_size;
+
+ /// buffer holding the various parts of the current message being writen
+ /**
+ * Lock m_write_lock
+ */
+ std::vector<transport::buffer> m_send_buffer;
+
+ /// a list of pointers to hold on to the messages being written to keep them
+ /// from going out of scope before the write is complete.
+ std::vector<message_ptr> m_current_msgs;
+
+ /// True if there is currently an outstanding transport write
+ /**
+ * Lock m_write_lock
+ */
+ bool m_write_flag;
+
+ /// True if this connection is presently reading new data
+ bool m_read_flag;
+
+ // connection data
+ request_type m_request;
+ response_type m_response;
+ uri_ptr m_uri;
+ std::string m_subprotocol;
+
+ // connection data that might not be necessary to keep around for the life
+ // of the whole connection.
+ std::vector<std::string> m_requested_subprotocols;
+
+ bool const m_is_server;
+ alog_type& m_alog;
+ elog_type& m_elog;
+
+ rng_type & m_rng;
+
+ // Close state
+ /// Close code that was sent on the wire by this endpoint
+ close::status::value m_local_close_code;
+
+ /// Close reason that was sent on the wire by this endpoint
+ std::string m_local_close_reason;
+
+ /// Close code that was received on the wire from the remote endpoint
+ close::status::value m_remote_close_code;
+
+ /// Close reason that was received on the wire from the remote endpoint
+ std::string m_remote_close_reason;
+
+ /// Detailed internal error code
+ lib::error_code m_ec;
+
+ /// A flag that gets set once it is determined that the connection is an
+ /// HTTP connection and not a WebSocket one.
+ bool m_is_http;
+
+ /// A flag that gets set when the completion of an http connection is
+ /// deferred until later.
+ session::http_state::value m_http_state;
+
+ bool m_was_clean;
+
+ /// Whether or not this endpoint initiated the closing handshake.
+ bool m_closed_by_me;
+
+ /// ???
+ bool m_failed_by_me;
+
+ /// Whether or not this endpoint initiated the drop of the TCP connection
+ bool m_dropped_by_me;
+};
+
+} // namespace websocketpp
+
+#include <websocketpp/impl/connection_impl.hpp>
+
+#endif // WEBSOCKETPP_CONNECTION_HPP
diff --git a/websocketpp/connection_base.hpp b/websocketpp/connection_base.hpp
new file mode 100644
index 00000000..2e700962
--- /dev/null
+++ b/websocketpp/connection_base.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONNECTION_BASE_HPP
+#define WEBSOCKETPP_CONNECTION_BASE_HPP
+
+namespace websocketpp {
+
+/// Stub for user supplied base class.
+class connection_base {};
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONNECTION_BASE_HPP
diff --git a/websocketpp/endpoint.hpp b/websocketpp/endpoint.hpp
new file mode 100644
index 00000000..65584d8a
--- /dev/null
+++ b/websocketpp/endpoint.hpp
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_ENDPOINT_HPP
+#define WEBSOCKETPP_ENDPOINT_HPP
+
+#include <websocketpp/connection.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+#include <websocketpp/version.hpp>
+
+#include <string>
+
+namespace websocketpp {
+
+/// Creates and manages connections associated with a WebSocket endpoint
+template <typename connection, typename config>
+class endpoint : public config::transport_type, public config::endpoint_base {
+public:
+ // Import appropriate types from our helper class
+ // See endpoint_types for more details.
+ typedef endpoint<connection,config> type;
+
+ /// Type of the transport component of this endpoint
+ typedef typename config::transport_type transport_type;
+ /// Type of the concurrency component of this endpoint
+ typedef typename config::concurrency_type concurrency_type;
+
+ /// Type of the connections that this endpoint creates
+ typedef connection connection_type;
+ /// Shared pointer to connection_type
+ typedef typename connection_type::ptr connection_ptr;
+ /// Weak pointer to connection type
+ typedef typename connection_type::weak_ptr connection_weak_ptr;
+
+ /// Type of the transport component of the connections that this endpoint
+ /// creates
+ typedef typename transport_type::transport_con_type transport_con_type;
+ /// Type of a shared pointer to the transport component of the connections
+ /// that this endpoint creates.
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ /// Type of message_handler
+ typedef typename connection_type::message_handler message_handler;
+ /// Type of message pointers that this endpoint uses
+ typedef typename connection_type::message_ptr message_ptr;
+
+ /// Type of error logger
+ typedef typename config::elog_type elog_type;
+ /// Type of access logger
+ typedef typename config::alog_type alog_type;
+
+ /// Type of our concurrency policy's scoped lock object
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ /// Type of our concurrency policy's mutex object
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ /// Type of RNG
+ typedef typename config::rng_type rng_type;
+
+ // TODO: organize these
+ typedef typename connection_type::termination_handler termination_handler;
+
+ // This would be ideal. Requires C++11 though
+ //friend connection;
+
+ explicit endpoint(bool p_is_server)
+ : m_alog(config::alog_level, log::channel_type_hint::access)
+ , m_elog(config::elog_level, log::channel_type_hint::error)
+ , m_user_agent(::websocketpp::user_agent)
+ , m_open_handshake_timeout_dur(config::timeout_open_handshake)
+ , m_close_handshake_timeout_dur(config::timeout_close_handshake)
+ , m_pong_timeout_dur(config::timeout_pong)
+ , m_max_message_size(config::max_message_size)
+ , m_max_http_body_size(config::max_http_body_size)
+ , m_is_server(p_is_server)
+ {
+ m_alog.set_channels(config::alog_level);
+ m_elog.set_channels(config::elog_level);
+
+ m_alog.write(log::alevel::devel, "endpoint constructor");
+
+ transport_type::init_logging(&m_alog, &m_elog);
+ }
+
+
+ /// Destructor
+ ~endpoint<connection,config>() {}
+
+ #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no copy constructor because endpoints are not copyable
+ endpoint(endpoint &) = delete;
+
+ // no copy assignment operator because endpoints are not copyable
+ endpoint & operator=(endpoint const &) = delete;
+ #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+ #ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
+ /// Move constructor
+ endpoint(endpoint && o)
+ : config::transport_type(std::move(o))
+ , config::endpoint_base(std::move(o))
+ , m_alog(std::move(o.m_alog))
+ , m_elog(std::move(o.m_elog))
+ , m_user_agent(std::move(o.m_user_agent))
+ , m_open_handler(std::move(o.m_open_handler))
+
+ , m_close_handler(std::move(o.m_close_handler))
+ , m_fail_handler(std::move(o.m_fail_handler))
+ , m_ping_handler(std::move(o.m_ping_handler))
+ , m_pong_handler(std::move(o.m_pong_handler))
+ , m_pong_timeout_handler(std::move(o.m_pong_timeout_handler))
+ , m_interrupt_handler(std::move(o.m_interrupt_handler))
+ , m_http_handler(std::move(o.m_http_handler))
+ , m_validate_handler(std::move(o.m_validate_handler))
+ , m_message_handler(std::move(o.m_message_handler))
+
+ , m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur)
+ , m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur)
+ , m_pong_timeout_dur(o.m_pong_timeout_dur)
+ , m_max_message_size(o.m_max_message_size)
+ , m_max_http_body_size(o.m_max_http_body_size)
+
+ , m_rng(std::move(o.m_rng))
+ , m_is_server(o.m_is_server)
+ {}
+
+ #ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no move assignment operator because of const member variables
+ endpoint & operator=(endpoint &&) = delete;
+ #endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+ #endif // _WEBSOCKETPP_MOVE_SEMANTICS_
+
+
+ /// Returns the user agent string that this endpoint will use
+ /**
+ * Returns the user agent string that this endpoint will use when creating
+ * new connections.
+ *
+ * The default value for this version is stored in websocketpp::user_agent
+ *
+ * @return The user agent string.
+ */
+ std::string get_user_agent() const {
+ scoped_lock_type guard(m_mutex);
+ return m_user_agent;
+ }
+
+ /// Sets the user agent string that this endpoint will use
+ /**
+ * Sets the identifier that this endpoint will use when creating new
+ * connections. Changing this value will only affect future connections.
+ * For client endpoints this will be sent as the "User-Agent" header in
+ * outgoing requests. For server endpoints this will be sent in the "Server"
+ * response header.
+ *
+ * Setting this value to the empty string will suppress the use of the
+ * Server and User-Agent headers. This is typically done to hide
+ * implementation details for security purposes.
+ *
+ * For best results set this before accepting or opening connections.
+ *
+ * The default value for this version is stored in websocketpp::user_agent
+ *
+ * This can be overridden on an individual connection basis by setting a
+ * custom "Server" header during the validate handler or "User-Agent"
+ * header on a connection before calling connect().
+ *
+ * @param ua The string to set the user agent to.
+ */
+ void set_user_agent(std::string const & ua) {
+ scoped_lock_type guard(m_mutex);
+ m_user_agent = ua;
+ }
+
+ /// Returns whether or not this endpoint is a server.
+ /**
+ * @return Whether or not this endpoint is a server
+ */
+ bool is_server() const {
+ return m_is_server;
+ }
+
+ /********************************/
+ /* Pass-through logging adaptor */
+ /********************************/
+
+ /// Set Access logging channel
+ /**
+ * Set the access logger's channel value. The value is a number whose
+ * interpretation depends on the logging policy in use.
+ *
+ * @param channels The channel value(s) to set
+ */
+ void set_access_channels(log::level channels) {
+ m_alog.set_channels(channels);
+ }
+
+ /// Clear Access logging channels
+ /**
+ * Clear the access logger's channel value. The value is a number whose
+ * interpretation depends on the logging policy in use.
+ *
+ * @param channels The channel value(s) to clear
+ */
+ void clear_access_channels(log::level channels) {
+ m_alog.clear_channels(channels);
+ }
+
+ /// Set Error logging channel
+ /**
+ * Set the error logger's channel value. The value is a number whose
+ * interpretation depends on the logging policy in use.
+ *
+ * @param channels The channel value(s) to set
+ */
+ void set_error_channels(log::level channels) {
+ m_elog.set_channels(channels);
+ }
+
+ /// Clear Error logging channels
+ /**
+ * Clear the error logger's channel value. The value is a number whose
+ * interpretation depends on the logging policy in use.
+ *
+ * @param channels The channel value(s) to clear
+ */
+ void clear_error_channels(log::level channels) {
+ m_elog.clear_channels(channels);
+ }
+
+ /// Get reference to access logger
+ /**
+ * @return A reference to the access logger
+ */
+ alog_type & get_alog() {
+ return m_alog;
+ }
+
+ /// Get reference to error logger
+ /**
+ * @return A reference to the error logger
+ */
+ elog_type & get_elog() {
+ return m_elog;
+ }
+
+ /*************************/
+ /* Set Handler functions */
+ /*************************/
+
+ void set_open_handler(open_handler h) {
+ m_alog.write(log::alevel::devel,"set_open_handler");
+ scoped_lock_type guard(m_mutex);
+ m_open_handler = h;
+ }
+ void set_close_handler(close_handler h) {
+ m_alog.write(log::alevel::devel,"set_close_handler");
+ scoped_lock_type guard(m_mutex);
+ m_close_handler = h;
+ }
+ void set_fail_handler(fail_handler h) {
+ m_alog.write(log::alevel::devel,"set_fail_handler");
+ scoped_lock_type guard(m_mutex);
+ m_fail_handler = h;
+ }
+ void set_ping_handler(ping_handler h) {
+ m_alog.write(log::alevel::devel,"set_ping_handler");
+ scoped_lock_type guard(m_mutex);
+ m_ping_handler = h;
+ }
+ void set_pong_handler(pong_handler h) {
+ m_alog.write(log::alevel::devel,"set_pong_handler");
+ scoped_lock_type guard(m_mutex);
+ m_pong_handler = h;
+ }
+ void set_pong_timeout_handler(pong_timeout_handler h) {
+ m_alog.write(log::alevel::devel,"set_pong_timeout_handler");
+ scoped_lock_type guard(m_mutex);
+ m_pong_timeout_handler = h;
+ }
+ void set_interrupt_handler(interrupt_handler h) {
+ m_alog.write(log::alevel::devel,"set_interrupt_handler");
+ scoped_lock_type guard(m_mutex);
+ m_interrupt_handler = h;
+ }
+ void set_http_handler(http_handler h) {
+ m_alog.write(log::alevel::devel,"set_http_handler");
+ scoped_lock_type guard(m_mutex);
+ m_http_handler = h;
+ }
+ void set_validate_handler(validate_handler h) {
+ m_alog.write(log::alevel::devel,"set_validate_handler");
+ scoped_lock_type guard(m_mutex);
+ m_validate_handler = h;
+ }
+ void set_message_handler(message_handler h) {
+ m_alog.write(log::alevel::devel,"set_message_handler");
+ scoped_lock_type guard(m_mutex);
+ m_message_handler = h;
+ }
+
+ //////////////////////////////////////////
+ // Connection timeouts and other limits //
+ //////////////////////////////////////////
+
+ /// Set open handshake timeout
+ /**
+ * Sets the length of time the library will wait after an opening handshake
+ * has been initiated before cancelling it. This can be used to prevent
+ * excessive wait times for outgoing clients or excessive resource usage
+ * from broken clients or DoS attacks on servers.
+ *
+ * Connections that time out will have their fail handlers called with the
+ * open_handshake_timeout error code.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_open_handshake'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the open handshake timeout in ms
+ */
+ void set_open_handshake_timeout(long dur) {
+ scoped_lock_type guard(m_mutex);
+ m_open_handshake_timeout_dur = dur;
+ }
+
+ /// Set close handshake timeout
+ /**
+ * Sets the length of time the library will wait after a closing handshake
+ * has been initiated before cancelling it. This can be used to prevent
+ * excessive wait times for outgoing clients or excessive resource usage
+ * from broken clients or DoS attacks on servers.
+ *
+ * Connections that time out will have their close handlers called with the
+ * close_handshake_timeout error code.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_close_handshake'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the close handshake timeout in ms
+ */
+ void set_close_handshake_timeout(long dur) {
+ scoped_lock_type guard(m_mutex);
+ m_close_handshake_timeout_dur = dur;
+ }
+
+ /// Set pong timeout
+ /**
+ * Sets the length of time the library will wait for a pong response to a
+ * ping. This can be used as a keepalive or to detect broken connections.
+ *
+ * Pong responses that time out will have the pong timeout handler called.
+ *
+ * The default value is specified via the compile time config value
+ * 'timeout_pong'. The default value in the core config
+ * is 5000ms. A value of 0 will disable the timer entirely.
+ *
+ * To be effective, the transport you are using must support timers. See
+ * the documentation for your transport policy for details about its
+ * timer support.
+ *
+ * @param dur The length of the pong timeout in ms
+ */
+ void set_pong_timeout(long dur) {
+ scoped_lock_type guard(m_mutex);
+ m_pong_timeout_dur = dur;
+ }
+
+ /// Get default maximum message size
+ /**
+ * Get the default maximum message size that will be used for new
+ * connections created by this endpoint. The maximum message size determines
+ * the point at which the connection will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is set by the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ */
+ size_t get_max_message_size() const {
+ return m_max_message_size;
+ }
+
+ /// Set default maximum message size
+ /**
+ * Set the default maximum message size that will be used for new
+ * connections created by this endpoint. Maximum message size determines the
+ * point at which the connection will fail a connection with the
+ * message_too_big protocol error.
+ *
+ * The default is set by the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_message_size(size_t new_value) {
+ m_max_message_size = new_value;
+ }
+
+ /// Get maximum HTTP message body size
+ /**
+ * Get maximum HTTP message body size. Maximum message body size determines
+ * the point at which the connection will stop reading an HTTP request whose
+ * body is too large.
+ *
+ * The default is set by the max_http_body_size value from the template
+ * config
+ *
+ * @since 0.5.0
+ *
+ * @return The maximum HTTP message body size
+ */
+ size_t get_max_http_body_size() const {
+ return m_max_http_body_size;
+ }
+
+ /// Set maximum HTTP message body size
+ /**
+ * Set maximum HTTP message body size. Maximum message body size determines
+ * the point at which the connection will stop reading an HTTP request whose
+ * body is too large.
+ *
+ * The default is set by the max_http_body_size value from the template
+ * config
+ *
+ * @since 0.5.1
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_http_body_size(size_t new_value) {
+ m_max_http_body_size = new_value;
+ }
+
+ /*************************************/
+ /* Connection pass through functions */
+ /*************************************/
+
+ /**
+ * These functions act as adaptors to their counterparts in connection. They
+ * can produce one additional type of error, the bad_connection error, that
+ * indicates that the conversion from connection_hdl to connection_ptr
+ * failed due to the connection not existing anymore. Each method has a
+ * default and an exception free varient.
+ */
+
+ void interrupt(connection_hdl hdl, lib::error_code & ec);
+ void interrupt(connection_hdl hdl);
+
+ /// Pause reading of new data (exception free)
+ /**
+ * Signals to the connection to halt reading of new data. While reading is
+ * paused, the connection will stop reading from its associated socket. In
+ * turn this will result in TCP based flow control kicking in and slowing
+ * data flow from the remote endpoint.
+ *
+ * This is useful for applications that push new requests to a queue to be
+ * processed by another thread and need a way to signal when their request
+ * queue is full without blocking the network processing thread.
+ *
+ * Use `resume_reading()` to resume.
+ *
+ * If supported by the transport this is done asynchronously. As such
+ * reading may not stop until the current read operation completes.
+ * Typically you can expect to receive no more bytes after initiating a read
+ * pause than the size of the read buffer.
+ *
+ * If reading is paused for this connection already nothing is changed.
+ */
+ void pause_reading(connection_hdl hdl, lib::error_code & ec);
+
+ /// Pause reading of new data
+ void pause_reading(connection_hdl hdl);
+
+ /// Resume reading of new data (exception free)
+ /**
+ * Signals to the connection to resume reading of new data after it was
+ * paused by `pause_reading()`.
+ *
+ * If reading is not paused for this connection already nothing is changed.
+ */
+ void resume_reading(connection_hdl hdl, lib::error_code & ec);
+
+ /// Resume reading of new data
+ void resume_reading(connection_hdl hdl);
+
+ /// Send deferred HTTP Response
+ /**
+ * Sends an http response to an HTTP connection that was deferred. This will
+ * send a complete response including all headers, status line, and body
+ * text. The connection will be closed afterwards.
+ *
+ * Exception free variant
+ *
+ * @since 0.6.0
+ *
+ * @param hdl The connection to send the response on
+ * @param ec A status code, zero on success, non-zero otherwise
+ */
+ void send_http_response(connection_hdl hdl, lib::error_code & ec);
+
+ /// Send deferred HTTP Response (exception free)
+ /**
+ * Sends an http response to an HTTP connection that was deferred. This will
+ * send a complete response including all headers, status line, and body
+ * text. The connection will be closed afterwards.
+ *
+ * Exception variant
+ *
+ * @since 0.6.0
+ *
+ * @param hdl The connection to send the response on
+ */
+ void send_http_response(connection_hdl hdl);
+
+ /// Create a message and add it to the outgoing send queue (exception free)
+ /**
+ * Convenience method to send a message given a payload string and an opcode
+ *
+ * @param [in] hdl The handle identifying the connection to send via.
+ * @param [in] payload The payload string to generated the message with
+ * @param [in] op The opcode to generated the message with.
+ * @param [out] ec A code to fill in for errors
+ */
+ void send(connection_hdl hdl, std::string const & payload,
+ frame::opcode::value op, lib::error_code & ec);
+ /// Create a message and add it to the outgoing send queue
+ /**
+ * Convenience method to send a message given a payload string and an opcode
+ *
+ * @param [in] hdl The handle identifying the connection to send via.
+ * @param [in] payload The payload string to generated the message with
+ * @param [in] op The opcode to generated the message with.
+ * @param [out] ec A code to fill in for errors
+ */
+ void send(connection_hdl hdl, std::string const & payload,
+ frame::opcode::value op);
+
+ void send(connection_hdl hdl, void const * payload, size_t len,
+ frame::opcode::value op, lib::error_code & ec);
+ void send(connection_hdl hdl, void const * payload, size_t len,
+ frame::opcode::value op);
+
+ void send(connection_hdl hdl, message_ptr msg, lib::error_code & ec);
+ void send(connection_hdl hdl, message_ptr msg);
+
+ void close(connection_hdl hdl, close::status::value const code,
+ std::string const & reason, lib::error_code & ec);
+ void close(connection_hdl hdl, close::status::value const code,
+ std::string const & reason);
+
+ /// Send a ping to a specific connection
+ /**
+ * @since 0.3.0-alpha3
+ *
+ * @param [in] hdl The connection_hdl of the connection to send to.
+ * @param [in] payload The payload string to send.
+ * @param [out] ec A reference to an error code to fill in
+ */
+ void ping(connection_hdl hdl, std::string const & payload,
+ lib::error_code & ec);
+ /// Send a ping to a specific connection
+ /**
+ * Exception variant of `ping`
+ *
+ * @since 0.3.0-alpha3
+ *
+ * @param [in] hdl The connection_hdl of the connection to send to.
+ * @param [in] payload The payload string to send.
+ */
+ void ping(connection_hdl hdl, std::string const & payload);
+
+ /// Send a pong to a specific connection
+ /**
+ * @since 0.3.0-alpha3
+ *
+ * @param [in] hdl The connection_hdl of the connection to send to.
+ * @param [in] payload The payload string to send.
+ * @param [out] ec A reference to an error code to fill in
+ */
+ void pong(connection_hdl hdl, std::string const & payload,
+ lib::error_code & ec);
+ /// Send a pong to a specific connection
+ /**
+ * Exception variant of `pong`
+ *
+ * @since 0.3.0-alpha3
+ *
+ * @param [in] hdl The connection_hdl of the connection to send to.
+ * @param [in] payload The payload string to send.
+ */
+ void pong(connection_hdl hdl, std::string const & payload);
+
+ /// Retrieves a connection_ptr from a connection_hdl (exception free)
+ /**
+ * Converting a weak pointer to shared_ptr is not thread safe because the
+ * pointer could be deleted at any time.
+ *
+ * NOTE: This method may be called by handler to upgrade its handle to a
+ * full connection_ptr. That full connection may then be used safely for the
+ * remainder of the handler body. get_con_from_hdl and the resulting
+ * connection_ptr are NOT safe to use outside the handler loop.
+ *
+ * @param hdl The connection handle to translate
+ *
+ * @return the connection_ptr. May be NULL if the handle was invalid.
+ */
+ connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) {
+ connection_ptr con = lib::static_pointer_cast<connection_type>(
+ hdl.lock());
+ if (!con) {
+ ec = error::make_error_code(error::bad_connection);
+ }
+ return con;
+ }
+
+ /// Retrieves a connection_ptr from a connection_hdl (exception version)
+ connection_ptr get_con_from_hdl(connection_hdl hdl) {
+ lib::error_code ec;
+ connection_ptr con = this->get_con_from_hdl(hdl,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+ return con;
+ }
+protected:
+ connection_ptr create_connection();
+
+ alog_type m_alog;
+ elog_type m_elog;
+private:
+ // dynamic settings
+ std::string m_user_agent;
+
+ open_handler m_open_handler;
+ close_handler m_close_handler;
+ fail_handler m_fail_handler;
+ ping_handler m_ping_handler;
+ pong_handler m_pong_handler;
+ pong_timeout_handler m_pong_timeout_handler;
+ interrupt_handler m_interrupt_handler;
+ http_handler m_http_handler;
+ validate_handler m_validate_handler;
+ message_handler m_message_handler;
+
+ long m_open_handshake_timeout_dur;
+ long m_close_handshake_timeout_dur;
+ long m_pong_timeout_dur;
+ size_t m_max_message_size;
+ size_t m_max_http_body_size;
+
+ rng_type m_rng;
+
+ // static settings
+ bool const m_is_server;
+
+ // endpoint state
+ mutable mutex_type m_mutex;
+};
+
+} // namespace websocketpp
+
+#include <websocketpp/impl/endpoint_impl.hpp>
+
+#endif // WEBSOCKETPP_ENDPOINT_HPP
diff --git a/websocketpp/endpoint_base.hpp b/websocketpp/endpoint_base.hpp
new file mode 100644
index 00000000..1ad1a44d
--- /dev/null
+++ b/websocketpp/endpoint_base.hpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_ENDPOINT_BASE_HPP
+#define WEBSOCKETPP_ENDPOINT_BASE_HPP
+
+namespace websocketpp {
+
+/// Stub for user supplied base class.
+class endpoint_base {};
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_ENDPOINT_BASE_HPP
diff --git a/websocketpp/error.hpp b/websocketpp/error.hpp
new file mode 100644
index 00000000..562fb6ed
--- /dev/null
+++ b/websocketpp/error.hpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_ERROR_HPP
+#define WEBSOCKETPP_ERROR_HPP
+
+#include <exception>
+#include <string>
+#include <utility>
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+namespace websocketpp {
+
+/// Combination error code / string type for returning two values
+typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+/// Library level error codes
+namespace error {
+enum value {
+ /// Catch-all library error
+ general = 1,
+
+ /// send attempted when endpoint write queue was full
+ send_queue_full,
+
+ /// Attempted an operation using a payload that was improperly formatted
+ /// ex: invalid UTF8 encoding on a text message.
+ payload_violation,
+
+ /// Attempted to open a secure connection with an insecure endpoint
+ endpoint_not_secure,
+
+ /// Attempted an operation that required an endpoint that is no longer
+ /// available. This is usually because the endpoint went out of scope
+ /// before a connection that it created.
+ endpoint_unavailable,
+
+ /// An invalid uri was supplied
+ invalid_uri,
+
+ /// The endpoint is out of outgoing message buffers
+ no_outgoing_buffers,
+
+ /// The endpoint is out of incoming message buffers
+ no_incoming_buffers,
+
+ /// The connection was in the wrong state for this operation
+ invalid_state,
+
+ /// Unable to parse close code
+ bad_close_code,
+
+ /// Close code is in a reserved range
+ reserved_close_code,
+
+ /// Close code is invalid
+ invalid_close_code,
+
+ /// Invalid UTF-8
+ invalid_utf8,
+
+ /// Invalid subprotocol
+ invalid_subprotocol,
+
+ /// An operation was attempted on a connection that did not exist or was
+ /// already deleted.
+ bad_connection,
+
+ /// Unit testing utility error code
+ test,
+
+ /// Connection creation attempted failed
+ con_creation_failed,
+
+ /// Selected subprotocol was not requested by the client
+ unrequested_subprotocol,
+
+ /// Attempted to use a client specific feature on a server endpoint
+ client_only,
+
+ /// Attempted to use a server specific feature on a client endpoint
+ server_only,
+
+ /// HTTP connection ended
+ http_connection_ended,
+
+ /// WebSocket opening handshake timed out
+ open_handshake_timeout,
+
+ /// WebSocket close handshake timed out
+ close_handshake_timeout,
+
+ /// Invalid port in URI
+ invalid_port,
+
+ /// An async accept operation failed because the underlying transport has been
+ /// requested to not listen for new connections anymore.
+ async_accept_not_listening,
+
+ /// The requested operation was canceled
+ operation_canceled,
+
+ /// Connection rejected
+ rejected,
+
+ /// Upgrade Required. This happens if an HTTP request is made to a
+ /// WebSocket++ server that doesn't implement an http handler
+ upgrade_required,
+
+ /// Invalid WebSocket protocol version
+ invalid_version,
+
+ /// Unsupported WebSocket protocol version
+ unsupported_version,
+
+ /// HTTP parse error
+ http_parse_error,
+
+ /// Extension negotiation failed
+ extension_neg_failed
+}; // enum value
+
+
+class category : public lib::error_category {
+public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::general:
+ return "Generic error";
+ case error::send_queue_full:
+ return "send queue full";
+ case error::payload_violation:
+ return "payload violation";
+ case error::endpoint_not_secure:
+ return "endpoint not secure";
+ case error::endpoint_unavailable:
+ return "endpoint not available";
+ case error::invalid_uri:
+ return "invalid uri";
+ case error::no_outgoing_buffers:
+ return "no outgoing message buffers";
+ case error::no_incoming_buffers:
+ return "no incoming message buffers";
+ case error::invalid_state:
+ return "invalid state";
+ case error::bad_close_code:
+ return "Unable to extract close code";
+ case error::invalid_close_code:
+ return "Extracted close code is in an invalid range";
+ case error::reserved_close_code:
+ return "Extracted close code is in a reserved range";
+ case error::invalid_utf8:
+ return "Invalid UTF-8";
+ case error::invalid_subprotocol:
+ return "Invalid subprotocol";
+ case error::bad_connection:
+ return "Bad Connection";
+ case error::test:
+ return "Test Error";
+ case error::con_creation_failed:
+ return "Connection creation attempt failed";
+ case error::unrequested_subprotocol:
+ return "Selected subprotocol was not requested by the client";
+ case error::client_only:
+ return "Feature not available on server endpoints";
+ case error::server_only:
+ return "Feature not available on client endpoints";
+ case error::http_connection_ended:
+ return "HTTP connection ended";
+ case error::open_handshake_timeout:
+ return "The opening handshake timed out";
+ case error::close_handshake_timeout:
+ return "The closing handshake timed out";
+ case error::invalid_port:
+ return "Invalid URI port";
+ case error::async_accept_not_listening:
+ return "Async Accept not listening";
+ case error::operation_canceled:
+ return "Operation canceled";
+ case error::rejected:
+ return "Connection rejected";
+ case error::upgrade_required:
+ return "Upgrade required";
+ case error::invalid_version:
+ return "Invalid version";
+ case error::unsupported_version:
+ return "Unsupported version";
+ case error::http_parse_error:
+ return "HTTP parse error";
+ case error::extension_neg_failed:
+ return "Extension negotiation failed";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+inline const lib::error_category& get_category() {
+ static category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+namespace websocketpp {
+
+class exception : public std::exception {
+public:
+ exception(std::string const & msg, lib::error_code ec = make_error_code(error::general))
+ : m_msg(msg.empty() ? ec.message() : msg), m_code(ec)
+ {}
+
+ explicit exception(lib::error_code ec)
+ : m_msg(ec.message()), m_code(ec)
+ {}
+
+ ~exception() throw() {}
+
+ virtual char const * what() const throw() {
+ return m_msg.c_str();
+ }
+
+ lib::error_code code() const throw() {
+ return m_code;
+ }
+
+ const std::string m_msg;
+ lib::error_code m_code;
+};
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_ERROR_HPP
diff --git a/websocketpp/extensions/extension.hpp b/websocketpp/extensions/extension.hpp
new file mode 100644
index 00000000..f5fbd9f7
--- /dev/null
+++ b/websocketpp/extensions/extension.hpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_EXTENSION_HPP
+#define WEBSOCKETPP_EXTENSION_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+
+/**
+ * Some generic information about extensions
+ *
+ * Each extension object has an implemented flag. It can be retrieved by calling
+ * is_implemented(). This compile time flag indicates whether or not the object
+ * in question actually implements the extension or if it is a placeholder stub
+ *
+ * Each extension object also has an enabled flag. It can be retrieved by
+ * calling is_enabled(). This runtime flag indicates whether or not the
+ * extension has been negotiated for this connection.
+ */
+namespace extensions {
+
+namespace error {
+enum value {
+ /// Catch all
+ general = 1,
+
+ /// Extension disabled
+ disabled
+};
+
+class category : public lib::error_category {
+public:
+ category() {}
+
+ const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.extension";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic extension error";
+ case disabled:
+ return "Use of methods from disabled extension";
+ default:
+ return "Unknown permessage-compress error";
+ }
+ }
+};
+
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace extensions
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum
+ <websocketpp::extensions::error::value>
+{
+ static const bool value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_EXTENSION_HPP
diff --git a/websocketpp/extensions/permessage_deflate/disabled.hpp b/websocketpp/extensions/permessage_deflate/disabled.hpp
new file mode 100644
index 00000000..49c0e1dc
--- /dev/null
+++ b/websocketpp/extensions/permessage_deflate/disabled.hpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
+#define WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <websocketpp/http/constants.hpp>
+#include <websocketpp/extensions/extension.hpp>
+
+#include <map>
+#include <string>
+#include <utility>
+
+namespace websocketpp {
+namespace extensions {
+namespace permessage_deflate {
+
+/// Stub class for use when disabling permessage_deflate extension
+/**
+ * This class is a stub that implements the permessage_deflate interface
+ * with minimal dependencies. It is used to disable permessage_deflate
+ * functionality at compile time without loading any unnecessary code.
+ */
+template <typename config>
+class disabled {
+ typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+public:
+ /// Negotiate extension
+ /**
+ * The disabled extension always fails the negotiation with a disabled
+ * error.
+ *
+ * @param offer Attribute from client's offer
+ * @return Status code and value to return to remote endpoint
+ */
+ err_str_pair negotiate(http::attribute_list const &) {
+ return make_pair(make_error_code(error::disabled),std::string());
+ }
+
+ /// Initialize state
+ /**
+ * For the disabled extension state initialization is a no-op.
+ *
+ * @param is_server True to initialize as a server, false for a client.
+ * @return A code representing the error that occurred, if any
+ */
+ lib::error_code init(bool) {
+ return lib::error_code();
+ }
+
+ /// Returns true if the extension is capable of providing
+ /// permessage_deflate functionality
+ bool is_implemented() const {
+ return false;
+ }
+
+ /// Returns true if permessage_deflate functionality is active for this
+ /// connection
+ bool is_enabled() const {
+ return false;
+ }
+
+ /// Generate extension offer
+ /**
+ * Creates an offer string to include in the Sec-WebSocket-Extensions
+ * header of outgoing client requests.
+ *
+ * @return A WebSocket extension offer string for this extension
+ */
+ std::string generate_offer() const {
+ return "";
+ }
+
+ /// Compress bytes
+ /**
+ * @param [in] in String to compress
+ * @param [out] out String to append compressed bytes to
+ * @return Error or status code
+ */
+ lib::error_code compress(std::string const &, std::string &) {
+ return make_error_code(error::disabled);
+ }
+
+ /// Decompress bytes
+ /**
+ * @param buf Byte buffer to decompress
+ * @param len Length of buf
+ * @param out String to append decompressed bytes to
+ * @return Error or status code
+ */
+ lib::error_code decompress(uint8_t const *, size_t, std::string &) {
+ return make_error_code(error::disabled);
+ }
+};
+
+} // namespace permessage_deflate
+} // namespace extensions
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_EXTENSION_PERMESSAGE_DEFLATE_DISABLED_HPP
diff --git a/websocketpp/extensions/permessage_deflate/enabled.hpp b/websocketpp/extensions/permessage_deflate/enabled.hpp
new file mode 100644
index 00000000..1581f14c
--- /dev/null
+++ b/websocketpp/extensions/permessage_deflate/enabled.hpp
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
+#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
+
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/error.hpp>
+
+#include <websocketpp/extensions/extension.hpp>
+
+#include "zlib.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace extensions {
+
+/// Implementation of the draft permessage-deflate WebSocket extension
+/**
+ * ### permessage-deflate interface
+ *
+ * **init**\n
+ * `lib::error_code init(bool is_server)`\n
+ * Performs initialization
+ *
+ * **is_implimented**\n
+ * `bool is_implimented()`\n
+ * Returns whether or not the object impliments the extension or not
+ *
+ * **is_enabled**\n
+ * `bool is_enabled()`\n
+ * Returns whether or not the extension was negotiated for the current
+ * connection
+ *
+ * **generate_offer**\n
+ * `std::string generate_offer() const`\n
+ * Create an extension offer string based on local policy
+ *
+ * **validate_response**\n
+ * `lib::error_code validate_response(http::attribute_list const & response)`\n
+ * Negotiate the parameters of extension use
+ *
+ * **negotiate**\n
+ * `err_str_pair negotiate(http::attribute_list const & attributes)`\n
+ * Negotiate the parameters of extension use
+ *
+ * **compress**\n
+ * `lib::error_code compress(std::string const & in, std::string & out)`\n
+ * Compress the bytes in `in` and append them to `out`
+ *
+ * **decompress**\n
+ * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
+ * out)`\n
+ * Decompress `len` bytes from `buf` and append them to string `out`
+ */
+namespace permessage_deflate {
+
+/// Permessage deflate error values
+namespace error {
+enum value {
+ /// Catch all
+ general = 1,
+
+ /// Invalid extension attributes
+ invalid_attributes,
+
+ /// Invalid extension attribute value
+ invalid_attribute_value,
+
+ /// Invalid megotiation mode
+ invalid_mode,
+
+ /// Unsupported extension attributes
+ unsupported_attributes,
+
+ /// Invalid value for max_window_bits
+ invalid_max_window_bits,
+
+ /// ZLib Error
+ zlib_error,
+
+ /// Uninitialized
+ uninitialized,
+};
+
+/// Permessage-deflate error category
+class category : public lib::error_category {
+public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.extension.permessage-deflate";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic permessage-compress error";
+ case invalid_attributes:
+ return "Invalid extension attributes";
+ case invalid_attribute_value:
+ return "Invalid extension attribute value";
+ case invalid_mode:
+ return "Invalid permessage-deflate negotiation mode";
+ case unsupported_attributes:
+ return "Unsupported extension attributes";
+ case invalid_max_window_bits:
+ return "Invalid value for max_window_bits";
+ case zlib_error:
+ return "A zlib function returned an error";
+ case uninitialized:
+ return "Deflate extension must be initialized before use";
+ default:
+ return "Unknown permessage-compress error";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the permessage-deflate error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Create an error code in the permessage-deflate category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace permessage_deflate
+} // namespace extensions
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum
+ <websocketpp::extensions::permessage_deflate::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+namespace websocketpp {
+namespace extensions {
+namespace permessage_deflate {
+
+/// Default value for server_max_window_bits as defined by draft 17
+static uint8_t const default_server_max_window_bits = 15;
+/// Minimum value for server_max_window_bits as defined by draft 17
+static uint8_t const min_server_max_window_bits = 8;
+/// Maximum value for server_max_window_bits as defined by draft 17
+static uint8_t const max_server_max_window_bits = 15;
+
+/// Default value for client_max_window_bits as defined by draft 17
+static uint8_t const default_client_max_window_bits = 15;
+/// Minimum value for client_max_window_bits as defined by draft 17
+static uint8_t const min_client_max_window_bits = 8;
+/// Maximum value for client_max_window_bits as defined by draft 17
+static uint8_t const max_client_max_window_bits = 15;
+
+namespace mode {
+enum value {
+ /// Accept any value the remote endpoint offers
+ accept = 1,
+ /// Decline any value the remote endpoint offers. Insist on defaults.
+ decline,
+ /// Use the largest value common to both offers
+ largest,
+ /// Use the smallest value common to both offers
+ smallest
+};
+} // namespace mode
+
+template <typename config>
+class enabled {
+public:
+ enabled()
+ : m_enabled(false)
+ , m_server_no_context_takeover(false)
+ , m_client_no_context_takeover(false)
+ , m_server_max_window_bits(15)
+ , m_client_max_window_bits(15)
+ , m_server_max_window_bits_mode(mode::accept)
+ , m_client_max_window_bits_mode(mode::accept)
+ , m_initialized(false)
+ , m_compress_buffer_size(16384)
+ {
+ m_dstate.zalloc = Z_NULL;
+ m_dstate.zfree = Z_NULL;
+ m_dstate.opaque = Z_NULL;
+
+ m_istate.zalloc = Z_NULL;
+ m_istate.zfree = Z_NULL;
+ m_istate.opaque = Z_NULL;
+ m_istate.avail_in = 0;
+ m_istate.next_in = Z_NULL;
+ }
+
+ ~enabled() {
+ if (!m_initialized) {
+ return;
+ }
+
+ int ret = deflateEnd(&m_dstate);
+
+ if (ret != Z_OK) {
+ //std::cout << "error cleaning up zlib compression state"
+ // << std::endl;
+ }
+
+ ret = inflateEnd(&m_istate);
+
+ if (ret != Z_OK) {
+ //std::cout << "error cleaning up zlib decompression state"
+ // << std::endl;
+ }
+ }
+
+ /// Initialize zlib state
+ /**
+ * Note: this should be called *after* the negotiation methods. It will use
+ * information from the negotiation to determine how to initialize the zlib
+ * data structures.
+ *
+ * @todo memory level, strategy, etc are hardcoded
+ *
+ * @param is_server True to initialize as a server, false for a client.
+ * @return A code representing the error that occurred, if any
+ */
+ lib::error_code init(bool is_server) {
+ uint8_t deflate_bits;
+ uint8_t inflate_bits;
+
+ if (is_server) {
+ deflate_bits = m_server_max_window_bits;
+ inflate_bits = m_client_max_window_bits;
+ } else {
+ deflate_bits = m_client_max_window_bits;
+ inflate_bits = m_server_max_window_bits;
+ }
+
+ int ret = deflateInit2(
+ &m_dstate,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ -1*deflate_bits,
+ 4, // memory level 1-9
+ Z_DEFAULT_STRATEGY
+ );
+
+ if (ret != Z_OK) {
+ return make_error_code(error::zlib_error);
+ }
+
+ ret = inflateInit2(
+ &m_istate,
+ -1*inflate_bits
+ );
+
+ if (ret != Z_OK) {
+ return make_error_code(error::zlib_error);
+ }
+
+ m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
+ if ((m_server_no_context_takeover && is_server) ||
+ (m_client_no_context_takeover && !is_server))
+ {
+ m_flush = Z_FULL_FLUSH;
+ } else {
+ m_flush = Z_SYNC_FLUSH;
+ }
+ m_initialized = true;
+ return lib::error_code();
+ }
+
+ /// Test if this object implements the permessage-deflate specification
+ /**
+ * Because this object does implieent it, it will always return true.
+ *
+ * @return Whether or not this object implements permessage-deflate
+ */
+ bool is_implemented() const {
+ return true;
+ }
+
+ /// Test if the extension was negotiated for this connection
+ /**
+ * Retrieves whether or not this extension is in use based on the initial
+ * handshake extension negotiations.
+ *
+ * @return Whether or not the extension is in use
+ */
+ bool is_enabled() const {
+ return m_enabled;
+ }
+
+ /// Reset server's outgoing LZ77 sliding window for each new message
+ /**
+ * Enabling this setting will cause the server's compressor to reset the
+ * compression state (the LZ77 sliding window) for every message. This
+ * means that the compressor will not look back to patterns in previous
+ * messages to improve compression. This will reduce the compression
+ * efficiency for large messages somewhat and small messages drastically.
+ *
+ * This option may reduce server compressor memory usage and client
+ * decompressor memory usage.
+ * @todo Document to what extent memory usage will be reduced
+ *
+ * For clients, this option is dependent on server support. Enabling it
+ * via this method does not guarantee that it will be successfully
+ * negotiated, only that it will be requested.
+ *
+ * For servers, no client support is required. Enabling this option on a
+ * server will result in its use. The server will signal to clients that
+ * the option will be in use so they can optimize resource usage if they
+ * are able.
+ */
+ void enable_server_no_context_takeover() {
+ m_server_no_context_takeover = true;
+ }
+
+ /// Reset client's outgoing LZ77 sliding window for each new message
+ /**
+ * Enabling this setting will cause the client's compressor to reset the
+ * compression state (the LZ77 sliding window) for every message. This
+ * means that the compressor will not look back to patterns in previous
+ * messages to improve compression. This will reduce the compression
+ * efficiency for large messages somewhat and small messages drastically.
+ *
+ * This option may reduce client compressor memory usage and server
+ * decompressor memory usage.
+ * @todo Document to what extent memory usage will be reduced
+ *
+ * This option is supported by all compliant clients and servers. Enabling
+ * it via either endpoint should be sufficient to ensure it is used.
+ */
+ void enable_client_no_context_takeover() {
+ m_client_no_context_takeover = true;
+ }
+
+ /// Limit server LZ77 sliding window size
+ /**
+ * The bits setting is the base 2 logarithm of the maximum window size that
+ * the server must use to compress outgoing messages. The permitted range
+ * is 8 to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB
+ * window. The default setting is 15.
+ *
+ * Mode Options:
+ * - accept: Accept whatever the remote endpoint offers.
+ * - decline: Decline any offers to deviate from the defaults
+ * - largest: Accept largest window size acceptable to both endpoints
+ * - smallest: Accept smallest window size acceptiable to both endpoints
+ *
+ * This setting is dependent on server support. A client requesting this
+ * setting may be rejected by the server or have the exact value used
+ * adjusted by the server. A server may unilaterally set this value without
+ * client support.
+ *
+ * @param bits The size to request for the outgoing window size
+ * @param mode The mode to use for negotiating this parameter
+ * @return A status code
+ */
+ lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) {
+ if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
+ return error::make_error_code(error::invalid_max_window_bits);
+ }
+ m_server_max_window_bits = bits;
+ m_server_max_window_bits_mode = mode;
+
+ return lib::error_code();
+ }
+
+ /// Limit client LZ77 sliding window size
+ /**
+ * The bits setting is the base 2 logarithm of the window size that the
+ * client must use to compress outgoing messages. The permitted range is 8
+ * to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB window.
+ * The default setting is 15.
+ *
+ * Mode Options:
+ * - accept: Accept whatever the remote endpoint offers.
+ * - decline: Decline any offers to deviate from the defaults
+ * - largest: Accept largest window size acceptable to both endpoints
+ * - smallest: Accept smallest window size acceptiable to both endpoints
+ *
+ * This setting is dependent on client support. A client may limit its own
+ * outgoing window size unilaterally. A server may only limit the client's
+ * window size if the remote client supports that feature.
+ *
+ * @param bits The size to request for the outgoing window size
+ * @param mode The mode to use for negotiating this parameter
+ * @return A status code
+ */
+ lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) {
+ if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
+ return error::make_error_code(error::invalid_max_window_bits);
+ }
+ m_client_max_window_bits = bits;
+ m_client_max_window_bits_mode = mode;
+
+ return lib::error_code();
+ }
+
+ /// Generate extension offer
+ /**
+ * Creates an offer string to include in the Sec-WebSocket-Extensions
+ * header of outgoing client requests.
+ *
+ * @return A WebSocket extension offer string for this extension
+ */
+ std::string generate_offer() const {
+ // TODO: this should be dynamically generated based on user settings
+ return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
+ }
+
+ /// Validate extension response
+ /**
+ * Confirm that the server has negotiated settings compatible with our
+ * original offer and apply those settings to the extension state.
+ *
+ * @param response The server response attribute list to validate
+ * @return Validation error or 0 on success
+ */
+ lib::error_code validate_offer(http::attribute_list const &) {
+ return lib::error_code();
+ }
+
+ /// Negotiate extension
+ /**
+ * Confirm that the client's extension negotiation offer has settings
+ * compatible with local policy. If so, generate a reply and apply those
+ * settings to the extension state.
+ *
+ * @param offer Attribute from client's offer
+ * @return Status code and value to return to remote endpoint
+ */
+ err_str_pair negotiate(http::attribute_list const & offer) {
+ err_str_pair ret;
+
+ http::attribute_list::const_iterator it;
+ for (it = offer.begin(); it != offer.end(); ++it) {
+ if (it->first == "server_no_context_takeover") {
+ negotiate_server_no_context_takeover(it->second,ret.first);
+ } else if (it->first == "client_no_context_takeover") {
+ negotiate_client_no_context_takeover(it->second,ret.first);
+ } else if (it->first == "server_max_window_bits") {
+ negotiate_server_max_window_bits(it->second,ret.first);
+ } else if (it->first == "client_max_window_bits") {
+ negotiate_client_max_window_bits(it->second,ret.first);
+ } else {
+ ret.first = make_error_code(error::invalid_attributes);
+ }
+
+ if (ret.first) {
+ break;
+ }
+ }
+
+ if (ret.first == lib::error_code()) {
+ m_enabled = true;
+ ret.second = generate_response();
+ }
+
+ return ret;
+ }
+
+ /// Compress bytes
+ /**
+ * @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames
+ * on 64 bit machines.
+ *
+ * @param [in] in String to compress
+ * @param [out] out String to append compressed bytes to
+ * @return Error or status code
+ */
+ lib::error_code compress(std::string const & in, std::string & out) {
+ if (!m_initialized) {
+ return make_error_code(error::uninitialized);
+ }
+
+ size_t output;
+
+ if (in.empty()) {
+ uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
+ out.append((char *)(buf),6);
+ return lib::error_code();
+ }
+
+ m_dstate.avail_in = in.size();
+ m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
+
+ do {
+ // Output to local buffer
+ m_dstate.avail_out = m_compress_buffer_size;
+ m_dstate.next_out = m_compress_buffer.get();
+
+ deflate(&m_dstate, m_flush);
+
+ output = m_compress_buffer_size - m_dstate.avail_out;
+
+ out.append((char *)(m_compress_buffer.get()),output);
+ } while (m_dstate.avail_out == 0);
+
+ return lib::error_code();
+ }
+
+ /// Decompress bytes
+ /**
+ * @param buf Byte buffer to decompress
+ * @param len Length of buf
+ * @param out String to append decompressed bytes to
+ * @return Error or status code
+ */
+ lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
+ out)
+ {
+ if (!m_initialized) {
+ return make_error_code(error::uninitialized);
+ }
+
+ int ret;
+
+ m_istate.avail_in = len;
+ m_istate.next_in = const_cast<unsigned char *>(buf);
+
+ do {
+ m_istate.avail_out = m_compress_buffer_size;
+ m_istate.next_out = m_compress_buffer.get();
+
+ ret = inflate(&m_istate, Z_SYNC_FLUSH);
+
+ if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
+ return make_error_code(error::zlib_error);
+ }
+
+ out.append(
+ reinterpret_cast<char *>(m_compress_buffer.get()),
+ m_compress_buffer_size - m_istate.avail_out
+ );
+ } while (m_istate.avail_out == 0);
+
+ return lib::error_code();
+ }
+private:
+ /// Generate negotiation response
+ /**
+ * @return Generate extension negotiation reponse string to send to client
+ */
+ std::string generate_response() {
+ std::string ret = "permessage-deflate";
+
+ if (m_server_no_context_takeover) {
+ ret += "; server_no_context_takeover";
+ }
+
+ if (m_client_no_context_takeover) {
+ ret += "; client_no_context_takeover";
+ }
+
+ if (m_server_max_window_bits < default_server_max_window_bits) {
+ std::stringstream s;
+ s << int(m_server_max_window_bits);
+ ret += "; server_max_window_bits="+s.str();
+ }
+
+ if (m_client_max_window_bits < default_client_max_window_bits) {
+ std::stringstream s;
+ s << int(m_client_max_window_bits);
+ ret += "; client_max_window_bits="+s.str();
+ }
+
+ return ret;
+ }
+
+ /// Negotiate server_no_context_takeover attribute
+ /**
+ * @param [in] value The value of the attribute from the offer
+ * @param [out] ec A reference to the error code to return errors via
+ */
+ void negotiate_server_no_context_takeover(std::string const & value,
+ lib::error_code & ec)
+ {
+ if (!value.empty()) {
+ ec = make_error_code(error::invalid_attribute_value);
+ return;
+ }
+
+ m_server_no_context_takeover = true;
+ }
+
+ /// Negotiate client_no_context_takeover attribute
+ /**
+ * @param [in] value The value of the attribute from the offer
+ * @param [out] ec A reference to the error code to return errors via
+ */
+ void negotiate_client_no_context_takeover(std::string const & value,
+ lib::error_code & ec)
+ {
+ if (!value.empty()) {
+ ec = make_error_code(error::invalid_attribute_value);
+ return;
+ }
+
+ m_client_no_context_takeover = true;
+ }
+
+ /// Negotiate server_max_window_bits attribute
+ /**
+ * When this method starts, m_server_max_window_bits will contain the server's
+ * preferred value and m_server_max_window_bits_mode will contain the mode the
+ * server wants to use to for negotiation. `value` contains the value the
+ * client requested that we use.
+ *
+ * options:
+ * - decline (refuse to use the attribute)
+ * - accept (use whatever the client says)
+ * - largest (use largest possible value)
+ * - smallest (use smallest possible value)
+ *
+ * @param [in] value The value of the attribute from the offer
+ * @param [out] ec A reference to the error code to return errors via
+ */
+ void negotiate_server_max_window_bits(std::string const & value,
+ lib::error_code & ec)
+ {
+ uint8_t bits = uint8_t(atoi(value.c_str()));
+
+ if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
+ ec = make_error_code(error::invalid_attribute_value);
+ m_server_max_window_bits = default_server_max_window_bits;
+ return;
+ }
+
+ switch (m_server_max_window_bits_mode) {
+ case mode::decline:
+ m_server_max_window_bits = default_server_max_window_bits;
+ break;
+ case mode::accept:
+ m_server_max_window_bits = bits;
+ break;
+ case mode::largest:
+ m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
+ break;
+ case mode::smallest:
+ m_server_max_window_bits = min_server_max_window_bits;
+ break;
+ default:
+ ec = make_error_code(error::invalid_mode);
+ m_server_max_window_bits = default_server_max_window_bits;
+ }
+ }
+
+ /// Negotiate client_max_window_bits attribute
+ /**
+ * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode
+ * will contain the server's preferred values for window size and
+ * negotiation mode.
+ *
+ * options:
+ * - decline (refuse to use the attribute)
+ * - accept (use whatever the client says)
+ * - largest (use largest possible value)
+ * - smallest (use smallest possible value)
+ *
+ * @param [in] value The value of the attribute from the offer
+ * @param [out] ec A reference to the error code to return errors via
+ */
+ void negotiate_client_max_window_bits(std::string const & value,
+ lib::error_code & ec)
+ {
+ uint8_t bits = uint8_t(atoi(value.c_str()));
+
+ if (value.empty()) {
+ bits = default_client_max_window_bits;
+ } else if (bits < min_client_max_window_bits ||
+ bits > max_client_max_window_bits)
+ {
+ ec = make_error_code(error::invalid_attribute_value);
+ m_client_max_window_bits = default_client_max_window_bits;
+ return;
+ }
+
+ switch (m_client_max_window_bits_mode) {
+ case mode::decline:
+ m_client_max_window_bits = default_client_max_window_bits;
+ break;
+ case mode::accept:
+ m_client_max_window_bits = bits;
+ break;
+ case mode::largest:
+ m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
+ break;
+ case mode::smallest:
+ m_client_max_window_bits = min_client_max_window_bits;
+ break;
+ default:
+ ec = make_error_code(error::invalid_mode);
+ m_client_max_window_bits = default_client_max_window_bits;
+ }
+ }
+
+ bool m_enabled;
+ bool m_server_no_context_takeover;
+ bool m_client_no_context_takeover;
+ uint8_t m_server_max_window_bits;
+ uint8_t m_client_max_window_bits;
+ mode::value m_server_max_window_bits_mode;
+ mode::value m_client_max_window_bits_mode;
+
+ bool m_initialized;
+ int m_flush;
+ size_t m_compress_buffer_size;
+ lib::unique_ptr_uchar_array m_compress_buffer;
+ z_stream m_dstate;
+ z_stream m_istate;
+};
+
+} // namespace permessage_deflate
+} // namespace extensions
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
diff --git a/websocketpp/frame.hpp b/websocketpp/frame.hpp
new file mode 100644
index 00000000..8a173375
--- /dev/null
+++ b/websocketpp/frame.hpp
@@ -0,0 +1,861 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_FRAME_HPP
+#define WEBSOCKETPP_FRAME_HPP
+
+#include <algorithm>
+#include <string>
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/network.hpp>
+
+#include <websocketpp/utilities.hpp>
+
+namespace websocketpp {
+/// Data structures and utility functions for manipulating WebSocket frames
+/**
+ * namespace frame provides a number of data structures and utility functions
+ * for reading, writing, and manipulating binary encoded WebSocket frames.
+ */
+namespace frame {
+
+/// Minimum length of a WebSocket frame header.
+static unsigned int const BASIC_HEADER_LENGTH = 2;
+/// Maximum length of a WebSocket header
+static unsigned int const MAX_HEADER_LENGTH = 14;
+/// Maximum length of the variable portion of the WebSocket header
+static unsigned int const MAX_EXTENDED_HEADER_LENGTH = 12;
+
+/// Two byte conversion union
+union uint16_converter {
+ uint16_t i;
+ uint8_t c[2];
+};
+
+/// Four byte conversion union
+union uint32_converter {
+ uint32_t i;
+ uint8_t c[4];
+};
+
+/// Eight byte conversion union
+union uint64_converter {
+ uint64_t i;
+ uint8_t c[8];
+};
+
+/// Constants and utility functions related to WebSocket opcodes
+/**
+ * WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
+ */
+namespace opcode {
+ enum value {
+ continuation = 0x0,
+ text = 0x1,
+ binary = 0x2,
+ rsv3 = 0x3,
+ rsv4 = 0x4,
+ rsv5 = 0x5,
+ rsv6 = 0x6,
+ rsv7 = 0x7,
+ close = 0x8,
+ ping = 0x9,
+ pong = 0xA,
+ control_rsvb = 0xB,
+ control_rsvc = 0xC,
+ control_rsvd = 0xD,
+ control_rsve = 0xE,
+ control_rsvf = 0xF,
+
+ CONTINUATION = 0x0,
+ TEXT = 0x1,
+ BINARY = 0x2,
+ RSV3 = 0x3,
+ RSV4 = 0x4,
+ RSV5 = 0x5,
+ RSV6 = 0x6,
+ RSV7 = 0x7,
+ CLOSE = 0x8,
+ PING = 0x9,
+ PONG = 0xA,
+ CONTROL_RSVB = 0xB,
+ CONTROL_RSVC = 0xC,
+ CONTROL_RSVD = 0xD,
+ CONTROL_RSVE = 0xE,
+ CONTROL_RSVF = 0xF
+ };
+
+ /// Check if an opcode is reserved
+ /**
+ * @param v The opcode to test.
+ * @return Whether or not the opcode is reserved.
+ */
+ inline bool reserved(value v) {
+ return (v >= rsv3 && v <= rsv7) ||
+ (v >= control_rsvb && v <= control_rsvf);
+ }
+
+ /// Check if an opcode is invalid
+ /**
+ * Invalid opcodes are negative or require greater than 4 bits to store.
+ *
+ * @param v The opcode to test.
+ * @return Whether or not the opcode is invalid.
+ */
+ inline bool invalid(value v) {
+ return (v > 0xF || v < 0);
+ }
+
+ /// Check if an opcode is for a control frame
+ /**
+ * @param v The opcode to test.
+ * @return Whether or not the opcode is a control opcode.
+ */
+ inline bool is_control(value v) {
+ return v >= 0x8;
+ }
+}
+
+/// Constants related to frame and payload limits
+namespace limits {
+ /// Minimum length of a WebSocket frame header.
+ static unsigned int const basic_header_length = 2;
+
+ /// Maximum length of a WebSocket header
+ static unsigned int const max_header_length = 14;
+
+ /// Maximum length of the variable portion of the WebSocket header
+ static unsigned int const max_extended_header_length = 12;
+
+ /// Maximum size of a basic WebSocket payload
+ static uint8_t const payload_size_basic = 125;
+
+ /// Maximum size of an extended WebSocket payload (basic payload = 126)
+ static uint16_t const payload_size_extended = 0xFFFF; // 2^16, 65535
+
+ /// Maximum size of a jumbo WebSocket payload (basic payload = 127)
+ static uint64_t const payload_size_jumbo = 0x7FFFFFFFFFFFFFFFLL;//2^63
+
+ /// Maximum size of close frame reason
+ /**
+ * This is payload_size_basic - 2 bytes (as first two bytes are used for
+ * the close code
+ */
+ static uint8_t const close_reason_size = 123;
+}
+
+
+// masks for fields in the basic header
+static uint8_t const BHB0_OPCODE = 0x0F;
+static uint8_t const BHB0_RSV3 = 0x10;
+static uint8_t const BHB0_RSV2 = 0x20;
+static uint8_t const BHB0_RSV1 = 0x40;
+static uint8_t const BHB0_FIN = 0x80;
+
+static uint8_t const BHB1_PAYLOAD = 0x7F;
+static uint8_t const BHB1_MASK = 0x80;
+
+static uint8_t const payload_size_code_16bit = 0x7E; // 126
+static uint8_t const payload_size_code_64bit = 0x7F; // 127
+
+typedef uint32_converter masking_key_type;
+
+/// The constant size component of a WebSocket frame header
+struct basic_header {
+ basic_header() : b0(0x00),b1(0x00) {}
+
+ basic_header(uint8_t p0, uint8_t p1) : b0(p0), b1(p1) {}
+
+ basic_header(opcode::value op, uint64_t size, bool fin, bool mask,
+ bool rsv1 = false, bool rsv2 = false, bool rsv3 = false) : b0(0x00),
+ b1(0x00)
+ {
+ if (fin) {
+ b0 |= BHB0_FIN;
+ }
+ if (rsv1) {
+ b0 |= BHB0_RSV1;
+ }
+ if (rsv2) {
+ b0 |= BHB0_RSV2;
+ }
+ if (rsv3) {
+ b0 |= BHB0_RSV3;
+ }
+ b0 |= (op & BHB0_OPCODE);
+
+ if (mask) {
+ b1 |= BHB1_MASK;
+ }
+
+ uint8_t basic_value;
+
+ if (size <= limits::payload_size_basic) {
+ basic_value = static_cast<uint8_t>(size);
+ } else if (size <= limits::payload_size_extended) {
+ basic_value = payload_size_code_16bit;
+ } else {
+ basic_value = payload_size_code_64bit;
+ }
+
+
+ b1 |= basic_value;
+ }
+
+ uint8_t b0;
+ uint8_t b1;
+};
+
+/// The variable size component of a WebSocket frame header
+struct extended_header {
+ extended_header() {
+ std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
+ }
+
+ extended_header(uint64_t payload_size) {
+ std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
+
+ copy_payload(payload_size);
+ }
+
+ extended_header(uint64_t payload_size, uint32_t masking_key) {
+ std::fill_n(this->bytes,MAX_EXTENDED_HEADER_LENGTH,0x00);
+
+ // Copy payload size
+ int offset = copy_payload(payload_size);
+
+ // Copy Masking Key
+ uint32_converter temp32;
+ temp32.i = masking_key;
+ std::copy(temp32.c,temp32.c+4,bytes+offset);
+ }
+
+ uint8_t bytes[MAX_EXTENDED_HEADER_LENGTH];
+private:
+ int copy_payload(uint64_t payload_size) {
+ int payload_offset = 0;
+
+ if (payload_size <= limits::payload_size_basic) {
+ payload_offset = 8;
+ } else if (payload_size <= limits::payload_size_extended) {
+ payload_offset = 6;
+ }
+
+ uint64_converter temp64;
+ temp64.i = lib::net::_htonll(payload_size);
+ std::copy(temp64.c+payload_offset,temp64.c+8,bytes);
+
+ return 8-payload_offset;
+ }
+};
+
+bool get_fin(basic_header const &h);
+void set_fin(basic_header &h, bool value);
+bool get_rsv1(basic_header const &h);
+void set_rsv1(basic_header &h, bool value);
+bool get_rsv2(basic_header const &h);
+void set_rsv2(basic_header &h, bool value);
+bool get_rsv3(basic_header const &h);
+void set_rsv3(basic_header &h, bool value);
+opcode::value get_opcode(basic_header const &h);
+bool get_masked(basic_header const &h);
+void set_masked(basic_header &h, bool value);
+uint8_t get_basic_size(basic_header const &);
+size_t get_header_len(basic_header const &);
+unsigned int get_masking_key_offset(basic_header const &);
+
+std::string write_header(basic_header const &, extended_header const &);
+masking_key_type get_masking_key(basic_header const &, extended_header const &);
+uint16_t get_extended_size(extended_header const &);
+uint64_t get_jumbo_size(extended_header const &);
+uint64_t get_payload_size(basic_header const &, extended_header const &);
+
+size_t prepare_masking_key(masking_key_type const & key);
+size_t circshift_prepared_key(size_t prepared_key, size_t offset);
+
+// Functions for performing xor based masking and unmasking
+template <typename input_iter, typename output_iter>
+void byte_mask(input_iter b, input_iter e, output_iter o, masking_key_type
+ const & key, size_t key_offset = 0);
+template <typename iter_type>
+void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
+ size_t key_offset = 0);
+void word_mask_exact(uint8_t * input, uint8_t * output, size_t length,
+ masking_key_type const & key);
+void word_mask_exact(uint8_t * data, size_t length, masking_key_type const &
+ key);
+size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
+ size_t prepared_key);
+size_t word_mask_circ(uint8_t * data, size_t length, size_t prepared_key);
+
+/// Check whether the frame's FIN bit is set.
+/**
+ * @param [in] h The basic header to extract from.
+ * @return True if the header's fin bit is set.
+ */
+inline bool get_fin(basic_header const & h) {
+ return ((h.b0 & BHB0_FIN) == BHB0_FIN);
+}
+
+/// Set the frame's FIN bit
+/**
+ * @param [out] h Header to set.
+ * @param [in] value Value to set it to.
+ */
+inline void set_fin(basic_header & h, bool value) {
+ h.b0 = (value ? h.b0 | BHB0_FIN : h.b0 & ~BHB0_FIN);
+}
+
+/// check whether the frame's RSV1 bit is set
+/**
+ * @param [in] h The basic header to extract from.
+ * @return True if the header's RSV1 bit is set.
+ */
+inline bool get_rsv1(const basic_header &h) {
+ return ((h.b0 & BHB0_RSV1) == BHB0_RSV1);
+}
+
+/// Set the frame's RSV1 bit
+/**
+ * @param [out] h Header to set.
+ * @param [in] value Value to set it to.
+ */
+inline void set_rsv1(basic_header &h, bool value) {
+ h.b0 = (value ? h.b0 | BHB0_RSV1 : h.b0 & ~BHB0_RSV1);
+}
+
+/// check whether the frame's RSV2 bit is set
+/**
+ * @param [in] h The basic header to extract from.
+ * @return True if the header's RSV2 bit is set.
+ */
+inline bool get_rsv2(const basic_header &h) {
+ return ((h.b0 & BHB0_RSV2) == BHB0_RSV2);
+}
+
+/// Set the frame's RSV2 bit
+/**
+ * @param [out] h Header to set.
+ * @param [in] value Value to set it to.
+ */
+inline void set_rsv2(basic_header &h, bool value) {
+ h.b0 = (value ? h.b0 | BHB0_RSV2 : h.b0 & ~BHB0_RSV2);
+}
+
+/// check whether the frame's RSV3 bit is set
+/**
+ * @param [in] h The basic header to extract from.
+ * @return True if the header's RSV3 bit is set.
+ */
+inline bool get_rsv3(const basic_header &h) {
+ return ((h.b0 & BHB0_RSV3) == BHB0_RSV3);
+}
+
+/// Set the frame's RSV3 bit
+/**
+ * @param [out] h Header to set.
+ * @param [in] value Value to set it to.
+ */
+inline void set_rsv3(basic_header &h, bool value) {
+ h.b0 = (value ? h.b0 | BHB0_RSV3 : h.b0 & ~BHB0_RSV3);
+}
+
+/// Extract opcode from basic header
+/**
+ * @param [in] h The basic header to extract from.
+ * @return The opcode value of the header.
+ */
+inline opcode::value get_opcode(const basic_header &h) {
+ return opcode::value(h.b0 & BHB0_OPCODE);
+}
+
+/// check whether the frame is masked
+/**
+ * @param [in] h The basic header to extract from.
+ * @return True if the header mask bit is set.
+ */
+inline bool get_masked(basic_header const & h) {
+ return ((h.b1 & BHB1_MASK) == BHB1_MASK);
+}
+
+/// Set the frame's MASK bit
+/**
+ * @param [out] h Header to set.
+ * @param value Value to set it to.
+ */
+inline void set_masked(basic_header & h, bool value) {
+ h.b1 = (value ? h.b1 | BHB1_MASK : h.b1 & ~BHB1_MASK);
+}
+
+/// Extracts the raw payload length specified in the basic header
+/**
+ * A basic WebSocket frame header contains a 7 bit value that represents the
+ * payload size. There are two reserved values that are used to indicate that
+ * the actual payload size will not fit in 7 bits and that the full payload
+ * size is included in a separate field. The values are as follows:
+ *
+ * PAYLOAD_SIZE_CODE_16BIT (0x7E) indicates that the actual payload is less
+ * than 16 bit
+ *
+ * PAYLOAD_SIZE_CODE_64BIT (0x7F) indicates that the actual payload is less
+ * than 63 bit
+ *
+ * @param [in] h Basic header to read value from.
+ * @return The exact size encoded in h.
+ */
+inline uint8_t get_basic_size(const basic_header &h) {
+ return h.b1 & BHB1_PAYLOAD;
+}
+
+/// Calculates the full length of the header based on the first bytes.
+/**
+ * A WebSocket frame header always has at least two bytes. Encoded within the
+ * first two bytes is all the information necessary to calculate the full
+ * (variable) header length. get_header_len() calculates the full header
+ * length for the given two byte basic header.
+ *
+ * @param h Basic frame header to extract size from.
+ * @return Full length of the extended header.
+ */
+inline size_t get_header_len(basic_header const & h) {
+ // TODO: check extensions?
+
+ // masking key offset represents the space used for the extended length
+ // fields
+ size_t size = BASIC_HEADER_LENGTH + get_masking_key_offset(h);
+
+ // If the header is masked there is a 4 byte masking key
+ if (get_masked(h)) {
+ size += 4;
+ }
+
+ return size;
+}
+
+/// Calculate the offset location of the masking key within the extended header
+/**
+ * Calculate the offset location of the masking key within the extended header
+ * using information from its corresponding basic header
+ *
+ * @param h Corresponding basic header to calculate from.
+ *
+ * @return byte offset of the first byte of the masking key
+ */
+inline unsigned int get_masking_key_offset(const basic_header &h) {
+ if (get_basic_size(h) == payload_size_code_16bit) {
+ return 2;
+ } else if (get_basic_size(h) == payload_size_code_64bit) {
+ return 8;
+ } else {
+ return 0;
+ }
+}
+
+/// Generate a properly sized contiguous string that encodes a full frame header
+/**
+ * Copy the basic header h and extended header e into a properly sized
+ * contiguous frame header string for the purposes of writing out to the wire.
+ *
+ * @param h The basic header to include
+ * @param e The extended header to include
+ *
+ * @return A contiguous string containing h and e
+ */
+inline std::string prepare_header(const basic_header &h, const
+ extended_header &e)
+{
+ std::string ret;
+
+ ret.push_back(char(h.b0));
+ ret.push_back(char(h.b1));
+ ret.append(
+ reinterpret_cast<const char*>(e.bytes),
+ get_header_len(h)-BASIC_HEADER_LENGTH
+ );
+
+ return ret;
+}
+
+/// Extract the masking key from a frame header
+/**
+ * Note that while read and written as an integer at times, this value is not
+ * an integer and should never be interpreted as one. Big and little endian
+ * machines will generate and store masking keys differently without issue as
+ * long as the integer values remain irrelivant.
+ *
+ * @param h The basic header to extract from
+ * @param e The extended header to extract from
+ *
+ * @return The masking key as an integer.
+ */
+inline masking_key_type get_masking_key(const basic_header &h, const
+ extended_header &e)
+{
+ masking_key_type temp32;
+
+ if (!get_masked(h)) {
+ temp32.i = 0;
+ } else {
+ unsigned int offset = get_masking_key_offset(h);
+ std::copy(e.bytes+offset,e.bytes+offset+4,temp32.c);
+ }
+
+ return temp32;
+}
+
+/// Extract the extended size field from an extended header
+/**
+ * It is the responsibility of the caller to verify that e is a valid extended
+ * header. This function assumes that e contains an extended payload size.
+ *
+ * @param e The extended header to extract from
+ *
+ * @return The size encoded in the extended header in host byte order
+ */
+inline uint16_t get_extended_size(const extended_header &e) {
+ uint16_converter temp16;
+ std::copy(e.bytes,e.bytes+2,temp16.c);
+ return ntohs(temp16.i);
+}
+
+/// Extract the jumbo size field from an extended header
+/**
+ * It is the responsibility of the caller to verify that e is a valid extended
+ * header. This function assumes that e contains a jumbo payload size.
+ *
+ * @param e The extended header to extract from
+ *
+ * @return The size encoded in the extended header in host byte order
+ */
+inline uint64_t get_jumbo_size(const extended_header &e) {
+ uint64_converter temp64;
+ std::copy(e.bytes,e.bytes+8,temp64.c);
+ return lib::net::_ntohll(temp64.i);
+}
+
+/// Extract the full payload size field from a WebSocket header
+/**
+ * It is the responsibility of the caller to verify that h and e together
+ * represent a valid WebSocket frame header. This function assumes only that h
+ * and e are valid. It uses information in the basic header to determine where
+ * to look for the payload_size
+ *
+ * @param h The basic header to extract from
+ * @param e The extended header to extract from
+ *
+ * @return The size encoded in the combined header in host byte order.
+ */
+inline uint64_t get_payload_size(const basic_header &h, const
+ extended_header &e)
+{
+ uint8_t val = get_basic_size(h);
+
+ if (val <= limits::payload_size_basic) {
+ return val;
+ } else if (val == payload_size_code_16bit) {
+ return get_extended_size(e);
+ } else {
+ return get_jumbo_size(e);
+ }
+}
+
+/// Extract a masking key into a value the size of a machine word.
+/**
+ * Machine word size must be 4 or 8.
+ *
+ * @param key Masking key to extract from
+ *
+ * @return prepared key as a machine word
+ */
+inline size_t prepare_masking_key(const masking_key_type& key) {
+ size_t low_bits = static_cast<size_t>(key.i);
+
+ if (sizeof(size_t) == 8) {
+ uint64_t high_bits = static_cast<size_t>(key.i);
+ return static_cast<size_t>((high_bits << 32) | low_bits);
+ } else {
+ return low_bits;
+ }
+}
+
+/// circularly shifts the supplied prepared masking key by offset bytes
+/**
+ * Prepared_key must be the output of prepare_masking_key with the associated
+ * restrictions on the machine word size. offset must be greater than or equal
+ * to zero and less than sizeof(size_t).
+ */
+inline size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
+ if (lib::net::is_little_endian()) {
+ size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
+ return (prepared_key >> offset*8) | temp;
+ } else {
+ size_t temp = prepared_key >> (sizeof(size_t)-offset)*8;
+ return (prepared_key << offset*8) | temp;
+ }
+}
+
+/// Byte by byte mask/unmask
+/**
+ * Iterator based byte by byte masking and unmasking for WebSocket payloads.
+ * Performs masking in place using the supplied key offset by the supplied
+ * offset number of bytes.
+ *
+ * This function is simple and can be done in place on input with arbitrary
+ * lengths and does not vary based on machine word size. It is slow.
+ *
+ * @param b Beginning iterator to start masking
+ *
+ * @param e Ending iterator to end masking
+ *
+ * @param o Beginning iterator to store masked results
+ *
+ * @param key 32 bit key to mask with.
+ *
+ * @param key_offset offset value to start masking at.
+ */
+template <typename input_iter, typename output_iter>
+void byte_mask(input_iter first, input_iter last, output_iter result,
+ masking_key_type const & key, size_t key_offset)
+{
+ size_t key_index = key_offset%4;
+ while (first != last) {
+ *result = *first ^ key.c[key_index++];
+ key_index %= 4;
+ ++result;
+ ++first;
+ }
+}
+
+/// Byte by byte mask/unmask (in place)
+/**
+ * Iterator based byte by byte masking and unmasking for WebSocket payloads.
+ * Performs masking in place using the supplied key offset by the supplied
+ * offset number of bytes.
+ *
+ * This function is simple and can be done in place on input with arbitrary
+ * lengths and does not vary based on machine word size. It is slow.
+ *
+ * @param b Beginning iterator to start masking
+ *
+ * @param e Ending iterator to end masking
+ *
+ * @param key 32 bit key to mask with.
+ *
+ * @param key_offset offset value to start masking at.
+ */
+template <typename iter_type>
+void byte_mask(iter_type b, iter_type e, masking_key_type const & key,
+ size_t key_offset)
+{
+ byte_mask(b,e,b,key,key_offset);
+}
+
+/// Exact word aligned mask/unmask
+/**
+ * Balanced combination of byte by byte and circular word by word masking.
+ * Best used to mask complete messages at once. Has much higher setup costs than
+ * word_mask_circ but works with exact sized buffers.
+ *
+ * Buffer based word by word masking and unmasking for WebSocket payloads.
+ * Masking is done in word by word chunks with the remainder not divisible by
+ * the word size done byte by byte.
+ *
+ * input and output must both be at least length bytes. Exactly length bytes
+ * will be written.
+ *
+ * @param input buffer to mask or unmask
+ *
+ * @param output buffer to store the output. May be the same as input.
+ *
+ * @param length length of data buffer
+ *
+ * @param key Masking key to use
+ */
+inline void word_mask_exact(uint8_t* input, uint8_t* output, size_t length,
+ const masking_key_type& key)
+{
+ size_t prepared_key = prepare_masking_key(key);
+ size_t n = length/sizeof(size_t);
+ size_t* input_word = reinterpret_cast<size_t*>(input);
+ size_t* output_word = reinterpret_cast<size_t*>(output);
+
+ for (size_t i = 0; i < n; i++) {
+ output_word[i] = input_word[i] ^ prepared_key;
+ }
+
+ for (size_t i = n*sizeof(size_t); i < length; i++) {
+ output[i] = input[i] ^ key.c[i%4];
+ }
+}
+
+/// Exact word aligned mask/unmask (in place)
+/**
+ * In place version of word_mask_exact
+ *
+ * @see word_mask_exact
+ *
+ * @param data buffer to read and write from
+ *
+ * @param length length of data buffer
+ *
+ * @param key Masking key to use
+ */
+inline void word_mask_exact(uint8_t* data, size_t length, const
+ masking_key_type& key)
+{
+ word_mask_exact(data,data,length,key);
+}
+
+/// Circular word aligned mask/unmask
+/**
+ * Performs a circular mask/unmask in word sized chunks using pre-prepared keys
+ * that store state between calls. Best for providing streaming masking or
+ * unmasking of small chunks at a time of a larger message. Requires that the
+ * underlying allocated size of the data buffer be a multiple of the word size.
+ * Data in the buffer after `length` will be overwritten only with the same
+ * values that were originally present.
+ *
+ * Buffer based word by word masking and unmasking for WebSocket payloads.
+ * Performs masking in place using the supplied key. Casts the data buffer to
+ * an array of size_t's and performs masking word by word. The underlying
+ * buffer size must be a muliple of the word size.
+ *
+ * word_mask returns a copy of prepared_key circularly shifted based on the
+ * length value. The returned value may be fed back into word_mask when more
+ * data is available.
+ *
+ * input and output must both have length at least:
+ * ceil(length/sizeof(size_t))*sizeof(size_t)
+ * Exactly that many bytes will be written, although only exactly length bytes
+ * will be changed (trailing bytes will be replaced without masking)
+ *
+ * @param data Character buffer to mask
+ *
+ * @param length Length of data
+ *
+ * @param prepared_key Prepared key to use.
+ *
+ * @return the prepared_key shifted to account for the input length
+ */
+inline size_t word_mask_circ(uint8_t * input, uint8_t * output, size_t length,
+ size_t prepared_key)
+{
+ size_t n = length / sizeof(size_t); // whole words
+ size_t l = length - (n * sizeof(size_t)); // remaining bytes
+ size_t * input_word = reinterpret_cast<size_t *>(input);
+ size_t * output_word = reinterpret_cast<size_t *>(output);
+
+ // mask word by word
+ for (size_t i = 0; i < n; i++) {
+ output_word[i] = input_word[i] ^ prepared_key;
+ }
+
+ // mask partial word at the end
+ size_t start = length - l;
+ uint8_t * byte_key = reinterpret_cast<uint8_t *>(&prepared_key);
+ for (size_t i = 0; i < l; ++i) {
+ output[start+i] = input[start+i] ^ byte_key[i];
+ }
+
+ return circshift_prepared_key(prepared_key,l);
+}
+
+/// Circular word aligned mask/unmask (in place)
+/**
+ * In place version of word_mask_circ
+ *
+ * @see word_mask_circ
+ *
+ * @param data Character buffer to read from and write to
+ *
+ * @param length Length of data
+ *
+ * @param prepared_key Prepared key to use.
+ *
+ * @return the prepared_key shifted to account for the input length
+ */
+inline size_t word_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
+ return word_mask_circ(data,data,length,prepared_key);
+}
+
+/// Circular byte aligned mask/unmask
+/**
+ * Performs a circular mask/unmask in byte sized chunks using pre-prepared keys
+ * that store state between calls. Best for providing streaming masking or
+ * unmasking of small chunks at a time of a larger message. Requires that the
+ * underlying allocated size of the data buffer be a multiple of the word size.
+ * Data in the buffer after `length` will be overwritten only with the same
+ * values that were originally present.
+ *
+ * word_mask returns a copy of prepared_key circularly shifted based on the
+ * length value. The returned value may be fed back into byte_mask when more
+ * data is available.
+ *
+ * @param data Character buffer to mask
+ *
+ * @param length Length of data
+ *
+ * @param prepared_key Prepared key to use.
+ *
+ * @return the prepared_key shifted to account for the input length
+ */
+inline size_t byte_mask_circ(uint8_t * input, uint8_t * output, size_t length,
+ size_t prepared_key)
+{
+ uint32_converter key;
+ key.i = prepared_key;
+
+ for (size_t i = 0; i < length; ++i) {
+ output[i] = input[i] ^ key.c[i % 4];
+ }
+
+ return circshift_prepared_key(prepared_key,length % 4);
+}
+
+/// Circular byte aligned mask/unmask (in place)
+/**
+ * In place version of byte_mask_circ
+ *
+ * @see byte_mask_circ
+ *
+ * @param data Character buffer to read from and write to
+ *
+ * @param length Length of data
+ *
+ * @param prepared_key Prepared key to use.
+ *
+ * @return the prepared_key shifted to account for the input length
+ */
+inline size_t byte_mask_circ(uint8_t* data, size_t length, size_t prepared_key){
+ return byte_mask_circ(data,data,length,prepared_key);
+}
+
+} // namespace frame
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_FRAME_HPP
diff --git a/websocketpp/http/constants.hpp b/websocketpp/http/constants.hpp
new file mode 100644
index 00000000..f946cb31
--- /dev/null
+++ b/websocketpp/http/constants.hpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_CONSTANTS_HPP
+#define HTTP_CONSTANTS_HPP
+
+#include <exception>
+#include <map>
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace websocketpp {
+/// HTTP handling support
+namespace http {
+ /// The type of an HTTP attribute list
+ /**
+ * The attribute list is an unordered key/value map. Encoded attribute
+ * values are delimited by semicolons.
+ */
+ typedef std::map<std::string,std::string> attribute_list;
+
+ /// The type of an HTTP parameter list
+ /**
+ * The parameter list is an ordered pairing of a parameter and its
+ * associated attribute list. Encoded parameter values are delimited by
+ * commas.
+ */
+ typedef std::vector< std::pair<std::string,attribute_list> > parameter_list;
+
+ /// Literal value of the HTTP header delimiter
+ static char const header_delimiter[] = "\r\n";
+
+ /// Literal value of the HTTP header separator
+ static char const header_separator[] = ":";
+
+ /// Literal value of an empty header
+ static std::string const empty_header;
+
+ /// Maximum size in bytes before rejecting an HTTP header as too big.
+ size_t const max_header_size = 16000;
+
+ /// Default Maximum size in bytes for HTTP message bodies.
+ size_t const max_body_size = 32000000;
+
+ /// Number of bytes to use for temporary istream read buffers
+ size_t const istream_buffer = 512;
+
+ /// invalid HTTP token characters
+ /**
+ * 0x00 - 0x32, 0x7f-0xff
+ * ( ) < > @ , ; : \ " / [ ] ? = { }
+ */
+ static char const header_token[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..0f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 10..1f
+ 0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0, // 20..2f
+ 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 30..3f
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 40..4f
+ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1, // 50..5f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 60..6f
+ 1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0, // 70..7f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 80..8f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 90..9f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // a0..af
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // b0..bf
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // c0..cf
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // d0..df
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // e0..ef
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // f0..ff
+ };
+
+ /// Is the character a token
+ inline bool is_token_char(unsigned char c) {
+ return (header_token[c] == 1);
+ }
+
+ /// Is the character a non-token
+ inline bool is_not_token_char(unsigned char c) {
+ return !header_token[c];
+ }
+
+ /// Is the character whitespace
+ /**
+ * whitespace is space (32) or horizontal tab (9)
+ */
+ inline bool is_whitespace_char(unsigned char c) {
+ return (c == 9 || c == 32);
+ }
+
+ /// Is the character non-whitespace
+ inline bool is_not_whitespace_char(unsigned char c) {
+ return (c != 9 && c != 32);
+ }
+
+ /// HTTP Status codes
+ namespace status_code {
+ enum value {
+ uninitialized = 0,
+
+ continue_code = 100,
+ switching_protocols = 101,
+
+ ok = 200,
+ created = 201,
+ accepted = 202,
+ non_authoritative_information = 203,
+ no_content = 204,
+ reset_content = 205,
+ partial_content = 206,
+
+ multiple_choices = 300,
+ moved_permanently = 301,
+ found = 302,
+ see_other = 303,
+ not_modified = 304,
+ use_proxy = 305,
+ temporary_redirect = 307,
+
+ bad_request = 400,
+ unauthorized = 401,
+ payment_required = 402,
+ forbidden = 403,
+ not_found = 404,
+ method_not_allowed = 405,
+ not_acceptable = 406,
+ proxy_authentication_required = 407,
+ request_timeout = 408,
+ conflict = 409,
+ gone = 410,
+ length_required = 411,
+ precondition_failed = 412,
+ request_entity_too_large = 413,
+ request_uri_too_long = 414,
+ unsupported_media_type = 415,
+ request_range_not_satisfiable = 416,
+ expectation_failed = 417,
+ im_a_teapot = 418,
+ upgrade_required = 426,
+ precondition_required = 428,
+ too_many_requests = 429,
+ request_header_fields_too_large = 431,
+
+ internal_server_error = 500,
+ not_implemented = 501,
+ bad_gateway = 502,
+ service_unavailable = 503,
+ gateway_timeout = 504,
+ http_version_not_supported = 505,
+ not_extended = 510,
+ network_authentication_required = 511
+ };
+
+ // TODO: should this be inline?
+ inline std::string get_string(value c) {
+ switch (c) {
+ case uninitialized:
+ return "Uninitialized";
+ case continue_code:
+ return "Continue";
+ case switching_protocols:
+ return "Switching Protocols";
+ case ok:
+ return "OK";
+ case created:
+ return "Created";
+ case accepted:
+ return "Accepted";
+ case non_authoritative_information:
+ return "Non Authoritative Information";
+ case no_content:
+ return "No Content";
+ case reset_content:
+ return "Reset Content";
+ case partial_content:
+ return "Partial Content";
+ case multiple_choices:
+ return "Multiple Choices";
+ case moved_permanently:
+ return "Moved Permanently";
+ case found:
+ return "Found";
+ case see_other:
+ return "See Other";
+ case not_modified:
+ return "Not Modified";
+ case use_proxy:
+ return "Use Proxy";
+ case temporary_redirect:
+ return "Temporary Redirect";
+ case bad_request:
+ return "Bad Request";
+ case unauthorized:
+ return "Unauthorized";
+ case payment_required:
+ return "Payment Required";
+ case forbidden:
+ return "Forbidden";
+ case not_found:
+ return "Not Found";
+ case method_not_allowed:
+ return "Method Not Allowed";
+ case not_acceptable:
+ return "Not Acceptable";
+ case proxy_authentication_required:
+ return "Proxy Authentication Required";
+ case request_timeout:
+ return "Request Timeout";
+ case conflict:
+ return "Conflict";
+ case gone:
+ return "Gone";
+ case length_required:
+ return "Length Required";
+ case precondition_failed:
+ return "Precondition Failed";
+ case request_entity_too_large:
+ return "Request Entity Too Large";
+ case request_uri_too_long:
+ return "Request-URI Too Long";
+ case unsupported_media_type:
+ return "Unsupported Media Type";
+ case request_range_not_satisfiable:
+ return "Requested Range Not Satisfiable";
+ case expectation_failed:
+ return "Expectation Failed";
+ case im_a_teapot:
+ return "I'm a teapot";
+ case upgrade_required:
+ return "Upgrade Required";
+ case precondition_required:
+ return "Precondition Required";
+ case too_many_requests:
+ return "Too Many Requests";
+ case request_header_fields_too_large:
+ return "Request Header Fields Too Large";
+ case internal_server_error:
+ return "Internal Server Error";
+ case not_implemented:
+ return "Not Implemented";
+ case bad_gateway:
+ return "Bad Gateway";
+ case service_unavailable:
+ return "Service Unavailable";
+ case gateway_timeout:
+ return "Gateway Timeout";
+ case http_version_not_supported:
+ return "HTTP Version Not Supported";
+ case not_extended:
+ return "Not Extended";
+ case network_authentication_required:
+ return "Network Authentication Required";
+ default:
+ return "Unknown";
+ }
+ }
+ }
+
+ class exception : public std::exception {
+ public:
+ exception(const std::string& log_msg,
+ status_code::value error_code,
+ const std::string& error_msg = std::string(),
+ const std::string& body = std::string())
+ : m_msg(log_msg)
+ , m_error_msg(error_msg)
+ , m_body(body)
+ , m_error_code(error_code) {}
+
+ ~exception() throw() {}
+
+ virtual const char* what() const throw() {
+ return m_msg.c_str();
+ }
+
+ std::string m_msg;
+ std::string m_error_msg;
+ std::string m_body;
+ status_code::value m_error_code;
+ };
+}
+}
+
+#endif // HTTP_CONSTANTS_HPP
diff --git a/websocketpp/http/impl/parser.hpp b/websocketpp/http/impl/parser.hpp
new file mode 100644
index 00000000..1d59b938
--- /dev/null
+++ b/websocketpp/http/impl/parser.hpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_IMPL_HPP
+#define HTTP_PARSER_IMPL_HPP
+
+#include <algorithm>
+#include <cstdlib>
+#include <istream>
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+inline void parser::set_version(std::string const & version) {
+ m_version = version;
+}
+
+inline std::string const & parser::get_header(std::string const & key) const {
+ header_list::const_iterator h = m_headers.find(key);
+
+ if (h == m_headers.end()) {
+ return empty_header;
+ } else {
+ return h->second;
+ }
+}
+
+inline bool parser::get_header_as_plist(std::string const & key,
+ parameter_list & out) const
+{
+ header_list::const_iterator it = m_headers.find(key);
+
+ if (it == m_headers.end() || it->second.size() == 0) {
+ return false;
+ }
+
+ return this->parse_parameter_list(it->second,out);
+}
+
+inline void parser::append_header(std::string const & key, std::string const &
+ val)
+{
+ if (std::find_if(key.begin(),key.end(),is_not_token_char) != key.end()) {
+ throw exception("Invalid header name",status_code::bad_request);
+ }
+
+ if (this->get_header(key).empty()) {
+ m_headers[key] = val;
+ } else {
+ m_headers[key] += ", " + val;
+ }
+}
+
+inline void parser::replace_header(std::string const & key, std::string const &
+ val)
+{
+ m_headers[key] = val;
+}
+
+inline void parser::remove_header(std::string const & key) {
+ m_headers.erase(key);
+}
+
+inline void parser::set_body(std::string const & value) {
+ if (value.size() == 0) {
+ remove_header("Content-Length");
+ m_body.clear();
+ return;
+ }
+
+ // TODO: should this method respect the max size? If so how should errors
+ // be indicated?
+
+ std::stringstream len;
+ len << value.size();
+ replace_header("Content-Length", len.str());
+ m_body = value;
+}
+
+inline bool parser::parse_parameter_list(std::string const & in,
+ parameter_list & out) const
+{
+ if (in.size() == 0) {
+ return false;
+ }
+
+ std::string::const_iterator it;
+ it = extract_parameters(in.begin(),in.end(),out);
+ return (it == in.begin());
+}
+
+inline bool parser::prepare_body() {
+ if (!get_header("Content-Length").empty()) {
+ std::string const & cl_header = get_header("Content-Length");
+ char * end;
+
+ // TODO: not 100% sure what the compatibility of this method is. Also,
+ // I believe this will only work up to 32bit sizes. Is there a need for
+ // > 4GiB HTTP payloads?
+ m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10);
+
+ if (m_body_bytes_needed > m_body_bytes_max) {
+ throw exception("HTTP message body too large",
+ status_code::request_entity_too_large);
+ }
+
+ m_body_encoding = body_encoding::plain;
+ return true;
+ } else if (get_header("Transfer-Encoding") == "chunked") {
+ // TODO
+ //m_body_encoding = body_encoding::chunked;
+ return false;
+ } else {
+ return false;
+ }
+}
+
+inline size_t parser::process_body(char const * buf, size_t len) {
+ if (m_body_encoding == body_encoding::plain) {
+ size_t processed = (std::min)(m_body_bytes_needed,len);
+ m_body.append(buf,processed);
+ m_body_bytes_needed -= processed;
+ return processed;
+ } else if (m_body_encoding == body_encoding::chunked) {
+ // TODO:
+ throw exception("Unexpected body encoding",
+ status_code::internal_server_error);
+ } else {
+ throw exception("Unexpected body encoding",
+ status_code::internal_server_error);
+ }
+}
+
+inline void parser::process_header(std::string::iterator begin,
+ std::string::iterator end)
+{
+ std::string::iterator cursor = std::search(
+ begin,
+ end,
+ header_separator,
+ header_separator + sizeof(header_separator) - 1
+ );
+
+ if (cursor == end) {
+ throw exception("Invalid header line",status_code::bad_request);
+ }
+
+ append_header(strip_lws(std::string(begin,cursor)),
+ strip_lws(std::string(cursor+sizeof(header_separator)-1,end)));
+}
+
+inline std::string parser::raw_headers() const {
+ std::stringstream raw;
+
+ header_list::const_iterator it;
+ for (it = m_headers.begin(); it != m_headers.end(); it++) {
+ raw << it->first << ": " << it->second << "\r\n";
+ }
+
+ return raw.str();
+}
+
+
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#endif // HTTP_PARSER_IMPL_HPP
diff --git a/websocketpp/http/impl/request.hpp b/websocketpp/http/impl/request.hpp
new file mode 100644
index 00000000..311a620f
--- /dev/null
+++ b/websocketpp/http/impl/request.hpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_REQUEST_IMPL_HPP
+#define HTTP_PARSER_REQUEST_IMPL_HPP
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+inline size_t request::consume(char const * buf, size_t len) {
+ size_t bytes_processed;
+
+ if (m_ready) {return 0;}
+
+ if (m_body_bytes_needed > 0) {
+ bytes_processed = process_body(buf,len);
+ if (body_ready()) {
+ m_ready = true;
+ }
+ return bytes_processed;
+ }
+
+ // copy new header bytes into buffer
+ m_buf->append(buf,len);
+
+ // Search for delimiter in buf. If found read until then. If not read all
+ std::string::iterator begin = m_buf->begin();
+ std::string::iterator end;
+
+ for (;;) {
+ // search for line delimiter
+ end = std::search(
+ begin,
+ m_buf->end(),
+ header_delimiter,
+ header_delimiter+sizeof(header_delimiter)-1
+ );
+
+ m_header_bytes += (end-begin+sizeof(header_delimiter));
+
+ if (m_header_bytes > max_header_size) {
+ // exceeded max header size
+ throw exception("Maximum header size exceeded.",
+ status_code::request_header_fields_too_large);
+ }
+
+ if (end == m_buf->end()) {
+ // we are out of bytes. Discard the processed bytes and copy the
+ // remaining unprecessed bytes to the beginning of the buffer
+ std::copy(begin,end,m_buf->begin());
+ m_buf->resize(static_cast<std::string::size_type>(end-begin));
+ m_header_bytes -= m_buf->size();
+
+ return len;
+ }
+
+ //the range [begin,end) now represents a line to be processed.
+ if (end-begin == 0) {
+ // we got a blank line
+ if (m_method.empty() || get_header("Host").empty()) {
+ throw exception("Incomplete Request",status_code::bad_request);
+ }
+
+ bytes_processed = (
+ len - static_cast<std::string::size_type>(m_buf->end()-end)
+ + sizeof(header_delimiter) - 1
+ );
+
+ // frees memory used temporarily during request parsing
+ m_buf.reset();
+
+ // if this was not an upgrade request and has a content length
+ // continue capturing content-length bytes and expose them as a
+ // request body.
+
+ if (prepare_body()) {
+ bytes_processed += process_body(buf+bytes_processed,len-bytes_processed);
+ if (body_ready()) {
+ m_ready = true;
+ }
+ return bytes_processed;
+ } else {
+ m_ready = true;
+
+ // return number of bytes processed (starting bytes - bytes left)
+ return bytes_processed;
+ }
+ } else {
+ if (m_method.empty()) {
+ this->process(begin,end);
+ } else {
+ this->process_header(begin,end);
+ }
+ }
+
+ begin = end+(sizeof(header_delimiter)-1);
+ }
+}
+
+inline std::string request::raw() const {
+ // TODO: validation. Make sure all required fields have been set?
+ std::stringstream ret;
+
+ ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
+ ret << raw_headers() << "\r\n" << m_body;
+
+ return ret.str();
+}
+
+inline std::string request::raw_head() const {
+ // TODO: validation. Make sure all required fields have been set?
+ std::stringstream ret;
+
+ ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
+ ret << raw_headers() << "\r\n";
+
+ return ret.str();
+}
+
+inline void request::set_method(std::string const & method) {
+ if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) {
+ throw exception("Invalid method token.",status_code::bad_request);
+ }
+
+ m_method = method;
+}
+
+inline void request::set_uri(std::string const & uri) {
+ // TODO: validation?
+ m_uri = uri;
+}
+
+inline void request::process(std::string::iterator begin, std::string::iterator
+ end)
+{
+ std::string::iterator cursor_start = begin;
+ std::string::iterator cursor_end = std::find(begin,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line1",status_code::bad_request);
+ }
+
+ set_method(std::string(cursor_start,cursor_end));
+
+ cursor_start = cursor_end+1;
+ cursor_end = std::find(cursor_start,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line2",status_code::bad_request);
+ }
+
+ set_uri(std::string(cursor_start,cursor_end));
+ set_version(std::string(cursor_end+1,end));
+}
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#endif // HTTP_PARSER_REQUEST_IMPL_HPP
diff --git a/websocketpp/http/impl/response.hpp b/websocketpp/http/impl/response.hpp
new file mode 100644
index 00000000..4400cda5
--- /dev/null
+++ b/websocketpp/http/impl/response.hpp
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_RESPONSE_IMPL_HPP
+#define HTTP_PARSER_RESPONSE_IMPL_HPP
+
+#include <algorithm>
+#include <istream>
+#include <sstream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+inline size_t response::consume(char const * buf, size_t len) {
+ if (m_state == DONE) {return 0;}
+
+ if (m_state == BODY) {
+ return this->process_body(buf,len);
+ }
+
+ // copy new header bytes into buffer
+ m_buf->append(buf,len);
+
+ // Search for delimiter in buf. If found read until then. If not read all
+ std::string::iterator begin = m_buf->begin();
+ std::string::iterator end = begin;
+
+
+ for (;;) {
+ // search for delimiter
+ end = std::search(
+ begin,
+ m_buf->end(),
+ header_delimiter,
+ header_delimiter + sizeof(header_delimiter) - 1
+ );
+
+ m_header_bytes += (end-begin+sizeof(header_delimiter));
+
+ if (m_header_bytes > max_header_size) {
+ // exceeded max header size
+ throw exception("Maximum header size exceeded.",
+ status_code::request_header_fields_too_large);
+ }
+
+ if (end == m_buf->end()) {
+ // we are out of bytes. Discard the processed bytes and copy the
+ // remaining unprecessed bytes to the beginning of the buffer
+ std::copy(begin,end,m_buf->begin());
+ m_buf->resize(static_cast<std::string::size_type>(end-begin));
+
+ m_read += len;
+ m_header_bytes -= m_buf->size();
+
+ return len;
+ }
+
+ //the range [begin,end) now represents a line to be processed.
+
+ if (end-begin == 0) {
+ // we got a blank line
+ if (m_state == RESPONSE_LINE) {
+ throw exception("Incomplete Request",status_code::bad_request);
+ }
+
+ // TODO: grab content-length
+ std::string length = get_header("Content-Length");
+
+ if (length.empty()) {
+ // no content length found, read indefinitely
+ m_read = 0;
+ } else {
+ std::istringstream ss(length);
+
+ if ((ss >> m_read).fail()) {
+ throw exception("Unable to parse Content-Length header",
+ status_code::bad_request);
+ }
+ }
+
+ m_state = BODY;
+
+ // calc header bytes processed (starting bytes - bytes left)
+ size_t read = (
+ len - static_cast<std::string::size_type>(m_buf->end() - end)
+ + sizeof(header_delimiter) - 1
+ );
+
+ // if there were bytes left process them as body bytes
+ if (read < len) {
+ read += this->process_body(buf+read,(len-read));
+ }
+
+ // frees memory used temporarily during header parsing
+ m_buf.reset();
+
+ return read;
+ } else {
+ if (m_state == RESPONSE_LINE) {
+ this->process(begin,end);
+ m_state = HEADERS;
+ } else {
+ this->process_header(begin,end);
+ }
+ }
+
+ begin = end+(sizeof(header_delimiter) - 1);
+ }
+}
+
+inline size_t response::consume(std::istream & s) {
+ char buf[istream_buffer];
+ size_t bytes_read;
+ size_t bytes_processed;
+ size_t total = 0;
+
+ while (s.good()) {
+ s.getline(buf,istream_buffer);
+ bytes_read = static_cast<size_t>(s.gcount());
+
+ if (s.fail() || s.eof()) {
+ bytes_processed = this->consume(buf,bytes_read);
+ total += bytes_processed;
+
+ if (bytes_processed != bytes_read) {
+ // problem
+ break;
+ }
+ } else if (s.bad()) {
+ // problem
+ break;
+ } else {
+ // the delimiting newline was found. Replace the trailing null with
+ // the newline that was discarded, since our raw consume function
+ // expects the newline to be be there.
+ buf[bytes_read-1] = '\n';
+ bytes_processed = this->consume(buf,bytes_read);
+ total += bytes_processed;
+
+ if (bytes_processed != bytes_read) {
+ // problem
+ break;
+ }
+ }
+ }
+
+ return total;
+}
+
+inline std::string response::raw() const {
+ // TODO: validation. Make sure all required fields have been set?
+
+ std::stringstream ret;
+
+ ret << get_version() << " " << m_status_code << " " << m_status_msg;
+ ret << "\r\n" << raw_headers() << "\r\n";
+
+ ret << m_body;
+
+ return ret.str();
+}
+
+inline void response::set_status(status_code::value code) {
+ // TODO: validation?
+ m_status_code = code;
+ m_status_msg = get_string(code);
+}
+
+inline void response::set_status(status_code::value code, std::string const &
+ msg)
+{
+ // TODO: validation?
+ m_status_code = code;
+ m_status_msg = msg;
+}
+
+inline void response::process(std::string::iterator begin,
+ std::string::iterator end)
+{
+ std::string::iterator cursor_start = begin;
+ std::string::iterator cursor_end = std::find(begin,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid response line",status_code::bad_request);
+ }
+
+ set_version(std::string(cursor_start,cursor_end));
+
+ cursor_start = cursor_end+1;
+ cursor_end = std::find(cursor_start,end,' ');
+
+ if (cursor_end == end) {
+ throw exception("Invalid request line",status_code::bad_request);
+ }
+
+ int code;
+
+ std::istringstream ss(std::string(cursor_start,cursor_end));
+
+ if ((ss >> code).fail()) {
+ throw exception("Unable to parse response code",status_code::bad_request);
+ }
+
+ set_status(status_code::value(code),std::string(cursor_end+1,end));
+}
+
+inline size_t response::process_body(char const * buf, size_t len) {
+ // If no content length was set then we read forever and never set m_ready
+ if (m_read == 0) {
+ //m_body.append(buf,len);
+ //return len;
+ m_state = DONE;
+ return 0;
+ }
+
+ // Otherwise m_read is the number of bytes left.
+ size_t to_read;
+
+ if (len >= m_read) {
+ // if we have more bytes than we need read, read only the amount needed
+ // then set done state
+ to_read = m_read;
+ m_state = DONE;
+ } else {
+ // we need more bytes than are available, read them all
+ to_read = len;
+ }
+
+ m_body.append(buf,to_read);
+ m_read -= to_read;
+ return to_read;
+}
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#endif // HTTP_PARSER_RESPONSE_IMPL_HPP
diff --git a/websocketpp/http/parser.hpp b/websocketpp/http/parser.hpp
new file mode 100644
index 00000000..90f49ebe
--- /dev/null
+++ b/websocketpp/http/parser.hpp
@@ -0,0 +1,619 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_HPP
+#define HTTP_PARSER_HPP
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <utility>
+
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/http/constants.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+namespace state {
+ enum value {
+ method,
+ resource,
+ version,
+ headers
+ };
+}
+
+namespace body_encoding {
+ enum value {
+ unknown,
+ plain,
+ chunked
+ };
+}
+
+typedef std::map<std::string, std::string, utility::ci_less > header_list;
+
+/// Read and return the next token in the stream
+/**
+ * Read until a non-token character is found and then return the token and
+ * iterator to the next character to read
+ *
+ * @param begin An iterator to the beginning of the sequence
+ * @param end An iterator to the end of the sequence
+ * @return A pair containing the token and an iterator to the next character in
+ * the stream
+ */
+template <typename InputIterator>
+std::pair<std::string,InputIterator> extract_token(InputIterator begin,
+ InputIterator end)
+{
+ InputIterator it = std::find_if(begin,end,&is_not_token_char);
+ return std::make_pair(std::string(begin,it),it);
+}
+
+/// Read and return the next quoted string in the stream
+/**
+ * Read a double quoted string starting at `begin`. The quotes themselves are
+ * stripped. The quoted value is returned along with an iterator to the next
+ * character to read
+ *
+ * @param begin An iterator to the beginning of the sequence
+ * @param end An iterator to the end of the sequence
+ * @return A pair containing the string read and an iterator to the next
+ * character in the stream
+ */
+template <typename InputIterator>
+std::pair<std::string,InputIterator> extract_quoted_string(InputIterator begin,
+ InputIterator end)
+{
+ std::string s;
+
+ if (end == begin) {
+ return std::make_pair(s,begin);
+ }
+
+ if (*begin != '"') {
+ return std::make_pair(s,begin);
+ }
+
+ InputIterator cursor = begin+1;
+ InputIterator marker = cursor;
+
+ cursor = std::find(cursor,end,'"');
+
+ while (cursor != end) {
+ // either this is the end or a quoted string
+ if (*(cursor-1) == '\\') {
+ s.append(marker,cursor-1);
+ s.append(1,'"');
+ ++cursor;
+ marker = cursor;
+ } else {
+ s.append(marker,cursor);
+ ++cursor;
+ return std::make_pair(s,cursor);
+ }
+
+ cursor = std::find(cursor,end,'"');
+ }
+
+ return std::make_pair("",begin);
+}
+
+/// Read and discard one unit of linear whitespace
+/**
+ * Read one unit of linear white space and return the iterator to the character
+ * afterwards. If `begin` is returned, no whitespace was extracted.
+ *
+ * @param begin An iterator to the beginning of the sequence
+ * @param end An iterator to the end of the sequence
+ * @return An iterator to the character after the linear whitespace read
+ */
+template <typename InputIterator>
+InputIterator extract_lws(InputIterator begin, InputIterator end) {
+ InputIterator it = begin;
+
+ // strip leading CRLF
+ if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' &&
+ is_whitespace_char(static_cast<unsigned char>(*(begin+2))))
+ {
+ it+=3;
+ }
+
+ it = std::find_if(it,end,&is_not_whitespace_char);
+ return it;
+}
+
+/// Read and discard linear whitespace
+/**
+ * Read linear white space until a non-lws character is read and return an
+ * iterator to that character. If `begin` is returned, no whitespace was
+ * extracted.
+ *
+ * @param begin An iterator to the beginning of the sequence
+ * @param end An iterator to the end of the sequence
+ * @return An iterator to the character after the linear whitespace read
+ */
+template <typename InputIterator>
+InputIterator extract_all_lws(InputIterator begin, InputIterator end) {
+ InputIterator old_it;
+ InputIterator new_it = begin;
+
+ do {
+ // Pull value from previous iteration
+ old_it = new_it;
+
+ // look ahead another pass
+ new_it = extract_lws(old_it,end);
+ } while (new_it != end && old_it != new_it);
+
+ return new_it;
+}
+
+/// Extract HTTP attributes
+/**
+ * An http attributes list is a semicolon delimited list of key value pairs in
+ * the format: *( ";" attribute "=" value ) where attribute is a token and value
+ * is a token or quoted string.
+ *
+ * Attributes extracted are appended to the supplied attributes list
+ * `attributes`.
+ *
+ * @param [in] begin An iterator to the beginning of the sequence
+ * @param [in] end An iterator to the end of the sequence
+ * @param [out] attributes A reference to the attributes list to append
+ * attribute/value pairs extracted to
+ * @return An iterator to the character after the last atribute read
+ */
+template <typename InputIterator>
+InputIterator extract_attributes(InputIterator begin, InputIterator end,
+ attribute_list & attributes)
+{
+ InputIterator cursor;
+ bool first = true;
+
+ if (begin == end) {
+ return begin;
+ }
+
+ cursor = begin;
+ std::pair<std::string,InputIterator> ret;
+
+ while (cursor != end) {
+ std::string name;
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ break;
+ }
+
+ if (first) {
+ // ignore this check for the very first pass
+ first = false;
+ } else {
+ if (*cursor == ';') {
+ // advance past the ';'
+ ++cursor;
+ } else {
+ // non-semicolon in this position indicates end end of the
+ // attribute list, break and return.
+ break;
+ }
+ }
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ ret = http::parser::extract_token(cursor,end);
+
+ if (ret.first.empty()) {
+ // error: expected a token
+ return begin;
+ } else {
+ name = ret.first;
+ cursor = ret.second;
+ }
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end || *cursor != '=') {
+ // if there is an equals sign, read the attribute value. Otherwise
+ // record a blank value and continue
+ attributes[name].clear();
+ continue;
+ }
+
+ // advance past the '='
+ ++cursor;
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ // error: expected a token or quoted string
+ return begin;
+ }
+
+ ret = http::parser::extract_quoted_string(cursor,end);
+ if (ret.second != cursor) {
+ attributes[name] = ret.first;
+ cursor = ret.second;
+ continue;
+ }
+
+ ret = http::parser::extract_token(cursor,end);
+ if (ret.first.empty()) {
+ // error : expected token or quoted string
+ return begin;
+ } else {
+ attributes[name] = ret.first;
+ cursor = ret.second;
+ }
+ }
+
+ return cursor;
+}
+
+/// Extract HTTP parameters
+/**
+ * An http parameters list is a comma delimited list of tokens followed by
+ * optional semicolon delimited attributes lists.
+ *
+ * Parameters extracted are appended to the supplied parameters list
+ * `parameters`.
+ *
+ * @param [in] begin An iterator to the beginning of the sequence
+ * @param [in] end An iterator to the end of the sequence
+ * @param [out] parameters A reference to the parameters list to append
+ * paramter values extracted to
+ * @return An iterator to the character after the last parameter read
+ */
+template <typename InputIterator>
+InputIterator extract_parameters(InputIterator begin, InputIterator end,
+ parameter_list &parameters)
+{
+ InputIterator cursor;
+
+ if (begin == end) {
+ // error: expected non-zero length range
+ return begin;
+ }
+
+ cursor = begin;
+ std::pair<std::string,InputIterator> ret;
+
+ /**
+ * LWS
+ * token
+ * LWS
+ * *(";" method-param)
+ * LWS
+ * ,=loop again
+ */
+ while (cursor != end) {
+ std::string parameter_name;
+ attribute_list attributes;
+
+ // extract any stray whitespace
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {break;}
+
+ ret = http::parser::extract_token(cursor,end);
+
+ if (ret.first.empty()) {
+ // error: expected a token
+ return begin;
+ } else {
+ parameter_name = ret.first;
+ cursor = ret.second;
+ }
+
+ // Safe break point, insert parameter with blank attributes and exit
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {
+ //parameters[parameter_name] = attributes;
+ parameters.push_back(std::make_pair(parameter_name,attributes));
+ break;
+ }
+
+ // If there is an attribute list, read it in
+ if (*cursor == ';') {
+ InputIterator acursor;
+
+ ++cursor;
+ acursor = http::parser::extract_attributes(cursor,end,attributes);
+
+ if (acursor == cursor) {
+ // attribute extraction ended in syntax error
+ return begin;
+ }
+
+ cursor = acursor;
+ }
+
+ // insert parameter into output list
+ //parameters[parameter_name] = attributes;
+ parameters.push_back(std::make_pair(parameter_name,attributes));
+
+ cursor = http::parser::extract_all_lws(cursor,end);
+ if (cursor == end) {break;}
+
+ // if next char is ',' then read another parameter, else stop
+ if (*cursor != ',') {
+ break;
+ }
+
+ // advance past comma
+ ++cursor;
+
+ if (cursor == end) {
+ // expected more bytes after a comma
+ return begin;
+ }
+ }
+
+ return cursor;
+}
+
+inline std::string strip_lws(std::string const & input) {
+ std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
+ if (begin == input.end()) {
+ return std::string();
+ }
+
+ std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend());
+ if (rbegin == input.rend()) {
+ return std::string();
+ }
+
+ return std::string(begin,rbegin.base());
+}
+
+/// Base HTTP parser
+/**
+ * Includes methods and data elements common to all types of HTTP messages such
+ * as headers, versions, bodies, etc.
+ */
+class parser {
+public:
+ parser()
+ : m_header_bytes(0)
+ , m_body_bytes_needed(0)
+ , m_body_bytes_max(max_body_size)
+ , m_body_encoding(body_encoding::unknown) {}
+
+ /// Get the HTTP version string
+ /**
+ * @return The version string for this parser
+ */
+ std::string const & get_version() const {
+ return m_version;
+ }
+
+ /// Set HTTP parser Version
+ /**
+ * Input should be in format: HTTP/x.y where x and y are positive integers.
+ * @todo Does this method need any validation?
+ *
+ * @param [in] version The value to set the HTTP version to.
+ */
+ void set_version(std::string const & version);
+
+ /// Get the value of an HTTP header
+ /**
+ * @todo Make this method case insensitive.
+ *
+ * @param [in] key The name/key of the header to get.
+ * @return The value associated with the given HTTP header key.
+ */
+ std::string const & get_header(std::string const & key) const;
+
+ /// Extract an HTTP parameter list from a parser header.
+ /**
+ * If the header requested doesn't exist or exists and is empty the
+ * parameter list is valid (but empty).
+ *
+ * @param [in] key The name/key of the HTTP header to use as input.
+ * @param [out] out The parameter list to store extracted parameters in.
+ * @return Whether or not the input was a valid parameter list.
+ */
+ bool get_header_as_plist(std::string const & key, parameter_list & out)
+ const;
+
+ /// Append a value to an existing HTTP header
+ /**
+ * This method will set the value of the HTTP header `key` with the
+ * indicated value. If a header with the name `key` already exists, `val`
+ * will be appended to the existing value.
+ *
+ * @todo Make this method case insensitive.
+ * @todo Should there be any restrictions on which keys are allowed?
+ * @todo Exception free varient
+ *
+ * @see replace_header
+ *
+ * @param [in] key The name/key of the header to append to.
+ * @param [in] val The value to append.
+ */
+ void append_header(std::string const & key, std::string const & val);
+
+ /// Set a value for an HTTP header, replacing an existing value
+ /**
+ * This method will set the value of the HTTP header `key` with the
+ * indicated value. If a header with the name `key` already exists, `val`
+ * will replace the existing value.
+ *
+ * @todo Make this method case insensitive.
+ * @todo Should there be any restrictions on which keys are allowed?
+ * @todo Exception free varient
+ *
+ * @see append_header
+ *
+ * @param [in] key The name/key of the header to append to.
+ * @param [in] val The value to append.
+ */
+ void replace_header(std::string const & key, std::string const & val);
+
+ /// Remove a header from the parser
+ /**
+ * Removes the header entirely from the parser. This is different than
+ * setting the value of the header to blank.
+ *
+ * @todo Make this method case insensitive.
+ *
+ * @param [in] key The name/key of the header to remove.
+ */
+ void remove_header(std::string const & key);
+
+ /// Get HTTP body
+ /**
+ * Gets the body of the HTTP object
+ *
+ * @return The body of the HTTP message.
+ */
+ std::string const & get_body() const {
+ return m_body;
+ }
+
+ /// Set body content
+ /**
+ * Set the body content of the HTTP response to the parameter string. Note
+ * set_body will also set the Content-Length HTTP header to the appropriate
+ * value. If you want the Content-Length header to be something else, do so
+ * via replace_header("Content-Length") after calling set_body()
+ *
+ * @param value String data to include as the body content.
+ */
+ void set_body(std::string const & value);
+
+ /// Get body size limit
+ /**
+ * Retrieves the maximum number of bytes to parse & buffer before canceling
+ * a request.
+ *
+ * @since 0.5.0
+ *
+ * @return The maximum length of a message body.
+ */
+ size_t get_max_body_size() const {
+ return m_body_bytes_max;
+ }
+
+ /// Set body size limit
+ /**
+ * Set the maximum number of bytes to parse and buffer before canceling a
+ * request.
+ *
+ * @since 0.5.0
+ *
+ * @param value The size to set the max body length to.
+ */
+ void set_max_body_size(size_t value) {
+ m_body_bytes_max = value;
+ }
+
+ /// Extract an HTTP parameter list from a string.
+ /**
+ * @param [in] in The input string.
+ * @param [out] out The parameter list to store extracted parameters in.
+ * @return Whether or not the input was a valid parameter list.
+ */
+ bool parse_parameter_list(std::string const & in, parameter_list & out)
+ const;
+protected:
+ /// Process a header line
+ /**
+ * @todo Update this method to be exception free.
+ *
+ * @param [in] begin An iterator to the beginning of the sequence.
+ * @param [in] end An iterator to the end of the sequence.
+ */
+ void process_header(std::string::iterator begin, std::string::iterator end);
+
+ /// Prepare the parser to begin parsing body data
+ /**
+ * Inspects headers to determine if the message has a body that needs to be
+ * read. If so, sets up the necessary state, otherwise returns false. If
+ * this method returns true and loading the message body is desired call
+ * `process_body` until it returns zero bytes or an error.
+ *
+ * Must not be called until after all headers have been processed.
+ *
+ * @since 0.5.0
+ *
+ * @return True if more bytes are needed to load the body, false otherwise.
+ */
+ bool prepare_body();
+
+ /// Process body data
+ /**
+ * Parses body data.
+ *
+ * @since 0.5.0
+ *
+ * @param [in] begin An iterator to the beginning of the sequence.
+ * @param [in] end An iterator to the end of the sequence.
+ * @return The number of bytes processed
+ */
+ size_t process_body(char const * buf, size_t len);
+
+ /// Check if the parser is done parsing the body
+ /**
+ * Behavior before a call to `prepare_body` is undefined.
+ *
+ * @since 0.5.0
+ *
+ * @return True if the message body has been completed loaded.
+ */
+ bool body_ready() const {
+ return (m_body_bytes_needed == 0);
+ }
+
+ /// Generate and return the HTTP headers as a string
+ /**
+ * Each headers will be followed by the \r\n sequence including the last one.
+ * A second \r\n sequence (blank header) is not appended by this method
+ *
+ * @return The HTTP headers as a string.
+ */
+ std::string raw_headers() const;
+
+ std::string m_version;
+ header_list m_headers;
+
+ size_t m_header_bytes;
+
+ std::string m_body;
+ size_t m_body_bytes_needed;
+ size_t m_body_bytes_max;
+ body_encoding::value m_body_encoding;
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/parser.hpp>
+
+#endif // HTTP_PARSER_HPP
diff --git a/websocketpp/http/request.hpp b/websocketpp/http/request.hpp
new file mode 100644
index 00000000..3355c99b
--- /dev/null
+++ b/websocketpp/http/request.hpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_REQUEST_HPP
+#define HTTP_PARSER_REQUEST_HPP
+
+#include <string>
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+/// Stores, parses, and manipulates HTTP requests
+/**
+ * http::request provides the following functionality for working with HTTP
+ * requests.
+ *
+ * - Initialize request via manually setting each element
+ * - Initialize request via reading raw bytes and parsing
+ * - Once initialized, access individual parsed elements
+ * - Once initialized, read entire request as raw bytes
+ */
+class request : public parser {
+public:
+ typedef request type;
+ typedef lib::shared_ptr<type> ptr;
+
+ request()
+ : m_buf(lib::make_shared<std::string>())
+ , m_ready(false) {}
+
+ /// Process bytes in the input buffer
+ /**
+ * Process up to len bytes from input buffer buf. Returns the number of
+ * bytes processed. Bytes left unprocessed means bytes left over after the
+ * final header delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * request and the full headers need not be available before processing can
+ * begin. If the end of the request was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed requests, incomplete requests, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(char const * buf, size_t len);
+
+ /// Returns whether or not the request is ready for reading.
+ bool ready() const {
+ return m_ready;
+ }
+
+ /// Returns the full raw request (including the body)
+ std::string raw() const;
+
+ /// Returns the raw request headers only (similar to an HTTP HEAD request)
+ std::string raw_head() const;
+
+ /// Set the HTTP method. Must be a valid HTTP token
+ void set_method(std::string const & method);
+
+ /// Return the request method
+ std::string const & get_method() const {
+ return m_method;
+ }
+
+ /// Set the HTTP uri. Must be a valid HTTP uri
+ void set_uri(std::string const & uri);
+
+ /// Return the requested URI
+ std::string const & get_uri() const {
+ return m_uri;
+ }
+
+private:
+ /// Helper function for message::consume. Process request line
+ void process(std::string::iterator begin, std::string::iterator end);
+
+ lib::shared_ptr<std::string> m_buf;
+ std::string m_method;
+ std::string m_uri;
+ bool m_ready;
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/request.hpp>
+
+#endif // HTTP_PARSER_REQUEST_HPP
diff --git a/websocketpp/http/response.hpp b/websocketpp/http/response.hpp
new file mode 100644
index 00000000..e724a3d3
--- /dev/null
+++ b/websocketpp/http/response.hpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HTTP_PARSER_RESPONSE_HPP
+#define HTTP_PARSER_RESPONSE_HPP
+
+#include <iostream>
+#include <string>
+
+#include <websocketpp/http/parser.hpp>
+
+namespace websocketpp {
+namespace http {
+namespace parser {
+
+/// Stores, parses, and manipulates HTTP responses
+/**
+ * http::response provides the following functionality for working with HTTP
+ * responses.
+ *
+ * - Initialize response via manually setting each element
+ * - Initialize response via reading raw bytes and parsing
+ * - Once initialized, access individual parsed elements
+ * - Once initialized, read entire response as raw bytes
+ *
+ * http::response checks for header completeness separately from the full
+ * response. Once the header is complete, the Content-Length header is read to
+ * determine when to stop reading body bytes. If no Content-Length is present
+ * ready() will never return true. It is the responsibility of the caller to
+ * consume to determine when the response is complete (ie when the connection
+ * terminates, or some other metric).
+ */
+class response : public parser {
+public:
+ typedef response type;
+ typedef lib::shared_ptr<type> ptr;
+
+ response()
+ : m_read(0)
+ , m_buf(lib::make_shared<std::string>())
+ , m_status_code(status_code::uninitialized)
+ , m_state(RESPONSE_LINE) {}
+
+ /// Process bytes in the input buffer
+ /**
+ * Process up to len bytes from input buffer buf. Returns the number of
+ * bytes processed. Bytes left unprocessed means bytes left over after the
+ * final header delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * response and the full headers need not be available before processing can
+ * begin. If the end of the response was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed responses, incomplete responses, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(char const * buf, size_t len);
+
+ /// Process bytes in the input buffer (istream version)
+ /**
+ * Process bytes from istream s. Returns the number of bytes processed.
+ * Bytes left unprocessed means bytes left over after the final header
+ * delimiters.
+ *
+ * Consume is a streaming processor. It may be called multiple times on one
+ * response and the full headers need not be available before processing can
+ * begin. If the end of the response was reached during this call to consume
+ * the ready flag will be set. Further calls to consume once ready will be
+ * ignored.
+ *
+ * Consume will throw an http::exception in the case of an error. Typical
+ * error reasons include malformed responses, incomplete responses, and max
+ * header size being reached.
+ *
+ * @param buf Pointer to byte buffer
+ * @param len Size of byte buffer
+ * @return Number of bytes processed.
+ */
+ size_t consume(std::istream & s);
+
+ /// Returns true if the response is ready.
+ /**
+ * @note will never return true if the content length header is not present
+ */
+ bool ready() const {
+ return m_state == DONE;
+ }
+
+ /// Returns true if the response headers are fully parsed.
+ bool headers_ready() const {
+ return (m_state == BODY || m_state == DONE);
+ }
+
+ /// Returns the full raw response
+ std::string raw() const;
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code to `code` and looks up the corresponding
+ * message for standard codes. Non-standard codes will be entered as Unknown
+ * use set_status(status_code::value,std::string) overload to set both
+ * values explicitly.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ */
+ void set_status(status_code::value code);
+
+ /// Set response status code and message
+ /**
+ * Sets the response status code and message to independent custom values.
+ * use set_status(status_code::value) to set the code and have the standard
+ * message be automatically set.
+ *
+ * @param code Code to set
+ * @param msg Message to set
+ */
+ void set_status(status_code::value code, std::string const & msg);
+
+ /// Return the response status code
+ status_code::value get_status_code() const {
+ return m_status_code;
+ }
+
+ /// Return the response status message
+ const std::string& get_status_msg() const {
+ return m_status_msg;
+ }
+private:
+ /// Helper function for consume. Process response line
+ void process(std::string::iterator begin, std::string::iterator end);
+
+ /// Helper function for processing body bytes
+ size_t process_body(char const * buf, size_t len);
+
+ enum state {
+ RESPONSE_LINE = 0,
+ HEADERS = 1,
+ BODY = 2,
+ DONE = 3
+ };
+
+ std::string m_status_msg;
+ size_t m_read;
+ lib::shared_ptr<std::string> m_buf;
+ status_code::value m_status_code;
+ state m_state;
+
+};
+
+} // namespace parser
+} // namespace http
+} // namespace websocketpp
+
+#include <websocketpp/http/impl/response.hpp>
+
+#endif // HTTP_PARSER_RESPONSE_HPP
diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp
new file mode 100644
index 00000000..d1f8dff2
--- /dev/null
+++ b/websocketpp/impl/connection_impl.hpp
@@ -0,0 +1,2372 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
+#define WEBSOCKETPP_CONNECTION_IMPL_HPP
+
+#include <websocketpp/processors/hybi00.hpp>
+#include <websocketpp/processors/hybi07.hpp>
+#include <websocketpp/processors/hybi08.hpp>
+#include <websocketpp/processors/hybi13.hpp>
+
+#include <websocketpp/processors/processor.hpp>
+
+#include <websocketpp/common/platforms.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <algorithm>
+#include <exception>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace websocketpp {
+
+namespace istate = session::internal_state;
+
+template <typename config>
+void connection<config>::set_termination_handler(
+ termination_handler new_handler)
+{
+ m_alog.write(log::alevel::devel,
+ "connection set_termination_handler");
+
+ //scoped_lock_type lock(m_connection_state_lock);
+
+ m_termination_handler = new_handler;
+}
+
+template <typename config>
+std::string const & connection<config>::get_origin() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_processor->get_origin(m_request);
+}
+
+template <typename config>
+size_t connection<config>::get_buffered_amount() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_send_buffer_size;
+}
+
+template <typename config>
+session::state::value connection<config>::get_state() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_state;
+}
+
+template <typename config>
+lib::error_code connection<config>::send(std::string const & payload,
+ frame::opcode::value op)
+{
+ message_ptr msg = m_msg_manager->get_message(op,payload.size());
+ msg->append_payload(payload);
+ msg->set_compressed(true);
+
+ return send(msg);
+}
+
+template <typename config>
+lib::error_code connection<config>::send(void const * payload, size_t len,
+ frame::opcode::value op)
+{
+ message_ptr msg = m_msg_manager->get_message(op,len);
+ msg->append_payload(payload,len);
+
+ return send(msg);
+}
+
+template <typename config>
+lib::error_code connection<config>::send(typename config::message_type::ptr msg)
+{
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection send");
+ }
+
+ {
+ scoped_lock_type lock(m_connection_state_lock);
+ if (m_state != session::state::open) {
+ return error::make_error_code(error::invalid_state);
+ }
+ }
+
+ message_ptr outgoing_msg;
+ bool needs_writing = false;
+
+ if (msg->get_prepared()) {
+ outgoing_msg = msg;
+
+ scoped_lock_type lock(m_write_lock);
+ write_push(outgoing_msg);
+ needs_writing = !m_write_flag && !m_send_queue.empty();
+ } else {
+ outgoing_msg = m_msg_manager->get_message();
+
+ if (!outgoing_msg) {
+ return error::make_error_code(error::no_outgoing_buffers);
+ }
+
+ scoped_lock_type lock(m_write_lock);
+ lib::error_code ec = m_processor->prepare_data_frame(msg,outgoing_msg);
+
+ if (ec) {
+ return ec;
+ }
+
+ write_push(outgoing_msg);
+ needs_writing = !m_write_flag && !m_send_queue.empty();
+ }
+
+ if (needs_writing) {
+ transport_con_type::dispatch(lib::bind(
+ &type::write_frame,
+ type::get_shared()
+ ));
+ }
+
+ return lib::error_code();
+}
+
+template <typename config>
+void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection ping");
+ }
+
+ {
+ scoped_lock_type lock(m_connection_state_lock);
+ if (m_state != session::state::open) {
+ std::stringstream ss;
+ ss << "connection::ping called from invalid state " << m_state;
+ m_alog.write(log::alevel::devel,ss.str());
+ ec = error::make_error_code(error::invalid_state);
+ return;
+ }
+ }
+
+ message_ptr msg = m_msg_manager->get_message();
+ if (!msg) {
+ ec = error::make_error_code(error::no_outgoing_buffers);
+ return;
+ }
+
+ ec = m_processor->prepare_ping(payload,msg);
+ if (ec) {return;}
+
+ // set ping timer if we are listening for one
+ if (m_pong_timeout_handler) {
+ // Cancel any existing timers
+ if (m_ping_timer) {
+ m_ping_timer->cancel();
+ }
+
+ if (m_pong_timeout_dur > 0) {
+ m_ping_timer = transport_con_type::set_timer(
+ m_pong_timeout_dur,
+ lib::bind(
+ &type::handle_pong_timeout,
+ type::get_shared(),
+ payload,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ if (!m_ping_timer) {
+ // Our transport doesn't support timers
+ m_elog.write(log::elevel::warn,"Warning: a pong_timeout_handler is \
+ set but the transport in use does not support timeouts.");
+ }
+ }
+
+ bool needs_writing = false;
+ {
+ scoped_lock_type lock(m_write_lock);
+ write_push(msg);
+ needs_writing = !m_write_flag && !m_send_queue.empty();
+ }
+
+ if (needs_writing) {
+ transport_con_type::dispatch(lib::bind(
+ &type::write_frame,
+ type::get_shared()
+ ));
+ }
+
+ ec = lib::error_code();
+}
+
+template<typename config>
+void connection<config>::ping(std::string const & payload) {
+ lib::error_code ec;
+ ping(payload,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+template<typename config>
+void connection<config>::handle_pong_timeout(std::string payload,
+ lib::error_code const & ec)
+{
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ // ignore, this is expected
+ return;
+ }
+
+ m_elog.write(log::elevel::devel,"pong_timeout error: "+ec.message());
+ return;
+ }
+
+ if (m_pong_timeout_handler) {
+ m_pong_timeout_handler(m_connection_hdl,payload);
+ }
+}
+
+template <typename config>
+void connection<config>::pong(std::string const& payload, lib::error_code& ec) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection pong");
+ }
+
+ {
+ scoped_lock_type lock(m_connection_state_lock);
+ if (m_state != session::state::open) {
+ std::stringstream ss;
+ ss << "connection::pong called from invalid state " << m_state;
+ m_alog.write(log::alevel::devel,ss.str());
+ ec = error::make_error_code(error::invalid_state);
+ return;
+ }
+ }
+
+ message_ptr msg = m_msg_manager->get_message();
+ if (!msg) {
+ ec = error::make_error_code(error::no_outgoing_buffers);
+ return;
+ }
+
+ ec = m_processor->prepare_pong(payload,msg);
+ if (ec) {return;}
+
+ bool needs_writing = false;
+ {
+ scoped_lock_type lock(m_write_lock);
+ write_push(msg);
+ needs_writing = !m_write_flag && !m_send_queue.empty();
+ }
+
+ if (needs_writing) {
+ transport_con_type::dispatch(lib::bind(
+ &type::write_frame,
+ type::get_shared()
+ ));
+ }
+
+ ec = lib::error_code();
+}
+
+template<typename config>
+void connection<config>::pong(std::string const & payload) {
+ lib::error_code ec;
+ pong(payload,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+template <typename config>
+void connection<config>::close(close::status::value const code,
+ std::string const & reason, lib::error_code & ec)
+{
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection close");
+ }
+
+ // Truncate reason to maximum size allowable in a close frame.
+ std::string tr(reason,0,std::min<size_t>(reason.size(),
+ frame::limits::close_reason_size));
+
+ scoped_lock_type lock(m_connection_state_lock);
+
+ if (m_state != session::state::open) {
+ ec = error::make_error_code(error::invalid_state);
+ return;
+ }
+
+ ec = this->send_close_frame(code,tr,false,close::status::terminal(code));
+}
+
+template<typename config>
+void connection<config>::close(close::status::value const code,
+ std::string const & reason)
+{
+ lib::error_code ec;
+ close(code,reason,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+/// Trigger the on_interrupt handler
+/**
+ * This is thread safe if the transport is thread safe
+ */
+template <typename config>
+lib::error_code connection<config>::interrupt() {
+ m_alog.write(log::alevel::devel,"connection connection::interrupt");
+ return transport_con_type::interrupt(
+ lib::bind(
+ &type::handle_interrupt,
+ type::get_shared()
+ )
+ );
+}
+
+
+template <typename config>
+void connection<config>::handle_interrupt() {
+ if (m_interrupt_handler) {
+ m_interrupt_handler(m_connection_hdl);
+ }
+}
+
+template <typename config>
+lib::error_code connection<config>::pause_reading() {
+ m_alog.write(log::alevel::devel,"connection connection::pause_reading");
+ return transport_con_type::dispatch(
+ lib::bind(
+ &type::handle_pause_reading,
+ type::get_shared()
+ )
+ );
+}
+
+/// Pause reading handler. Not safe to call directly
+template <typename config>
+void connection<config>::handle_pause_reading() {
+ m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading");
+ m_read_flag = false;
+}
+
+template <typename config>
+lib::error_code connection<config>::resume_reading() {
+ m_alog.write(log::alevel::devel,"connection connection::resume_reading");
+ return transport_con_type::dispatch(
+ lib::bind(
+ &type::handle_resume_reading,
+ type::get_shared()
+ )
+ );
+}
+
+/// Resume reading helper method. Not safe to call directly
+template <typename config>
+void connection<config>::handle_resume_reading() {
+ m_read_flag = true;
+ read_frame();
+}
+
+
+
+
+
+
+
+
+
+
+
+template <typename config>
+bool connection<config>::get_secure() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_uri->get_secure();
+}
+
+template <typename config>
+std::string const & connection<config>::get_host() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_uri->get_host();
+}
+
+template <typename config>
+std::string const & connection<config>::get_resource() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_uri->get_resource();
+}
+
+template <typename config>
+uint16_t connection<config>::get_port() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_uri->get_port();
+}
+
+template <typename config>
+uri_ptr connection<config>::get_uri() const {
+ //scoped_lock_type lock(m_connection_state_lock);
+ return m_uri;
+}
+
+template <typename config>
+void connection<config>::set_uri(uri_ptr uri) {
+ //scoped_lock_type lock(m_connection_state_lock);
+ m_uri = uri;
+}
+
+
+
+
+
+
+template <typename config>
+std::string const & connection<config>::get_subprotocol() const {
+ return m_subprotocol;
+}
+
+template <typename config>
+std::vector<std::string> const &
+connection<config>::get_requested_subprotocols() const {
+ return m_requested_subprotocols;
+}
+
+template <typename config>
+void connection<config>::add_subprotocol(std::string const & value,
+ lib::error_code & ec)
+{
+ if (m_is_server) {
+ ec = error::make_error_code(error::client_only);
+ return;
+ }
+
+ // If the value is empty or has a non-RFC2616 token character it is invalid.
+ if (value.empty() || std::find_if(value.begin(),value.end(),
+ http::is_not_token_char) != value.end())
+ {
+ ec = error::make_error_code(error::invalid_subprotocol);
+ return;
+ }
+
+ m_requested_subprotocols.push_back(value);
+}
+
+template <typename config>
+void connection<config>::add_subprotocol(std::string const & value) {
+ lib::error_code ec;
+ this->add_subprotocol(value,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+
+template <typename config>
+void connection<config>::select_subprotocol(std::string const & value,
+ lib::error_code & ec)
+{
+ if (!m_is_server) {
+ ec = error::make_error_code(error::server_only);
+ return;
+ }
+
+ if (value.empty()) {
+ ec = lib::error_code();
+ return;
+ }
+
+ std::vector<std::string>::iterator it;
+
+ it = std::find(m_requested_subprotocols.begin(),
+ m_requested_subprotocols.end(),
+ value);
+
+ if (it == m_requested_subprotocols.end()) {
+ ec = error::make_error_code(error::unrequested_subprotocol);
+ return;
+ }
+
+ m_subprotocol = value;
+}
+
+template <typename config>
+void connection<config>::select_subprotocol(std::string const & value) {
+ lib::error_code ec;
+ this->select_subprotocol(value,ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+
+template <typename config>
+std::string const &
+connection<config>::get_request_header(std::string const & key) const {
+ return m_request.get_header(key);
+}
+
+template <typename config>
+std::string const &
+connection<config>::get_request_body() const {
+ return m_request.get_body();
+}
+
+template <typename config>
+std::string const &
+connection<config>::get_response_header(std::string const & key) const {
+ return m_response.get_header(key);
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::set_status(http::status_code::value code)
+{
+ if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
+ throw exception("Call to set_status from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ m_response.set_status(code);
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::set_status(http::status_code::value code,
+ std::string const & msg)
+{
+ if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
+ throw exception("Call to set_status from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+
+ m_response.set_status(code,msg);
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::set_body(std::string const & value) {
+ if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
+ throw exception("Call to set_status from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+
+ m_response.set_body(value);
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::append_header(std::string const & key,
+ std::string const & val)
+{
+ if (m_is_server) {
+ if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
+ // we are setting response headers for an incoming server connection
+ m_response.append_header(key,val);
+ } else {
+ throw exception("Call to append_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ } else {
+ if (m_internal_state == istate::USER_INIT) {
+ // we are setting initial headers for an outgoing client connection
+ m_request.append_header(key,val);
+ } else {
+ throw exception("Call to append_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ }
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::replace_header(std::string const & key,
+ std::string const & val)
+{
+ if (m_is_server) {
+ if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
+ // we are setting response headers for an incoming server connection
+ m_response.replace_header(key,val);
+ } else {
+ throw exception("Call to replace_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ } else {
+ if (m_internal_state == istate::USER_INIT) {
+ // we are setting initial headers for an outgoing client connection
+ m_request.replace_header(key,val);
+ } else {
+ throw exception("Call to replace_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ }
+}
+
+// TODO: EXCEPTION_FREE
+template <typename config>
+void connection<config>::remove_header(std::string const & key)
+{
+ if (m_is_server) {
+ if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
+ // we are setting response headers for an incoming server connection
+ m_response.remove_header(key);
+ } else {
+ throw exception("Call to remove_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ } else {
+ if (m_internal_state == istate::USER_INIT) {
+ // we are setting initial headers for an outgoing client connection
+ m_request.remove_header(key);
+ } else {
+ throw exception("Call to remove_header from invalid state",
+ error::make_error_code(error::invalid_state));
+ }
+ }
+}
+
+/// Defer HTTP Response until later
+/**
+ * Used in the http handler to defer the HTTP response for this connection
+ * until later. Handshake timers will be canceled and the connection will be
+ * left open until `send_http_response` or an equivalent is called.
+ *
+ * Warning: deferred connections won't time out and as a result can tie up
+ * resources.
+ *
+ * @return A status code, zero on success, non-zero otherwise
+ */
+template <typename config>
+lib::error_code connection<config>::defer_http_response() {
+ // Cancel handshake timer, otherwise the connection will time out and we'll
+ // close the connection before the app has a chance to send a response.
+ if (m_handshake_timer) {
+ m_handshake_timer->cancel();
+ m_handshake_timer.reset();
+ }
+
+ // Do something to signal deferral
+ m_http_state = session::http_state::deferred;
+
+ return lib::error_code();
+}
+
+/// Send deferred HTTP Response (exception free)
+/**
+ * Sends an http response to an HTTP connection that was deferred. This will
+ * send a complete response including all headers, status line, and body
+ * text. The connection will be closed afterwards.
+ *
+ * @since 0.6.0
+ *
+ * @param ec A status code, zero on success, non-zero otherwise
+ */
+template <typename config>
+void connection<config>::send_http_response(lib::error_code & ec) {
+ {
+ scoped_lock_type lock(m_connection_state_lock);
+ if (m_http_state != session::http_state::deferred) {
+ ec = error::make_error_code(error::invalid_state);
+ return;
+ }
+
+ m_http_state = session::http_state::body_written;
+ }
+
+ this->write_http_response(lib::error_code());
+ ec = lib::error_code();
+}
+
+template <typename config>
+void connection<config>::send_http_response() {
+ lib::error_code ec;
+ this->send_http_response(ec);
+ if (ec) {
+ throw exception(ec);
+ }
+}
+
+
+
+
+/******** logic thread ********/
+
+template <typename config>
+void connection<config>::start() {
+ m_alog.write(log::alevel::devel,"connection start");
+
+ if (m_internal_state != istate::USER_INIT) {
+ m_alog.write(log::alevel::devel,"Start called in invalid state");
+ this->terminate(error::make_error_code(error::invalid_state));
+ return;
+ }
+
+ m_internal_state = istate::TRANSPORT_INIT;
+
+ // Depending on how the transport implements init this function may return
+ // immediately and call handle_transport_init later or call
+ // handle_transport_init from this function.
+ transport_con_type::init(
+ lib::bind(
+ &type::handle_transport_init,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+}
+
+template <typename config>
+void connection<config>::handle_transport_init(lib::error_code const & ec) {
+ m_alog.write(log::alevel::devel,"connection handle_transport_init");
+
+ lib::error_code ecm = ec;
+
+ if (m_internal_state != istate::TRANSPORT_INIT) {
+ m_alog.write(log::alevel::devel,
+ "handle_transport_init must be called from transport init state");
+ ecm = error::make_error_code(error::invalid_state);
+ }
+
+ if (ecm) {
+ std::stringstream s;
+ s << "handle_transport_init received error: "<< ecm.message();
+ m_elog.write(log::elevel::rerror,s.str());
+
+ this->terminate(ecm);
+ return;
+ }
+
+ // At this point the transport is ready to read and write bytes.
+ if (m_is_server) {
+ m_internal_state = istate::READ_HTTP_REQUEST;
+ this->read_handshake(1);
+ } else {
+ // We are a client. Set the processor to the version specified in the
+ // config file and send a handshake request.
+ m_internal_state = istate::WRITE_HTTP_REQUEST;
+ m_processor = get_processor(config::client_version);
+ this->send_http_request();
+ }
+}
+
+template <typename config>
+void connection<config>::read_handshake(size_t num_bytes) {
+ m_alog.write(log::alevel::devel,"connection read_handshake");
+
+ if (m_open_handshake_timeout_dur > 0) {
+ m_handshake_timer = transport_con_type::set_timer(
+ m_open_handshake_timeout_dur,
+ lib::bind(
+ &type::handle_open_handshake_timeout,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ transport_con_type::async_read_at_least(
+ num_bytes,
+ m_buf,
+ config::connection_read_buffer_size,
+ lib::bind(
+ &type::handle_read_handshake,
+ type::get_shared(),
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+}
+
+// All exit paths for this function need to call write_http_response() or submit
+// a new read request with this function as the handler.
+template <typename config>
+void connection<config>::handle_read_handshake(lib::error_code const & ec,
+ size_t bytes_transferred)
+{
+ m_alog.write(log::alevel::devel,"connection handle_read_handshake");
+
+ lib::error_code ecm = ec;
+
+ if (!ecm) {
+ scoped_lock_type lock(m_connection_state_lock);
+
+ if (m_state == session::state::connecting) {
+ if (m_internal_state != istate::READ_HTTP_REQUEST) {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ } else if (m_state == session::state::closed) {
+ // The connection was canceled while the response was being sent,
+ // usually by the handshake timer. This is basically expected
+ // (though hopefully rare) and there is nothing we can do so ignore.
+ m_alog.write(log::alevel::devel,
+ "handle_read_handshake invoked after connection was closed");
+ return;
+ } else {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ }
+
+ if (ecm) {
+ if (ecm == transport::error::eof && m_state == session::state::closed) {
+ // we expect to get eof if the connection is closed already
+ m_alog.write(log::alevel::devel,
+ "got (expected) eof/state error from closed con");
+ return;
+ }
+
+ log_err(log::elevel::rerror,"handle_read_handshake",ecm);
+ this->terminate(ecm);
+ return;
+ }
+
+ // Boundaries checking. TODO: How much of this should be done?
+ if (bytes_transferred > config::connection_read_buffer_size) {
+ m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
+ this->terminate(make_error_code(error::general));
+ return;
+ }
+
+ size_t bytes_processed = 0;
+ try {
+ bytes_processed = m_request.consume(m_buf,bytes_transferred);
+ } catch (http::exception &e) {
+ // All HTTP exceptions will result in this request failing and an error
+ // response being returned. No more bytes will be read in this con.
+ m_response.set_status(e.m_error_code,e.m_error_msg);
+ this->write_http_response_error(error::make_error_code(error::http_parse_error));
+ return;
+ }
+
+ // More paranoid boundaries checking.
+ // TODO: Is this overkill?
+ if (bytes_processed > bytes_transferred) {
+ m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
+ this->terminate(make_error_code(error::general));
+ return;
+ }
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "bytes_transferred: " << bytes_transferred
+ << " bytes, bytes processed: " << bytes_processed << " bytes";
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ if (m_request.ready()) {
+ lib::error_code processor_ec = this->initialize_processor();
+ if (processor_ec) {
+ this->write_http_response_error(processor_ec);
+ return;
+ }
+
+ if (m_processor && m_processor->get_version() == 0) {
+ // Version 00 has an extra requirement to read some bytes after the
+ // handshake
+ if (bytes_transferred-bytes_processed >= 8) {
+ m_request.replace_header(
+ "Sec-WebSocket-Key3",
+ std::string(m_buf+bytes_processed,m_buf+bytes_processed+8)
+ );
+ bytes_processed += 8;
+ } else {
+ // TODO: need more bytes
+ m_alog.write(log::alevel::devel,"short key3 read");
+ m_response.set_status(http::status_code::internal_server_error);
+ this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3));
+ return;
+ }
+ }
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,m_request.raw());
+ if (!m_request.get_header("Sec-WebSocket-Key3").empty()) {
+ m_alog.write(log::alevel::devel,
+ utility::to_hex(m_request.get_header("Sec-WebSocket-Key3")));
+ }
+ }
+
+ // The remaining bytes in m_buf are frame data. Copy them to the
+ // beginning of the buffer and note the length. They will be read after
+ // the handshake completes and before more bytes are read.
+ std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
+ m_buf_cursor = bytes_transferred-bytes_processed;
+
+
+ m_internal_state = istate::PROCESS_HTTP_REQUEST;
+
+ // We have the complete request. Process it.
+ lib::error_code handshake_ec = this->process_handshake_request();
+
+ // Write a response if this is a websocket connection or if it is an
+ // HTTP connection for which the response has not been deferred or
+ // started yet by a different system (i.e. still in init state).
+ if (!m_is_http || m_http_state == session::http_state::init) {
+ this->write_http_response(handshake_ec);
+ }
+ } else {
+ // read at least 1 more byte
+ transport_con_type::async_read_at_least(
+ 1,
+ m_buf,
+ config::connection_read_buffer_size,
+ lib::bind(
+ &type::handle_read_handshake,
+ type::get_shared(),
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+ }
+}
+
+// write_http_response requires the request to be fully read and the connection
+// to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors
+// before the request is fully read (specifically at a point where we aren't
+// sure if the hybi00 key3 bytes need to be read). This method sets the correct
+// state and calls write_http_response
+template <typename config>
+void connection<config>::write_http_response_error(lib::error_code const & ec) {
+ if (m_internal_state != istate::READ_HTTP_REQUEST) {
+ m_alog.write(log::alevel::devel,
+ "write_http_response_error called in invalid state");
+ this->terminate(error::make_error_code(error::invalid_state));
+ return;
+ }
+
+ m_internal_state = istate::PROCESS_HTTP_REQUEST;
+
+ this->write_http_response(ec);
+}
+
+// All exit paths for this function need to call write_http_response() or submit
+// a new read request with this function as the handler.
+template <typename config>
+void connection<config>::handle_read_frame(lib::error_code const & ec,
+ size_t bytes_transferred)
+{
+ //m_alog.write(log::alevel::devel,"connection handle_read_frame");
+
+ lib::error_code ecm = ec;
+
+ if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+
+ if (ecm) {
+ log::level echannel = log::elevel::rerror;
+
+ if (ecm == transport::error::eof) {
+ if (m_state == session::state::closed) {
+ // we expect to get eof if the connection is closed already
+ // just ignore it
+ m_alog.write(log::alevel::devel,"got eof from closed con");
+ return;
+ } else if (m_state == session::state::closing && !m_is_server) {
+ // If we are a client we expect to get eof in the closing state,
+ // this is a signal to terminate our end of the connection after
+ // the closing handshake
+ terminate(lib::error_code());
+ return;
+ }
+ } else if (ecm == error::invalid_state) {
+ // In general, invalid state errors in the closed state are the
+ // result of handlers that were in the system already when the state
+ // changed and should be ignored as they pose no problems and there
+ // is nothing useful that we can do about them.
+ if (m_state == session::state::closed) {
+ m_alog.write(log::alevel::devel,
+ "handle_read_frame: got invalid istate in closed state");
+ return;
+ }
+ } else if (ecm == transport::error::tls_short_read) {
+ if (m_state == session::state::closed) {
+ // We expect to get a TLS short read if we try to read after the
+ // connection is closed. If this happens ignore and exit the
+ // read frame path.
+ terminate(lib::error_code());
+ return;
+ }
+ echannel = log::elevel::rerror;
+ } else if (ecm == transport::error::action_after_shutdown) {
+ echannel = log::elevel::info;
+ }
+
+ log_err(echannel, "handle_read_frame", ecm);
+ this->terminate(ecm);
+ return;
+ }
+
+ // Boundaries checking. TODO: How much of this should be done?
+ /*if (bytes_transferred > config::connection_read_buffer_size) {
+ m_elog.write(log::elevel::fatal,"Fatal boundaries checking error");
+ this->terminate(make_error_code(error::general));
+ return;
+ }*/
+
+ size_t p = 0;
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "p = " << p << " bytes transferred = " << bytes_transferred;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ while (p < bytes_transferred) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "calling consume with " << bytes_transferred-p << " bytes";
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ lib::error_code consume_ec;
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "Processing Bytes: " << utility::to_hex(reinterpret_cast<uint8_t*>(m_buf)+p,bytes_transferred-p);
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ p += m_processor->consume(
+ reinterpret_cast<uint8_t*>(m_buf)+p,
+ bytes_transferred-p,
+ consume_ec
+ );
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "bytes left after consume: " << bytes_transferred-p;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+ if (consume_ec) {
+ log_err(log::elevel::rerror, "consume", consume_ec);
+
+ if (config::drop_on_protocol_error) {
+ this->terminate(consume_ec);
+ return;
+ } else {
+ lib::error_code close_ec;
+ this->close(
+ processor::error::to_ws(consume_ec),
+ consume_ec.message(),
+ close_ec
+ );
+
+ if (close_ec) {
+ log_err(log::elevel::fatal, "Protocol error close frame ", close_ec);
+ this->terminate(close_ec);
+ return;
+ }
+ }
+ return;
+ }
+
+ if (m_processor->ready()) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "Complete message received. Dispatching";
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ message_ptr msg = m_processor->get_message();
+
+ if (!msg) {
+ m_alog.write(log::alevel::devel, "null message from m_processor");
+ } else if (!is_control(msg->get_opcode())) {
+ // data message, dispatch to user
+ if (m_state != session::state::open) {
+ m_elog.write(log::elevel::warn, "got non-close frame while closing");
+ } else if (m_message_handler) {
+ m_message_handler(m_connection_hdl, msg);
+ }
+ } else {
+ process_control_frame(msg);
+ }
+ }
+ }
+
+ read_frame();
+}
+
+/// Issue a new transport read unless reading is paused.
+template <typename config>
+void connection<config>::read_frame() {
+ if (!m_read_flag) {
+ return;
+ }
+
+ transport_con_type::async_read_at_least(
+ // std::min wont work with undefined static const values.
+ // TODO: is there a more elegant way to do this?
+ // Need to determine if requesting 1 byte or the exact number of bytes
+ // is better here. 1 byte lets us be a bit more responsive at a
+ // potential expense of additional runs through handle_read_frame
+ /*(m_processor->get_bytes_needed() > config::connection_read_buffer_size ?
+ config::connection_read_buffer_size : m_processor->get_bytes_needed())*/
+ 1,
+ m_buf,
+ config::connection_read_buffer_size,
+ m_handle_read_frame
+ );
+}
+
+template <typename config>
+lib::error_code connection<config>::initialize_processor() {
+ m_alog.write(log::alevel::devel,"initialize_processor");
+
+ // if it isn't a websocket handshake nothing to do.
+ if (!processor::is_websocket_handshake(m_request)) {
+ return lib::error_code();
+ }
+
+ int version = processor::get_websocket_version(m_request);
+
+ if (version < 0) {
+ m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
+ m_response.set_status(http::status_code::bad_request);
+ return error::make_error_code(error::invalid_version);
+ }
+
+ m_processor = get_processor(version);
+
+ // if the processor is not null we are done
+ if (m_processor) {
+ return lib::error_code();
+ }
+
+ // We don't have a processor for this version. Return bad request
+ // with Sec-WebSocket-Version header filled with values we do accept
+ m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version");
+ m_response.set_status(http::status_code::bad_request);
+
+ std::stringstream ss;
+ std::string sep;
+ std::vector<int>::const_iterator it;
+ for (it = versions_supported.begin(); it != versions_supported.end(); it++)
+ {
+ ss << sep << *it;
+ sep = ",";
+ }
+
+ m_response.replace_header("Sec-WebSocket-Version",ss.str());
+ return error::make_error_code(error::unsupported_version);
+}
+
+template <typename config>
+lib::error_code connection<config>::process_handshake_request() {
+ m_alog.write(log::alevel::devel,"process handshake request");
+
+ if (!processor::is_websocket_handshake(m_request)) {
+ // this is not a websocket handshake. Process as plain HTTP
+ m_alog.write(log::alevel::devel,"HTTP REQUEST");
+
+ // extract URI from request
+ m_uri = processor::get_uri_from_host(
+ m_request,
+ (transport_con_type::is_secure() ? "https" : "http")
+ );
+
+ if (!m_uri->get_valid()) {
+ m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
+ m_response.set_status(http::status_code::bad_request);
+ return error::make_error_code(error::invalid_uri);
+ }
+
+ if (m_http_handler) {
+ m_is_http = true;
+ m_http_handler(m_connection_hdl);
+
+ if (m_state == session::state::closed) {
+ return error::make_error_code(error::http_connection_ended);
+ }
+ } else {
+ set_status(http::status_code::upgrade_required);
+ return error::make_error_code(error::upgrade_required);
+ }
+
+ return lib::error_code();
+ }
+
+ lib::error_code ec = m_processor->validate_handshake(m_request);
+
+ // Validate: make sure all required elements are present.
+ if (ec){
+ // Not a valid handshake request
+ m_alog.write(log::alevel::devel, "Bad request " + ec.message());
+ m_response.set_status(http::status_code::bad_request);
+ return ec;
+ }
+
+ // Read extension parameters and set up values necessary for the end user
+ // to complete extension negotiation.
+ std::pair<lib::error_code,std::string> neg_results;
+ neg_results = m_processor->negotiate_extensions(m_request);
+
+ if (neg_results.first) {
+ // There was a fatal error in extension parsing that should result in
+ // a failed connection attempt.
+ m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
+ m_response.set_status(http::status_code::bad_request);
+ return neg_results.first;
+ } else {
+ // extension negotiation succeeded, set response header accordingly
+ // we don't send an empty extensions header because it breaks many
+ // clients.
+ if (neg_results.second.size() > 0) {
+ m_response.replace_header("Sec-WebSocket-Extensions",
+ neg_results.second);
+ }
+ }
+
+ // extract URI from request
+ m_uri = m_processor->get_uri(m_request);
+
+
+ if (!m_uri->get_valid()) {
+ m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
+ m_response.set_status(http::status_code::bad_request);
+ return error::make_error_code(error::invalid_uri);
+ }
+
+ // extract subprotocols
+ lib::error_code subp_ec = m_processor->extract_subprotocols(m_request,
+ m_requested_subprotocols);
+
+ if (subp_ec) {
+ // should we do anything?
+ }
+
+ // Ask application to validate the connection
+ if (!m_validate_handler || m_validate_handler(m_connection_hdl)) {
+ m_response.set_status(http::status_code::switching_protocols);
+
+ // Write the appropriate response headers based on request and
+ // processor version
+ ec = m_processor->process_handshake(m_request,m_subprotocol,m_response);
+
+ if (ec) {
+ std::stringstream s;
+ s << "Processing error: " << ec << "(" << ec.message() << ")";
+ m_alog.write(log::alevel::devel, s.str());
+
+ m_response.set_status(http::status_code::internal_server_error);
+ return ec;
+ }
+ } else {
+ // User application has rejected the handshake
+ m_alog.write(log::alevel::devel, "USER REJECT");
+
+ // Use Bad Request if the user handler did not provide a more
+ // specific http response error code.
+ // TODO: is there a better default?
+ if (m_response.get_status_code() == http::status_code::uninitialized) {
+ m_response.set_status(http::status_code::bad_request);
+ }
+
+ return error::make_error_code(error::rejected);
+ }
+
+ return lib::error_code();
+}
+
+template <typename config>
+void connection<config>::write_http_response(lib::error_code const & ec) {
+ m_alog.write(log::alevel::devel,"connection write_http_response");
+
+ if (ec == error::make_error_code(error::http_connection_ended)) {
+ m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
+ return;
+ }
+
+ if (m_response.get_status_code() == http::status_code::uninitialized) {
+ m_response.set_status(http::status_code::internal_server_error);
+ m_ec = error::make_error_code(error::general);
+ } else {
+ m_ec = ec;
+ }
+
+ m_response.set_version("HTTP/1.1");
+
+ // Set server header based on the user agent settings
+ if (m_response.get_header("Server").empty()) {
+ if (!m_user_agent.empty()) {
+ m_response.replace_header("Server",m_user_agent);
+ } else {
+ m_response.remove_header("Server");
+ }
+ }
+
+ // have the processor generate the raw bytes for the wire (if it exists)
+ if (m_processor) {
+ m_handshake_buffer = m_processor->get_raw(m_response);
+ } else {
+ // a processor wont exist for raw HTTP responses.
+ m_handshake_buffer = m_response.raw();
+ }
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
+ if (!m_response.get_header("Sec-WebSocket-Key3").empty()) {
+ m_alog.write(log::alevel::devel,
+ utility::to_hex(m_response.get_header("Sec-WebSocket-Key3")));
+ }
+ }
+
+ // write raw bytes
+ transport_con_type::async_write(
+ m_handshake_buffer.data(),
+ m_handshake_buffer.size(),
+ lib::bind(
+ &type::handle_write_http_response,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+}
+
+template <typename config>
+void connection<config>::handle_write_http_response(lib::error_code const & ec) {
+ m_alog.write(log::alevel::devel,"handle_write_http_response");
+
+ lib::error_code ecm = ec;
+
+ if (!ecm) {
+ scoped_lock_type lock(m_connection_state_lock);
+
+ if (m_state == session::state::connecting) {
+ if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ } else if (m_state == session::state::closed) {
+ // The connection was canceled while the response was being sent,
+ // usually by the handshake timer. This is basically expected
+ // (though hopefully rare) and there is nothing we can do so ignore.
+ m_alog.write(log::alevel::devel,
+ "handle_write_http_response invoked after connection was closed");
+ return;
+ } else {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ }
+
+ if (ecm) {
+ if (ecm == transport::error::eof && m_state == session::state::closed) {
+ // we expect to get eof if the connection is closed already
+ m_alog.write(log::alevel::devel,
+ "got (expected) eof/state error from closed con");
+ return;
+ }
+
+ log_err(log::elevel::rerror,"handle_write_http_response",ecm);
+ this->terminate(ecm);
+ return;
+ }
+
+ if (m_handshake_timer) {
+ m_handshake_timer->cancel();
+ m_handshake_timer.reset();
+ }
+
+ if (m_response.get_status_code() != http::status_code::switching_protocols)
+ {
+ /*if (m_processor || m_ec == error::http_parse_error ||
+ m_ec == error::invalid_version || m_ec == error::unsupported_version
+ || m_ec == error::upgrade_required)
+ {*/
+ if (!m_is_http) {
+ std::stringstream s;
+ s << "Handshake ended with HTTP error: "
+ << m_response.get_status_code();
+ m_elog.write(log::elevel::rerror,s.str());
+ } else {
+ // if this was not a websocket connection, we have written
+ // the expected response and the connection can be closed.
+
+ this->log_http_result();
+
+ if (m_ec) {
+ m_alog.write(log::alevel::devel,
+ "got to writing HTTP results with m_ec set: "+m_ec.message());
+ }
+ m_ec = make_error_code(error::http_connection_ended);
+ }
+
+ this->terminate(m_ec);
+ return;
+ }
+
+ this->log_open_result();
+
+ m_internal_state = istate::PROCESS_CONNECTION;
+ m_state = session::state::open;
+
+ if (m_open_handler) {
+ m_open_handler(m_connection_hdl);
+ }
+
+ this->handle_read_frame(lib::error_code(), m_buf_cursor);
+}
+
+template <typename config>
+void connection<config>::send_http_request() {
+ m_alog.write(log::alevel::devel,"connection send_http_request");
+
+ // TODO: origin header?
+
+ // Have the protocol processor fill in the appropriate fields based on the
+ // selected client version
+ if (m_processor) {
+ lib::error_code ec;
+ ec = m_processor->client_handshake_request(m_request,m_uri,
+ m_requested_subprotocols);
+
+ if (ec) {
+ log_err(log::elevel::fatal,"Internal library error: Processor",ec);
+ return;
+ }
+ } else {
+ m_elog.write(log::elevel::fatal,"Internal library error: missing processor");
+ return;
+ }
+
+ // Unless the user has overridden the user agent, send generic WS++ UA.
+ if (m_request.get_header("User-Agent").empty()) {
+ if (!m_user_agent.empty()) {
+ m_request.replace_header("User-Agent",m_user_agent);
+ } else {
+ m_request.remove_header("User-Agent");
+ }
+ }
+
+ m_handshake_buffer = m_request.raw();
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer);
+ }
+
+ if (m_open_handshake_timeout_dur > 0) {
+ m_handshake_timer = transport_con_type::set_timer(
+ m_open_handshake_timeout_dur,
+ lib::bind(
+ &type::handle_open_handshake_timeout,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ transport_con_type::async_write(
+ m_handshake_buffer.data(),
+ m_handshake_buffer.size(),
+ lib::bind(
+ &type::handle_send_http_request,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+}
+
+template <typename config>
+void connection<config>::handle_send_http_request(lib::error_code const & ec) {
+ m_alog.write(log::alevel::devel,"handle_send_http_request");
+
+ lib::error_code ecm = ec;
+
+ if (!ecm) {
+ scoped_lock_type lock(m_connection_state_lock);
+
+ if (m_state == session::state::connecting) {
+ if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
+ ecm = error::make_error_code(error::invalid_state);
+ } else {
+ m_internal_state = istate::READ_HTTP_RESPONSE;
+ }
+ } else if (m_state == session::state::closed) {
+ // The connection was canceled while the response was being sent,
+ // usually by the handshake timer. This is basically expected
+ // (though hopefully rare) and there is nothing we can do so ignore.
+ m_alog.write(log::alevel::devel,
+ "handle_send_http_request invoked after connection was closed");
+ return;
+ } else {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ }
+
+ if (ecm) {
+ if (ecm == transport::error::eof && m_state == session::state::closed) {
+ // we expect to get eof if the connection is closed already
+ m_alog.write(log::alevel::devel,
+ "got (expected) eof/state error from closed con");
+ return;
+ }
+
+ log_err(log::elevel::rerror,"handle_send_http_request",ecm);
+ this->terminate(ecm);
+ return;
+ }
+
+ transport_con_type::async_read_at_least(
+ 1,
+ m_buf,
+ config::connection_read_buffer_size,
+ lib::bind(
+ &type::handle_read_http_response,
+ type::get_shared(),
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+}
+
+template <typename config>
+void connection<config>::handle_read_http_response(lib::error_code const & ec,
+ size_t bytes_transferred)
+{
+ m_alog.write(log::alevel::devel,"handle_read_http_response");
+
+ lib::error_code ecm = ec;
+
+ if (!ecm) {
+ scoped_lock_type lock(m_connection_state_lock);
+
+ if (m_state == session::state::connecting) {
+ if (m_internal_state != istate::READ_HTTP_RESPONSE) {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ } else if (m_state == session::state::closed) {
+ // The connection was canceled while the response was being sent,
+ // usually by the handshake timer. This is basically expected
+ // (though hopefully rare) and there is nothing we can do so ignore.
+ m_alog.write(log::alevel::devel,
+ "handle_read_http_response invoked after connection was closed");
+ return;
+ } else {
+ ecm = error::make_error_code(error::invalid_state);
+ }
+ }
+
+ if (ecm) {
+ if (ecm == transport::error::eof && m_state == session::state::closed) {
+ // we expect to get eof if the connection is closed already
+ m_alog.write(log::alevel::devel,
+ "got (expected) eof/state error from closed con");
+ return;
+ }
+
+ log_err(log::elevel::rerror,"handle_read_http_response",ecm);
+ this->terminate(ecm);
+ return;
+ }
+
+ size_t bytes_processed = 0;
+ // TODO: refactor this to use error codes rather than exceptions
+ try {
+ bytes_processed = m_response.consume(m_buf,bytes_transferred);
+ } catch (http::exception & e) {
+ m_elog.write(log::elevel::rerror,
+ std::string("error in handle_read_http_response: ")+e.what());
+ this->terminate(make_error_code(error::general));
+ return;
+ }
+
+ m_alog.write(log::alevel::devel,std::string("Raw response: ")+m_response.raw());
+
+ if (m_response.headers_ready()) {
+ if (m_handshake_timer) {
+ m_handshake_timer->cancel();
+ m_handshake_timer.reset();
+ }
+
+ lib::error_code validate_ec = m_processor->validate_server_handshake_response(
+ m_request,
+ m_response
+ );
+ if (validate_ec) {
+ log_err(log::elevel::rerror,"Server handshake response",validate_ec);
+ this->terminate(validate_ec);
+ return;
+ }
+
+ // Read extension parameters and set up values necessary for the end
+ // user to complete extension negotiation.
+ std::pair<lib::error_code,std::string> neg_results;
+ neg_results = m_processor->negotiate_extensions(m_response);
+
+ if (neg_results.first) {
+ // There was a fatal error in extension negotiation. For the moment
+ // kill all connections that fail extension negotiation.
+
+ // TODO: deal with cases where the response is well formed but
+ // doesn't match the options requested by the client. Its possible
+ // that the best behavior in this cases is to log and continue with
+ // an unextended connection.
+ m_alog.write(log::alevel::devel, "Extension negotiation failed: "
+ + neg_results.first.message());
+ this->terminate(make_error_code(error::extension_neg_failed));
+ // TODO: close connection with reason 1010 (and list extensions)
+ }
+
+ // response is valid, connection can now be assumed to be open
+ m_internal_state = istate::PROCESS_CONNECTION;
+ m_state = session::state::open;
+
+ this->log_open_result();
+
+ if (m_open_handler) {
+ m_open_handler(m_connection_hdl);
+ }
+
+ // The remaining bytes in m_buf are frame data. Copy them to the
+ // beginning of the buffer and note the length. They will be read after
+ // the handshake completes and before more bytes are read.
+ std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
+ m_buf_cursor = bytes_transferred-bytes_processed;
+
+ this->handle_read_frame(lib::error_code(), m_buf_cursor);
+ } else {
+ transport_con_type::async_read_at_least(
+ 1,
+ m_buf,
+ config::connection_read_buffer_size,
+ lib::bind(
+ &type::handle_read_http_response,
+ type::get_shared(),
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+ }
+}
+
+template <typename config>
+void connection<config>::handle_open_handshake_timeout(
+ lib::error_code const & ec)
+{
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,"open handshake timer cancelled");
+ } else if (ec) {
+ m_alog.write(log::alevel::devel,
+ "open handle_open_handshake_timeout error: "+ec.message());
+ // TODO: ignore or fail here?
+ } else {
+ m_alog.write(log::alevel::devel,"open handshake timer expired");
+ terminate(make_error_code(error::open_handshake_timeout));
+ }
+}
+
+template <typename config>
+void connection<config>::handle_close_handshake_timeout(
+ lib::error_code const & ec)
+{
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,"asio close handshake timer cancelled");
+ } else if (ec) {
+ m_alog.write(log::alevel::devel,
+ "asio open handle_close_handshake_timeout error: "+ec.message());
+ // TODO: ignore or fail here?
+ } else {
+ m_alog.write(log::alevel::devel, "asio close handshake timer expired");
+ terminate(make_error_code(error::close_handshake_timeout));
+ }
+}
+
+template <typename config>
+void connection<config>::terminate(lib::error_code const & ec) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection terminate");
+ }
+
+ // Cancel close handshake timer
+ if (m_handshake_timer) {
+ m_handshake_timer->cancel();
+ m_handshake_timer.reset();
+ }
+
+ terminate_status tstat = unknown;
+ if (ec) {
+ m_ec = ec;
+ m_local_close_code = close::status::abnormal_close;
+ m_local_close_reason = ec.message();
+ }
+
+ // TODO: does any of this need a mutex?
+ if (m_is_http) {
+ m_http_state = session::http_state::closed;
+ }
+ if (m_state == session::state::connecting) {
+ m_state = session::state::closed;
+ tstat = failed;
+
+ // Log fail result here before socket is shut down and we can't get
+ // the remote address, etc anymore
+ if (m_ec != error::http_connection_ended) {
+ log_fail_result();
+ }
+ } else if (m_state != session::state::closed) {
+ m_state = session::state::closed;
+ tstat = closed;
+ } else {
+ m_alog.write(log::alevel::devel,
+ "terminate called on connection that was already terminated");
+ return;
+ }
+
+ // TODO: choose between shutdown and close based on error code sent
+
+ transport_con_type::async_shutdown(
+ lib::bind(
+ &type::handle_terminate,
+ type::get_shared(),
+ tstat,
+ lib::placeholders::_1
+ )
+ );
+}
+
+template <typename config>
+void connection<config>::handle_terminate(terminate_status tstat,
+ lib::error_code const & ec)
+{
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection handle_terminate");
+ }
+
+ if (ec) {
+ // there was an error actually shutting down the connection
+ log_err(log::elevel::devel,"handle_terminate",ec);
+ }
+
+ // clean shutdown
+ if (tstat == failed) {
+ if (m_ec != error::http_connection_ended) {
+ if (m_fail_handler) {
+ m_fail_handler(m_connection_hdl);
+ }
+ }
+ } else if (tstat == closed) {
+ if (m_close_handler) {
+ m_close_handler(m_connection_hdl);
+ }
+ log_close_result();
+ } else {
+ m_elog.write(log::elevel::rerror,"Unknown terminate_status");
+ }
+
+ // call the termination handler if it exists
+ // if it exists it might (but shouldn't) refer to a bad memory location.
+ // If it does, we don't care and should catch and ignore it.
+ if (m_termination_handler) {
+ try {
+ m_termination_handler(type::get_shared());
+ } catch (std::exception const & e) {
+ m_elog.write(log::elevel::warn,
+ std::string("termination_handler call failed. Reason was: ")+e.what());
+ }
+ }
+}
+
+template <typename config>
+void connection<config>::write_frame() {
+ //m_alog.write(log::alevel::devel,"connection write_frame");
+
+ {
+ scoped_lock_type lock(m_write_lock);
+
+ // Check the write flag. If true, there is an outstanding transport
+ // write already. In this case we just return. The write handler will
+ // start a new write if the write queue isn't empty. If false, we set
+ // the write flag and proceed to initiate a transport write.
+ if (m_write_flag) {
+ return;
+ }
+
+ // pull off all the messages that are ready to write.
+ // stop if we get a message marked terminal
+ message_ptr next_message = write_pop();
+ while (next_message) {
+ m_current_msgs.push_back(next_message);
+ if (!next_message->get_terminal()) {
+ next_message = write_pop();
+ } else {
+ next_message = message_ptr();
+ }
+ }
+
+ if (m_current_msgs.empty()) {
+ // there was nothing to send
+ return;
+ } else {
+ // At this point we own the next messages to be sent and are
+ // responsible for holding the write flag until they are
+ // successfully sent or there is some error
+ m_write_flag = true;
+ }
+ }
+
+ typename std::vector<message_ptr>::iterator it;
+ for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) {
+ std::string const & header = (*it)->get_header();
+ std::string const & payload = (*it)->get_payload();
+
+ m_send_buffer.push_back(transport::buffer(header.c_str(),header.size()));
+ m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size()));
+ }
+
+ // Print detailed send stats if those log levels are enabled
+ if (m_alog.static_test(log::alevel::frame_header)) {
+ if (m_alog.dynamic_test(log::alevel::frame_header)) {
+ std::stringstream general,header,payload;
+
+ general << "Dispatching write containing " << m_current_msgs.size()
+ <<" message(s) containing ";
+ header << "Header Bytes: \n";
+ payload << "Payload Bytes: \n";
+
+ size_t hbytes = 0;
+ size_t pbytes = 0;
+
+ for (size_t i = 0; i < m_current_msgs.size(); i++) {
+ hbytes += m_current_msgs[i]->get_header().size();
+ pbytes += m_current_msgs[i]->get_payload().size();
+
+
+ header << "[" << i << "] ("
+ << m_current_msgs[i]->get_header().size() << ") "
+ << utility::to_hex(m_current_msgs[i]->get_header()) << "\n";
+
+ if (m_alog.static_test(log::alevel::frame_payload)) {
+ if (m_alog.dynamic_test(log::alevel::frame_payload)) {
+ payload << "[" << i << "] ("
+ << m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
+ << (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
+ m_current_msgs[i]->get_payload() :
+ utility::to_hex(m_current_msgs[i]->get_payload())
+ )
+ << "\n";
+ }
+ }
+ }
+
+ general << hbytes << " header bytes and " << pbytes << " payload bytes";
+
+ m_alog.write(log::alevel::frame_header,general.str());
+ m_alog.write(log::alevel::frame_header,header.str());
+ m_alog.write(log::alevel::frame_payload,payload.str());
+ }
+ }
+
+ transport_con_type::async_write(
+ m_send_buffer,
+ m_write_frame_handler
+ );
+}
+
+template <typename config>
+void connection<config>::handle_write_frame(lib::error_code const & ec)
+{
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"connection handle_write_frame");
+ }
+
+ bool terminal = m_current_msgs.back()->get_terminal();
+
+ m_send_buffer.clear();
+ m_current_msgs.clear();
+ // TODO: recycle instead of deleting
+
+ if (ec) {
+ log_err(log::elevel::fatal,"handle_write_frame",ec);
+ this->terminate(ec);
+ return;
+ }
+
+ if (terminal) {
+ this->terminate(lib::error_code());
+ return;
+ }
+
+ bool needs_writing = false;
+ {
+ scoped_lock_type lock(m_write_lock);
+
+ // release write flag
+ m_write_flag = false;
+
+ needs_writing = !m_send_queue.empty();
+ }
+
+ if (needs_writing) {
+ transport_con_type::dispatch(lib::bind(
+ &type::write_frame,
+ type::get_shared()
+ ));
+ }
+}
+
+template <typename config>
+std::vector<int> const & connection<config>::get_supported_versions() const
+{
+ return versions_supported;
+}
+
+template <typename config>
+void connection<config>::process_control_frame(typename config::message_type::ptr msg)
+{
+ m_alog.write(log::alevel::devel,"process_control_frame");
+
+ frame::opcode::value op = msg->get_opcode();
+ lib::error_code ec;
+
+ std::stringstream s;
+ s << "Control frame received with opcode " << op;
+ m_alog.write(log::alevel::control,s.str());
+
+ if (m_state == session::state::closed) {
+ m_elog.write(log::elevel::warn,"got frame in state closed");
+ return;
+ }
+ if (op != frame::opcode::CLOSE && m_state != session::state::open) {
+ m_elog.write(log::elevel::warn,"got non-close frame in state closing");
+ return;
+ }
+
+ if (op == frame::opcode::PING) {
+ bool should_reply = true;
+
+ if (m_ping_handler) {
+ should_reply = m_ping_handler(m_connection_hdl, msg->get_payload());
+ }
+
+ if (should_reply) {
+ this->pong(msg->get_payload(),ec);
+ if (ec) {
+ log_err(log::elevel::devel,"Failed to send response pong",ec);
+ }
+ }
+ } else if (op == frame::opcode::PONG) {
+ if (m_pong_handler) {
+ m_pong_handler(m_connection_hdl, msg->get_payload());
+ }
+ if (m_ping_timer) {
+ m_ping_timer->cancel();
+ }
+ } else if (op == frame::opcode::CLOSE) {
+ m_alog.write(log::alevel::devel,"got close frame");
+ // record close code and reason somewhere
+
+ m_remote_close_code = close::extract_code(msg->get_payload(),ec);
+ if (ec) {
+ s.str("");
+ if (config::drop_on_protocol_error) {
+ s << "Received invalid close code " << m_remote_close_code
+ << " dropping connection per config.";
+ m_elog.write(log::elevel::devel,s.str());
+ this->terminate(ec);
+ } else {
+ s << "Received invalid close code " << m_remote_close_code
+ << " sending acknowledgement and closing";
+ m_elog.write(log::elevel::devel,s.str());
+ ec = send_close_ack(close::status::protocol_error,
+ "Invalid close code");
+ if (ec) {
+ log_err(log::elevel::devel,"send_close_ack",ec);
+ }
+ }
+ return;
+ }
+
+ m_remote_close_reason = close::extract_reason(msg->get_payload(),ec);
+ if (ec) {
+ if (config::drop_on_protocol_error) {
+ m_elog.write(log::elevel::devel,
+ "Received invalid close reason. Dropping connection per config");
+ this->terminate(ec);
+ } else {
+ m_elog.write(log::elevel::devel,
+ "Received invalid close reason. Sending acknowledgement and closing");
+ ec = send_close_ack(close::status::protocol_error,
+ "Invalid close reason");
+ if (ec) {
+ log_err(log::elevel::devel,"send_close_ack",ec);
+ }
+ }
+ return;
+ }
+
+ if (m_state == session::state::open) {
+ s.str("");
+ s << "Received close frame with code " << m_remote_close_code
+ << " and reason " << m_remote_close_reason;
+ m_alog.write(log::alevel::devel,s.str());
+
+ ec = send_close_ack();
+ if (ec) {
+ log_err(log::elevel::devel,"send_close_ack",ec);
+ }
+ } else if (m_state == session::state::closing && !m_was_clean) {
+ // ack of our close
+ m_alog.write(log::alevel::devel, "Got acknowledgement of close");
+
+ m_was_clean = true;
+
+ // If we are a server terminate the connection now. Clients should
+ // leave the connection open to give the server an opportunity to
+ // initiate the TCP close. The client's timer will handle closing
+ // its side of the connection if the server misbehaves.
+ //
+ // TODO: different behavior if the underlying transport doesn't
+ // support timers?
+ if (m_is_server) {
+ terminate(lib::error_code());
+ }
+ } else {
+ // spurious, ignore
+ m_elog.write(log::elevel::devel, "Got close frame in wrong state");
+ }
+ } else {
+ // got an invalid control opcode
+ m_elog.write(log::elevel::devel, "Got control frame with invalid opcode");
+ // initiate protocol error shutdown
+ }
+}
+
+template <typename config>
+lib::error_code connection<config>::send_close_ack(close::status::value code,
+ std::string const & reason)
+{
+ return send_close_frame(code,reason,true,m_is_server);
+}
+
+template <typename config>
+lib::error_code connection<config>::send_close_frame(close::status::value code,
+ std::string const & reason, bool ack, bool terminal)
+{
+ m_alog.write(log::alevel::devel,"send_close_frame");
+
+ // check for special codes
+
+ // If silent close is set, respect it and blank out close information
+ // Otherwise use whatever has been specified in the parameters. If
+ // parameters specifies close::status::blank then determine what to do
+ // based on whether or not this is an ack. If it is not an ack just
+ // send blank info. If it is an ack then echo the close information from
+ // the remote endpoint.
+ if (config::silent_close) {
+ m_alog.write(log::alevel::devel,"closing silently");
+ m_local_close_code = close::status::no_status;
+ m_local_close_reason.clear();
+ } else if (code != close::status::blank) {
+ m_alog.write(log::alevel::devel,"closing with specified codes");
+ m_local_close_code = code;
+ m_local_close_reason = reason;
+ } else if (!ack) {
+ m_alog.write(log::alevel::devel,"closing with no status code");
+ m_local_close_code = close::status::no_status;
+ m_local_close_reason.clear();
+ } else if (m_remote_close_code == close::status::no_status) {
+ m_alog.write(log::alevel::devel,
+ "acknowledging a no-status close with normal code");
+ m_local_close_code = close::status::normal;
+ m_local_close_reason.clear();
+ } else {
+ m_alog.write(log::alevel::devel,"acknowledging with remote codes");
+ m_local_close_code = m_remote_close_code;
+ m_local_close_reason = m_remote_close_reason;
+ }
+
+ std::stringstream s;
+ s << "Closing with code: " << m_local_close_code << ", and reason: "
+ << m_local_close_reason;
+ m_alog.write(log::alevel::devel,s.str());
+
+ message_ptr msg = m_msg_manager->get_message();
+ if (!msg) {
+ return error::make_error_code(error::no_outgoing_buffers);
+ }
+
+ lib::error_code ec = m_processor->prepare_close(m_local_close_code,
+ m_local_close_reason,msg);
+ if (ec) {
+ return ec;
+ }
+
+ // Messages flagged terminal will result in the TCP connection being dropped
+ // after the message has been written. This is typically used when servers
+ // send an ack and when any endpoint encounters a protocol error
+ if (terminal) {
+ msg->set_terminal(true);
+ }
+
+ m_state = session::state::closing;
+
+ if (ack) {
+ m_was_clean = true;
+ }
+
+ // Start a timer so we don't wait forever for the acknowledgement close
+ // frame
+ if (m_close_handshake_timeout_dur > 0) {
+ m_handshake_timer = transport_con_type::set_timer(
+ m_close_handshake_timeout_dur,
+ lib::bind(
+ &type::handle_close_handshake_timeout,
+ type::get_shared(),
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ bool needs_writing = false;
+ {
+ scoped_lock_type lock(m_write_lock);
+ write_push(msg);
+ needs_writing = !m_write_flag && !m_send_queue.empty();
+ }
+
+ if (needs_writing) {
+ transport_con_type::dispatch(lib::bind(
+ &type::write_frame,
+ type::get_shared()
+ ));
+ }
+
+ return lib::error_code();
+}
+
+template <typename config>
+typename connection<config>::processor_ptr
+connection<config>::get_processor(int version) const {
+ // TODO: allow disabling certain versions
+
+ processor_ptr p;
+
+ switch (version) {
+ case 0:
+ p = lib::make_shared<processor::hybi00<config> >(
+ transport_con_type::is_secure(),
+ m_is_server,
+ m_msg_manager
+ );
+ break;
+ case 7:
+ p = lib::make_shared<processor::hybi07<config> >(
+ transport_con_type::is_secure(),
+ m_is_server,
+ m_msg_manager,
+ lib::ref(m_rng)
+ );
+ break;
+ case 8:
+ p = lib::make_shared<processor::hybi08<config> >(
+ transport_con_type::is_secure(),
+ m_is_server,
+ m_msg_manager,
+ lib::ref(m_rng)
+ );
+ break;
+ case 13:
+ p = lib::make_shared<processor::hybi13<config> >(
+ transport_con_type::is_secure(),
+ m_is_server,
+ m_msg_manager,
+ lib::ref(m_rng)
+ );
+ break;
+ default:
+ return p;
+ }
+
+ // Settings not configured by the constructor
+ p->set_max_message_size(m_max_message_size);
+
+ return p;
+}
+
+template <typename config>
+void connection<config>::write_push(typename config::message_type::ptr msg)
+{
+ if (!msg) {
+ return;
+ }
+
+ m_send_buffer_size += msg->get_payload().size();
+ m_send_queue.push(msg);
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "write_push: message count: " << m_send_queue.size()
+ << " buffer size: " << m_send_buffer_size;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+}
+
+template <typename config>
+typename config::message_type::ptr connection<config>::write_pop()
+{
+ message_ptr msg;
+
+ if (m_send_queue.empty()) {
+ return msg;
+ }
+
+ msg = m_send_queue.front();
+
+ m_send_buffer_size -= msg->get_payload().size();
+ m_send_queue.pop();
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "write_pop: message count: " << m_send_queue.size()
+ << " buffer size: " << m_send_buffer_size;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+ return msg;
+}
+
+template <typename config>
+void connection<config>::log_open_result()
+{
+ std::stringstream s;
+
+ int version;
+ if (!processor::is_websocket_handshake(m_request)) {
+ version = -1;
+ } else {
+ version = processor::get_websocket_version(m_request);
+ }
+
+ // Connection Type
+ s << (version == -1 ? "HTTP" : "WebSocket") << " Connection ";
+
+ // Remote endpoint address
+ s << transport_con_type::get_remote_endpoint() << " ";
+
+ // Version string if WebSocket
+ if (version != -1) {
+ s << "v" << version << " ";
+ }
+
+ // User Agent
+ std::string ua = m_request.get_header("User-Agent");
+ if (ua.empty()) {
+ s << "\"\" ";
+ } else {
+ // check if there are any quotes in the user agent
+ s << "\"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
+ }
+
+ // URI
+ s << (m_uri ? m_uri->get_resource() : "NULL") << " ";
+
+ // Status code
+ s << m_response.get_status_code();
+
+ m_alog.write(log::alevel::connect,s.str());
+}
+
+template <typename config>
+void connection<config>::log_close_result()
+{
+ std::stringstream s;
+
+ s << "Disconnect "
+ << "close local:[" << m_local_close_code
+ << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason)
+ << "] remote:[" << m_remote_close_code
+ << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]";
+
+ m_alog.write(log::alevel::disconnect,s.str());
+}
+
+template <typename config>
+void connection<config>::log_fail_result()
+{
+ std::stringstream s;
+
+ int version = processor::get_websocket_version(m_request);
+
+ // Connection Type
+ s << "WebSocket Connection ";
+
+ // Remote endpoint address & WebSocket version
+ s << transport_con_type::get_remote_endpoint();
+ if (version < 0) {
+ s << " -";
+ } else {
+ s << " v" << version;
+ }
+
+ // User Agent
+ std::string ua = m_request.get_header("User-Agent");
+ if (ua.empty()) {
+ s << " \"\" ";
+ } else {
+ // check if there are any quotes in the user agent
+ s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
+ }
+
+ // URI
+ s << (m_uri ? m_uri->get_resource() : "-");
+
+ // HTTP Status code
+ s << " " << m_response.get_status_code();
+
+ // WebSocket++ error code & reason
+ s << " " << m_ec << " " << m_ec.message();
+
+ m_alog.write(log::alevel::fail,s.str());
+}
+
+template <typename config>
+void connection<config>::log_http_result() {
+ std::stringstream s;
+
+ if (processor::is_websocket_handshake(m_request)) {
+ m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
+ return;
+ }
+
+ // Connection Type
+ s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host"))
+ << " " << transport_con_type::get_remote_endpoint()
+ << " \"" << m_request.get_method()
+ << " " << (m_uri ? m_uri->get_resource() : "-")
+ << " " << m_request.get_version() << "\" " << m_response.get_status_code()
+ << " " << m_response.get_body().size();
+
+ // User Agent
+ std::string ua = m_request.get_header("User-Agent");
+ if (ua.empty()) {
+ s << " \"\" ";
+ } else {
+ // check if there are any quotes in the user agent
+ s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
+ }
+
+ m_alog.write(log::alevel::http,s.str());
+}
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_CONNECTION_IMPL_HPP
diff --git a/websocketpp/impl/endpoint_impl.hpp b/websocketpp/impl/endpoint_impl.hpp
new file mode 100644
index 00000000..e09cda95
--- /dev/null
+++ b/websocketpp/impl/endpoint_impl.hpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP
+#define WEBSOCKETPP_ENDPOINT_IMPL_HPP
+
+#include <string>
+
+namespace websocketpp {
+
+template <typename connection, typename config>
+typename endpoint<connection,config>::connection_ptr
+endpoint<connection,config>::create_connection() {
+ m_alog.write(log::alevel::devel,"create_connection");
+ //scoped_lock_type lock(m_state_lock);
+
+ /*if (m_state == STOPPING || m_state == STOPPED) {
+ return connection_ptr();
+ }*/
+
+ //scoped_lock_type guard(m_mutex);
+ // Create a connection on the heap and manage it using a shared pointer
+ connection_ptr con = lib::make_shared<connection_type>(m_is_server,
+ m_user_agent, lib::ref(m_alog), lib::ref(m_elog), lib::ref(m_rng));
+
+ connection_weak_ptr w(con);
+
+ // Create a weak pointer on the heap using that shared_ptr.
+ // Cast that weak pointer to void* and manage it using another shared_ptr
+ // connection_hdl hdl(reinterpret_cast<void*>(new connection_weak_ptr(con)));
+
+ con->set_handle(w);
+
+ // Copy default handlers from the endpoint
+ con->set_open_handler(m_open_handler);
+ con->set_close_handler(m_close_handler);
+ con->set_fail_handler(m_fail_handler);
+ con->set_ping_handler(m_ping_handler);
+ con->set_pong_handler(m_pong_handler);
+ con->set_pong_timeout_handler(m_pong_timeout_handler);
+ con->set_interrupt_handler(m_interrupt_handler);
+ con->set_http_handler(m_http_handler);
+ con->set_validate_handler(m_validate_handler);
+ con->set_message_handler(m_message_handler);
+
+ if (m_open_handshake_timeout_dur != config::timeout_open_handshake) {
+ con->set_open_handshake_timeout(m_open_handshake_timeout_dur);
+ }
+ if (m_close_handshake_timeout_dur != config::timeout_close_handshake) {
+ con->set_close_handshake_timeout(m_close_handshake_timeout_dur);
+ }
+ if (m_pong_timeout_dur != config::timeout_pong) {
+ con->set_pong_timeout(m_pong_timeout_dur);
+ }
+ if (m_max_message_size != config::max_message_size) {
+ con->set_max_message_size(m_max_message_size);
+ }
+ con->set_max_http_body_size(m_max_http_body_size);
+
+ lib::error_code ec;
+
+ ec = transport_type::init(con);
+ if (ec) {
+ m_elog.write(log::elevel::fatal,ec.message());
+ return connection_ptr();
+ }
+
+ return con;
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::interrupt(connection_hdl hdl, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+
+ m_alog.write(log::alevel::devel,"Interrupting connection");
+
+ ec = con->interrupt();
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::interrupt(connection_hdl hdl) {
+ lib::error_code ec;
+ interrupt(hdl,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::pause_reading(connection_hdl hdl, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+
+ ec = con->pause_reading();
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::pause_reading(connection_hdl hdl) {
+ lib::error_code ec;
+ pause_reading(hdl,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::resume_reading(connection_hdl hdl, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+
+ ec = con->resume_reading();
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::resume_reading(connection_hdl hdl) {
+ lib::error_code ec;
+ resume_reading(hdl,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send_http_response(connection_hdl hdl,
+ lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ con->send_http_response(ec);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send_http_response(connection_hdl hdl) {
+ lib::error_code ec;
+ send_http_response(hdl,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
+ frame::opcode::value op, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+
+ ec = con->send(payload,op);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
+ frame::opcode::value op)
+{
+ lib::error_code ec;
+ send(hdl,payload,op,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, void const * payload,
+ size_t len, frame::opcode::value op, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ ec = con->send(payload,len,op);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, void const * payload,
+ size_t len, frame::opcode::value op)
+{
+ lib::error_code ec;
+ send(hdl,payload,len,op,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, message_ptr msg,
+ lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ ec = con->send(msg);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::send(connection_hdl hdl, message_ptr msg) {
+ lib::error_code ec;
+ send(hdl,msg,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::close(connection_hdl hdl, close::status::value
+ const code, std::string const & reason,
+ lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ con->close(code,reason,ec);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::close(connection_hdl hdl, close::status::value
+ const code, std::string const & reason)
+{
+ lib::error_code ec;
+ close(hdl,code,reason,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
+ payload, lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ con->ping(payload,ec);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::ping(connection_hdl hdl, std::string const & payload)
+{
+ lib::error_code ec;
+ ping(hdl,payload,ec);
+ if (ec) { throw exception(ec); }
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload,
+ lib::error_code & ec)
+{
+ connection_ptr con = get_con_from_hdl(hdl,ec);
+ if (ec) {return;}
+ con->pong(payload,ec);
+}
+
+template <typename connection, typename config>
+void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload)
+{
+ lib::error_code ec;
+ pong(hdl,payload,ec);
+ if (ec) { throw exception(ec); }
+}
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_ENDPOINT_IMPL_HPP
diff --git a/websocketpp/impl/utilities_impl.hpp b/websocketpp/impl/utilities_impl.hpp
new file mode 100644
index 00000000..6f86e22f
--- /dev/null
+++ b/websocketpp/impl/utilities_impl.hpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP
+#define WEBSOCKETPP_UTILITIES_IMPL_HPP
+
+#include <algorithm>
+#include <string>
+
+namespace websocketpp {
+namespace utility {
+
+inline std::string to_lower(std::string const & in) {
+ std::string out = in;
+ std::transform(out.begin(),out.end(),out.begin(),::tolower);
+ return out;
+}
+
+inline std::string to_hex(std::string const & input) {
+ std::string output;
+ std::string hex = "0123456789ABCDEF";
+
+ for (size_t i = 0; i < input.size(); i++) {
+ output += hex[(input[i] & 0xF0) >> 4];
+ output += hex[input[i] & 0x0F];
+ output += " ";
+ }
+
+ return output;
+}
+
+inline std::string to_hex(uint8_t const * input, size_t length) {
+ std::string output;
+ std::string hex = "0123456789ABCDEF";
+
+ for (size_t i = 0; i < length; i++) {
+ output += hex[(input[i] & 0xF0) >> 4];
+ output += hex[input[i] & 0x0F];
+ output += " ";
+ }
+
+ return output;
+}
+
+inline std::string to_hex(const char* input,size_t length) {
+ return to_hex(reinterpret_cast<const uint8_t*>(input),length);
+}
+
+inline std::string string_replace_all(std::string subject, std::string const &
+ search, std::string const & replace)
+{
+ size_t pos = 0;
+ while((pos = subject.find(search, pos)) != std::string::npos) {
+ subject.replace(pos, search.length(), replace);
+ pos += replace.length();
+ }
+ return subject;
+}
+
+} // namespace utility
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_UTILITIES_IMPL_HPP
diff --git a/websocketpp/logger/basic.hpp b/websocketpp/logger/basic.hpp
new file mode 100644
index 00000000..84514130
--- /dev/null
+++ b/websocketpp/logger/basic.hpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_LOGGER_BASIC_HPP
+#define WEBSOCKETPP_LOGGER_BASIC_HPP
+
+/* Need a way to print a message to the log
+ *
+ * - timestamps
+ * - channels
+ * - thread safe
+ * - output to stdout or file
+ * - selective output channels, both compile time and runtime
+ * - named channels
+ * - ability to test whether a log message will be printed at compile time
+ *
+ */
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/stdint.hpp>
+#include <websocketpp/common/time.hpp>
+
+#include <ctime>
+#include <iostream>
+#include <iomanip>
+#include <string>
+
+namespace websocketpp {
+namespace log {
+
+/// Basic logger that outputs to an ostream
+template <typename concurrency, typename names>
+class basic {
+public:
+ basic<concurrency,names>(channel_type_hint::value h =
+ channel_type_hint::access)
+ : m_static_channels(0xffffffff)
+ , m_dynamic_channels(0)
+ , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
+
+ basic<concurrency,names>(std::ostream * out)
+ : m_static_channels(0xffffffff)
+ , m_dynamic_channels(0)
+ , m_out(out) {}
+
+ basic<concurrency,names>(level c, channel_type_hint::value h =
+ channel_type_hint::access)
+ : m_static_channels(c)
+ , m_dynamic_channels(0)
+ , m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
+
+ basic<concurrency,names>(level c, std::ostream * out)
+ : m_static_channels(c)
+ , m_dynamic_channels(0)
+ , m_out(out) {}
+
+ /// Destructor
+ ~basic<concurrency,names>() {}
+
+ /// Copy constructor
+ basic<concurrency,names>(basic<concurrency,names> const & other)
+ : m_static_channels(other.m_static_channels)
+ , m_dynamic_channels(other.m_dynamic_channels)
+ , m_out(other.m_out)
+ {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no copy assignment operator because of const member variables
+ basic<concurrency,names> & operator=(basic<concurrency,names> const &) = delete;
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
+ /// Move constructor
+ basic<concurrency,names>(basic<concurrency,names> && other)
+ : m_static_channels(other.m_static_channels)
+ , m_dynamic_channels(other.m_dynamic_channels)
+ , m_out(other.m_out)
+ {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no move assignment operator because of const member variables
+ basic<concurrency,names> & operator=(basic<concurrency,names> &&) = delete;
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
+
+ void set_ostream(std::ostream * out = &std::cout) {
+ m_out = out;
+ }
+
+ void set_channels(level channels) {
+ if (channels == names::none) {
+ clear_channels(names::all);
+ return;
+ }
+
+ scoped_lock_type lock(m_lock);
+ m_dynamic_channels |= (channels & m_static_channels);
+ }
+
+ void clear_channels(level channels) {
+ scoped_lock_type lock(m_lock);
+ m_dynamic_channels &= ~channels;
+ }
+
+ /// Write a string message to the given channel
+ /**
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level channel, std::string const & msg) {
+ scoped_lock_type lock(m_lock);
+ if (!this->dynamic_test(channel)) { return; }
+ *m_out << "[" << timestamp << "] "
+ << "[" << names::channel_name(channel) << "] "
+ << msg << "\n";
+ m_out->flush();
+ }
+
+ /// Write a cstring message to the given channel
+ /**
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level channel, char const * msg) {
+ scoped_lock_type lock(m_lock);
+ if (!this->dynamic_test(channel)) { return; }
+ *m_out << "[" << timestamp << "] "
+ << "[" << names::channel_name(channel) << "] "
+ << msg << "\n";
+ m_out->flush();
+ }
+
+ _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level channel) const {
+ return ((channel & m_static_channels) != 0);
+ }
+
+ bool dynamic_test(level channel) {
+ return ((channel & m_dynamic_channels) != 0);
+ }
+
+protected:
+ typedef typename concurrency::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency::mutex_type mutex_type;
+ mutex_type m_lock;
+
+private:
+ // The timestamp does not include the time zone, because on Windows with the
+ // default registry settings, the time zone would be written out in full,
+ // which would be obnoxiously verbose.
+ //
+ // TODO: find a workaround for this or make this format user settable
+ static std::ostream & timestamp(std::ostream & os) {
+ std::time_t t = std::time(NULL);
+ std::tm lt = lib::localtime(t);
+ #ifdef _WEBSOCKETPP_PUTTIME_
+ return os << std::put_time(&lt,"%Y-%m-%d %H:%M:%S");
+ #else // Falls back to strftime, which requires a temporary copy of the string.
+ char buffer[20];
+ size_t result = std::strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",&lt);
+ return os << (result == 0 ? "Unknown" : buffer);
+ #endif
+ }
+
+ level const m_static_channels;
+ level m_dynamic_channels;
+ std::ostream * m_out;
+};
+
+} // log
+} // websocketpp
+
+#endif // WEBSOCKETPP_LOGGER_BASIC_HPP
diff --git a/websocketpp/logger/levels.hpp b/websocketpp/logger/levels.hpp
new file mode 100644
index 00000000..cd7ccd69
--- /dev/null
+++ b/websocketpp/logger/levels.hpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_LOGGER_LEVELS_HPP
+#define WEBSOCKETPP_LOGGER_LEVELS_HPP
+
+#include <websocketpp/common/stdint.hpp>
+
+namespace websocketpp {
+namespace log {
+
+/// Type of a channel package
+typedef uint32_t level;
+
+/// Package of values for hinting at the nature of a given logger.
+/**
+ * Used by the library to signal to the logging class a hint that it can use to
+ * set itself up. For example, the `access` hint indicates that it is an access
+ * log that might be suitable for being printed to an access log file or to cout
+ * whereas `error` might be suitable for an error log file or cerr.
+ */
+struct channel_type_hint {
+ /// Type of a channel type hint value
+ typedef uint32_t value;
+
+ /// No information
+ static value const none = 0;
+ /// Access log
+ static value const access = 1;
+ /// Error log
+ static value const error = 2;
+};
+
+/// Package of log levels for logging errors
+struct elevel {
+ /// Special aggregate value representing "no levels"
+ static level const none = 0x0;
+ /// Low level debugging information (warning: very chatty)
+ static level const devel = 0x1;
+ /// Information about unusual system states or other minor internal library
+ /// problems, less chatty than devel.
+ static level const library = 0x2;
+ /// Information about minor configuration problems or additional information
+ /// about other warnings.
+ static level const info = 0x4;
+ /// Information about important problems not severe enough to terminate
+ /// connections.
+ static level const warn = 0x8;
+ /// Recoverable error. Recovery may mean cleanly closing the connection with
+ /// an appropriate error code to the remote endpoint.
+ static level const rerror = 0x10;
+ /// Unrecoverable error. This error will trigger immediate unclean
+ /// termination of the connection or endpoint.
+ static level const fatal = 0x20;
+ /// Special aggregate value representing "all levels"
+ static level const all = 0xffffffff;
+
+ /// Get the textual name of a channel given a channel id
+ /**
+ * The id must be that of a single channel. Passing an aggregate channel
+ * package results in undefined behavior.
+ *
+ * @param channel The channel id to look up.
+ *
+ * @return The name of the specified channel.
+ */
+ static char const * channel_name(level channel) {
+ switch(channel) {
+ case devel:
+ return "devel";
+ case library:
+ return "library";
+ case info:
+ return "info";
+ case warn:
+ return "warning";
+ case rerror:
+ return "error";
+ case fatal:
+ return "fatal";
+ default:
+ return "unknown";
+ }
+ }
+};
+
+/// Package of log levels for logging access events
+struct alevel {
+ /// Special aggregate value representing "no levels"
+ static level const none = 0x0;
+ /// Information about new connections
+ /**
+ * One line for each new connection that includes a host of information
+ * including: the remote address, websocket version, requested resource,
+ * http code, remote user agent
+ */
+ static level const connect = 0x1;
+ /// One line for each closed connection. Includes closing codes and reasons.
+ static level const disconnect = 0x2;
+ /// One line per control frame
+ static level const control = 0x4;
+ /// One line per frame, includes the full frame header
+ static level const frame_header = 0x8;
+ /// One line per frame, includes the full message payload (warning: chatty)
+ static level const frame_payload = 0x10;
+ /// Reserved
+ static level const message_header = 0x20;
+ /// Reserved
+ static level const message_payload = 0x40;
+ /// Reserved
+ static level const endpoint = 0x80;
+ /// Extra information about opening handshakes
+ static level const debug_handshake = 0x100;
+ /// Extra information about closing handshakes
+ static level const debug_close = 0x200;
+ /// Development messages (warning: very chatty)
+ static level const devel = 0x400;
+ /// Special channel for application specific logs. Not used by the library.
+ static level const app = 0x800;
+ /// Access related to HTTP requests
+ static level const http = 0x1000;
+ /// One line for each failed WebSocket connection with details
+ static level const fail = 0x2000;
+ /// Aggregate package representing the commonly used core access channels
+ /// Connect, Disconnect, Fail, and HTTP
+ static level const access_core = 0x00003003;
+ /// Special aggregate value representing "all levels"
+ static level const all = 0xffffffff;
+
+ /// Get the textual name of a channel given a channel id
+ /**
+ * Get the textual name of a channel given a channel id. The id must be that
+ * of a single channel. Passing an aggregate channel package results in
+ * undefined behavior.
+ *
+ * @param channel The channelid to look up.
+ *
+ * @return The name of the specified channel.
+ */
+ static char const * channel_name(level channel) {
+ switch(channel) {
+ case connect:
+ return "connect";
+ case disconnect:
+ return "disconnect";
+ case control:
+ return "control";
+ case frame_header:
+ return "frame_header";
+ case frame_payload:
+ return "frame_payload";
+ case message_header:
+ return "message_header";
+ case message_payload:
+ return "message_payload";
+ case endpoint:
+ return "endpoint";
+ case debug_handshake:
+ return "debug_handshake";
+ case debug_close:
+ return "debug_close";
+ case devel:
+ return "devel";
+ case app:
+ return "application";
+ case http:
+ return "http";
+ case fail:
+ return "fail";
+ default:
+ return "unknown";
+ }
+ }
+};
+
+} // logger
+} // websocketpp
+
+#endif //WEBSOCKETPP_LOGGER_LEVELS_HPP
diff --git a/websocketpp/logger/stub.hpp b/websocketpp/logger/stub.hpp
new file mode 100644
index 00000000..2db6da7d
--- /dev/null
+++ b/websocketpp/logger/stub.hpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_LOGGER_STUB_HPP
+#define WEBSOCKETPP_LOGGER_STUB_HPP
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/cpp11.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace log {
+
+/// Stub logger that ignores all input
+class stub {
+public:
+ /// Construct the logger
+ /**
+ * @param hint A channel type specific hint for how to construct the logger
+ */
+ explicit stub(channel_type_hint::value) {}
+
+ /// Construct the logger
+ /**
+ * @param default_channels A set of channels to statically enable
+ * @param hint A channel type specific hint for how to construct the logger
+ */
+ stub(level, channel_type_hint::value) {}
+ _WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {}
+
+ /// Dynamically enable the given list of channels
+ /**
+ * All operations on the stub logger are no-ops and all arguments are
+ * ignored
+ *
+ * @param channels The package of channels to enable
+ */
+ void set_channels(level) {}
+
+ /// Dynamically disable the given list of channels
+ /**
+ * All operations on the stub logger are no-ops and all arguments are
+ * ignored
+ *
+ * @param channels The package of channels to disable
+ */
+ void clear_channels(level) {}
+
+ /// Write a string message to the given channel
+ /**
+ * Writing on the stub logger is a no-op and all arguments are ignored
+ *
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level, std::string const &) {}
+
+ /// Write a cstring message to the given channel
+ /**
+ * Writing on the stub logger is a no-op and all arguments are ignored
+ *
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level, char const *) {}
+
+ /// Test whether a channel is statically enabled
+ /**
+ * The stub logger has no channels so all arguments are ignored and
+ * `static_test` always returns false.
+ *
+ * @param channel The package of channels to test
+ */
+ _WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level) const {
+ return false;
+ }
+
+ /// Test whether a channel is dynamically enabled
+ /**
+ * The stub logger has no channels so all arguments are ignored and
+ * `dynamic_test` always returns false.
+ *
+ * @param channel The package of channels to test
+ */
+ bool dynamic_test(level) {
+ return false;
+ }
+};
+
+} // log
+} // websocketpp
+
+#endif // WEBSOCKETPP_LOGGER_STUB_HPP
diff --git a/websocketpp/logger/syslog.hpp b/websocketpp/logger/syslog.hpp
new file mode 100644
index 00000000..513abee4
--- /dev/null
+++ b/websocketpp/logger/syslog.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * The initial version of this logging policy was contributed to the WebSocket++
+ * project by Tom Hughes.
+ */
+
+#ifndef WEBSOCKETPP_LOGGER_SYSLOG_HPP
+#define WEBSOCKETPP_LOGGER_SYSLOG_HPP
+
+#include <syslog.h>
+
+#include <websocketpp/logger/basic.hpp>
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+namespace websocketpp {
+namespace log {
+
+/// Basic logger that outputs to syslog
+template <typename concurrency, typename names>
+class syslog : public basic<concurrency, names> {
+public:
+ typedef basic<concurrency, names> base;
+
+ /// Construct the logger
+ /**
+ * @param hint A channel type specific hint for how to construct the logger
+ */
+ syslog<concurrency,names>(channel_type_hint::value hint =
+ channel_type_hint::access)
+ : basic<concurrency,names>(hint), m_channel_type_hint(hint) {}
+
+ /// Construct the logger
+ /**
+ * @param channels A set of channels to statically enable
+ * @param hint A channel type specific hint for how to construct the logger
+ */
+ syslog<concurrency,names>(level channels, channel_type_hint::value hint =
+ channel_type_hint::access)
+ : basic<concurrency,names>(channels, hint), m_channel_type_hint(hint) {}
+
+ /// Write a string message to the given channel
+ /**
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level channel, std::string const & msg) {
+ write(channel, msg.c_str());
+ }
+
+ /// Write a cstring message to the given channel
+ /**
+ * @param channel The channel to write to
+ * @param msg The message to write
+ */
+ void write(level channel, char const * msg) {
+ scoped_lock_type lock(base::m_lock);
+ if (!this->dynamic_test(channel)) { return; }
+ ::syslog(syslog_priority(channel), "[%s] %s",
+ names::channel_name(channel), msg);
+ }
+private:
+ typedef typename base::scoped_lock_type scoped_lock_type;
+
+ /// The default level is used for all access logs and any error logs that
+ /// don't trivially map to one of the standard syslog levels.
+ static int const default_level = LOG_INFO;
+
+ /// retrieve the syslog priority code given a WebSocket++ channel
+ /**
+ * @param channel The level to look up
+ * @return The syslog level associated with `channel`
+ */
+ int syslog_priority(level channel) const {
+ if (m_channel_type_hint == channel_type_hint::access) {
+ return syslog_priority_access(channel);
+ } else {
+ return syslog_priority_error(channel);
+ }
+ }
+
+ /// retrieve the syslog priority code given a WebSocket++ error channel
+ /**
+ * @param channel The level to look up
+ * @return The syslog level associated with `channel`
+ */
+ int syslog_priority_error(level channel) const {
+ switch (channel) {
+ case elevel::devel:
+ return LOG_DEBUG;
+ case elevel::library:
+ return LOG_DEBUG;
+ case elevel::info:
+ return LOG_INFO;
+ case elevel::warn:
+ return LOG_WARNING;
+ case elevel::rerror:
+ return LOG_ERR;
+ case elevel::fatal:
+ return LOG_CRIT;
+ default:
+ return default_level;
+ }
+ }
+
+ /// retrieve the syslog priority code given a WebSocket++ access channel
+ /**
+ * @param channel The level to look up
+ * @return The syslog level associated with `channel`
+ */
+ _WEBSOCKETPP_CONSTEXPR_TOKEN_ int syslog_priority_access(level) const {
+ return default_level;
+ }
+
+ channel_type_hint::value m_channel_type_hint;
+};
+
+} // log
+} // websocketpp
+
+#endif // WEBSOCKETPP_LOGGER_SYSLOG_HPP
diff --git a/websocketpp/message_buffer/alloc.hpp b/websocketpp/message_buffer/alloc.hpp
new file mode 100644
index 00000000..75d89766
--- /dev/null
+++ b/websocketpp/message_buffer/alloc.hpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
+#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/frame.hpp>
+
+namespace websocketpp {
+namespace message_buffer {
+namespace alloc {
+
+/// A connection message manager that allocates a new message for each
+/// request.
+template <typename message>
+class con_msg_manager
+ : public lib::enable_shared_from_this<con_msg_manager<message> >
+{
+public:
+ typedef con_msg_manager<message> type;
+ typedef lib::shared_ptr<con_msg_manager> ptr;
+ typedef lib::weak_ptr<con_msg_manager> weak_ptr;
+
+ typedef typename message::ptr message_ptr;
+
+ /// Get an empty message buffer
+ /**
+ * @return A shared pointer to an empty new message
+ */
+ message_ptr get_message() {
+ return message_ptr(lib::make_shared<message>(type::shared_from_this()));
+ }
+
+ /// Get a message buffer with specified size and opcode
+ /**
+ * @param op The opcode to use
+ * @param size Minimum size in bytes to request for the message payload.
+ *
+ * @return A shared pointer to a new message with specified size.
+ */
+ message_ptr get_message(frame::opcode::value op,size_t size) {
+ return message_ptr(lib::make_shared<message>(type::shared_from_this(),op,size));
+ }
+
+ /// Recycle a message
+ /**
+ * This method shouldn't be called. If it is, return false to indicate an
+ * error. The rest of the method recycle chain should notice this and free
+ * the memory.
+ *
+ * @param msg The message to be recycled.
+ *
+ * @return true if the message was successfully recycled, false otherwse.
+ */
+ bool recycle(message *) {
+ return false;
+ }
+};
+
+/// An endpoint message manager that allocates a new manager for each
+/// connection.
+template <typename con_msg_manager>
+class endpoint_msg_manager {
+public:
+ typedef typename con_msg_manager::ptr con_msg_man_ptr;
+
+ /// Get a pointer to a connection message manager
+ /**
+ * @return A pointer to the requested connection message manager.
+ */
+ con_msg_man_ptr get_manager() const {
+ return con_msg_man_ptr(lib::make_shared<con_msg_manager>());
+ }
+};
+
+} // namespace alloc
+} // namespace message_buffer
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
diff --git a/websocketpp/message_buffer/message.hpp b/websocketpp/message_buffer/message.hpp
new file mode 100644
index 00000000..da36e200
--- /dev/null
+++ b/websocketpp/message_buffer/message.hpp
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP
+#define WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/frame.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace message_buffer {
+
+/* # message:
+ * object that stores a message while it is being sent or received. Contains
+ * the message payload itself, the message header, the extension data, and the
+ * opcode.
+ *
+ * # connection_message_manager:
+ * An object that manages all of the message_buffers associated with a given
+ * connection. Implements the get_message_buffer(size) method that returns
+ * a message buffer at least size bytes long.
+ *
+ * Message buffers are reference counted with shared ownership semantics. Once
+ * requested from the manager the requester and it's associated downstream code
+ * may keep a pointer to the message indefinitely at a cost of extra resource
+ * usage. Once the reference count drops to the point where the manager is the
+ * only reference the messages is recycled using whatever method is implemented
+ * in the manager.
+ *
+ * # endpoint_message_manager:
+ * An object that manages connection_message_managers. Implements the
+ * get_message_manager() method. This is used once by each connection to
+ * request the message manager that they are supposed to use to manage message
+ * buffers for their own use.
+ *
+ * TYPES OF CONNECTION_MESSAGE_MANAGERS
+ * - allocate a message with the exact size every time one is requested
+ * - maintain a pool of pre-allocated messages and return one when needed.
+ * Recycle previously used messages back into the pool
+ *
+ * TYPES OF ENDPOINT_MESSAGE_MANAGERS
+ * - allocate a new connection manager for each connection. Message pools
+ * become connection specific. This increases memory usage but improves
+ * concurrency.
+ * - allocate a single connection manager and share a pointer to it with all
+ * connections created by this endpoint. The message pool will be shared
+ * among all connections, improving memory usage and performance at the cost
+ * of reduced concurrency
+ */
+
+
+/// Represents a buffer for a single WebSocket message.
+/**
+ *
+ *
+ */
+template <template<class> class con_msg_manager>
+class message {
+public:
+ typedef lib::shared_ptr<message> ptr;
+
+ typedef con_msg_manager<message> con_msg_man_type;
+ typedef typename con_msg_man_type::ptr con_msg_man_ptr;
+ typedef typename con_msg_man_type::weak_ptr con_msg_man_weak_ptr;
+
+ /// Construct an empty message
+ /**
+ * Construct an empty message
+ */
+ message(const con_msg_man_ptr manager)
+ : m_manager(manager)
+ , m_prepared(false)
+ , m_fin(true)
+ , m_terminal(false)
+ , m_compressed(false) {}
+
+ /// Construct a message and fill in some values
+ /**
+ *
+ */
+ message(const con_msg_man_ptr manager, frame::opcode::value op, size_t size = 128)
+ : m_manager(manager)
+ , m_opcode(op)
+ , m_prepared(false)
+ , m_fin(true)
+ , m_terminal(false)
+ , m_compressed(false)
+ {
+ m_payload.reserve(size);
+ }
+
+ /// Return whether or not the message has been prepared for sending
+ /**
+ * The prepared flag indicates that the message has been prepared by a
+ * websocket protocol processor and is ready to be written to the wire.
+ *
+ * @return whether or not the message has been prepared for sending
+ */
+ bool get_prepared() const {
+ return m_prepared;
+ }
+
+ /// Set or clear the flag that indicates that the message has been prepared
+ /**
+ * This flag should not be set by end user code without a very good reason.
+ *
+ * @param value The value to set the prepared flag to
+ */
+ void set_prepared(bool value) {
+ m_prepared = value;
+ }
+
+ /// Return whether or not the message is flagged as compressed
+ /**
+ * @return whether or not the message is/should be compressed
+ */
+ bool get_compressed() const {
+ return m_compressed;
+ }
+
+ /// Set or clear the compression flag
+ /**
+ * Setting the compression flag indicates that the data in this message
+ * would benefit from compression. If both endpoints negotiate a compression
+ * extension WebSocket++ will attempt to compress messages with this flag.
+ * Setting this flag does not guarantee that the message will be compressed.
+ *
+ * @param value The value to set the compressed flag to
+ */
+ void set_compressed(bool value) {
+ m_compressed = value;
+ }
+
+ /// Get whether or not the message is terminal
+ /**
+ * Messages can be flagged as terminal, which results in the connection
+ * being close after they are written rather than the implementation going
+ * on to the next message in the queue. This is typically used internally
+ * for close messages only.
+ *
+ * @return Whether or not this message is marked terminal
+ */
+ bool get_terminal() const {
+ return m_terminal;
+ }
+
+ /// Set the terminal flag
+ /**
+ * This flag should not be set by end user code without a very good reason.
+ *
+ * @see get_terminal()
+ *
+ * @param value The value to set the terminal flag to.
+ */
+ void set_terminal(bool value) {
+ m_terminal = value;
+ }
+ /// Read the fin bit
+ /**
+ * A message with the fin bit set will be sent as the last message of its
+ * sequence. A message with the fin bit cleared will require subsequent
+ * frames of opcode continuation until one of them has the fin bit set.
+ *
+ * The remote end likely will not deliver any bytes until the frame with the fin
+ * bit set has been received.
+ *
+ * @return Whether or not the fin bit is set
+ */
+ bool get_fin() const {
+ return m_fin;
+ }
+
+ /// Set the fin bit
+ /**
+ * @see get_fin for a more detailed explaination of the fin bit
+ *
+ * @param value The value to set the fin bit to.
+ */
+ void set_fin(bool value) {
+ m_fin = value;
+ }
+
+ /// Return the message opcode
+ frame::opcode::value get_opcode() const {
+ return m_opcode;
+ }
+
+ /// Set the opcode
+ void set_opcode(frame::opcode::value op) {
+ m_opcode = op;
+ }
+
+ /// Return the prepared frame header
+ /**
+ * This value is typically set by a websocket protocol processor
+ * and shouldn't be tampered with.
+ */
+ std::string const & get_header() const {
+ return m_header;
+ }
+
+ /// Set prepared frame header
+ /**
+ * Under normal circumstances this should not be called by end users
+ *
+ * @param header A string to set the header to.
+ */
+ void set_header(std::string const & header) {
+ m_header = header;
+ }
+
+ std::string const & get_extension_data() const {
+ return m_extension_data;
+ }
+
+ /// Get a reference to the payload string
+ /**
+ * @return A const reference to the message's payload string
+ */
+ std::string const & get_payload() const {
+ return m_payload;
+ }
+
+ /// Get a non-const reference to the payload string
+ /**
+ * @return A reference to the message's payload string
+ */
+ std::string & get_raw_payload() {
+ return m_payload;
+ }
+
+ /// Set payload data
+ /**
+ * Set the message buffer's payload to the given value.
+ *
+ * @param payload A string to set the payload to.
+ */
+ void set_payload(std::string const & payload) {
+ m_payload = payload;
+ }
+
+ /// Set payload data
+ /**
+ * Set the message buffer's payload to the given value.
+ *
+ * @param payload A pointer to a data array to set to.
+ * @param len The length of new payload in bytes.
+ */
+ void set_payload(void const * payload, size_t len) {
+ m_payload.reserve(len);
+ char const * pl = static_cast<char const *>(payload);
+ m_payload.assign(pl, pl + len);
+ }
+
+ /// Append payload data
+ /**
+ * Append data to the message buffer's payload.
+ *
+ * @param payload A string containing the data array to append.
+ */
+ void append_payload(std::string const & payload) {
+ m_payload.append(payload);
+ }
+
+ /// Append payload data
+ /**
+ * Append data to the message buffer's payload.
+ *
+ * @param payload A pointer to a data array to append
+ * @param len The length of payload in bytes
+ */
+ void append_payload(void const * payload, size_t len) {
+ m_payload.reserve(m_payload.size()+len);
+ m_payload.append(static_cast<char const *>(payload),len);
+ }
+
+ /// Recycle the message
+ /**
+ * A request to recycle this message was received. Forward that request to
+ * the connection message manager for processing. Errors and exceptions
+ * from the manager's recycle member function should be passed back up the
+ * call chain. The caller to message::recycle will deal with them.
+ *
+ * Recycle must *only* be called by the message shared_ptr's destructor.
+ * Once recycled successfully, ownership of the memory has been passed to
+ * another system and must not be accessed again.
+ *
+ * @return true if the message was successfully recycled, false otherwise.
+ */
+ bool recycle() {
+ con_msg_man_ptr shared = m_manager.lock();
+
+ if (shared) {
+ return shared->recycle(this);
+ } else {
+ return false;
+ }
+ }
+private:
+ con_msg_man_weak_ptr m_manager;
+ std::string m_header;
+ std::string m_extension_data;
+ std::string m_payload;
+ frame::opcode::value m_opcode;
+ bool m_prepared;
+ bool m_fin;
+ bool m_terminal;
+ bool m_compressed;
+};
+
+} // namespace message_buffer
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_MESSAGE_BUFFER_MESSAGE_HPP
diff --git a/websocketpp/message_buffer/pool.hpp b/websocketpp/message_buffer/pool.hpp
new file mode 100644
index 00000000..3af9e02a
--- /dev/null
+++ b/websocketpp/message_buffer/pool.hpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
+#define WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
+
+#include <websocketpp/common/memory.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace message_buffer {
+
+/* # message:
+ * object that stores a message while it is being sent or received. Contains
+ * the message payload itself, the message header, the extension data, and the
+ * opcode.
+ *
+ * # connection_message_manager:
+ * An object that manages all of the message_buffers associated with a given
+ * connection. Implements the get_message_buffer(size) method that returns
+ * a message buffer at least size bytes long.
+ *
+ * Message buffers are reference counted with shared ownership semantics. Once
+ * requested from the manager the requester and it's associated downstream code
+ * may keep a pointer to the message indefinitely at a cost of extra resource
+ * usage. Once the reference count drops to the point where the manager is the
+ * only reference the messages is recycled using whatever method is implemented
+ * in the manager.
+ *
+ * # endpoint_message_manager:
+ * An object that manages connection_message_managers. Implements the
+ * get_message_manager() method. This is used once by each connection to
+ * request the message manager that they are supposed to use to manage message
+ * buffers for their own use.
+ *
+ * TYPES OF CONNECTION_MESSAGE_MANAGERS
+ * - allocate a message with the exact size every time one is requested
+ * - maintain a pool of pre-allocated messages and return one when needed.
+ * Recycle previously used messages back into the pool
+ *
+ * TYPES OF ENDPOINT_MESSAGE_MANAGERS
+ * - allocate a new connection manager for each connection. Message pools
+ * become connection specific. This increases memory usage but improves
+ * concurrency.
+ * - allocate a single connection manager and share a pointer to it with all
+ * connections created by this endpoint. The message pool will be shared
+ * among all connections, improving memory usage and performance at the cost
+ * of reduced concurrency
+ */
+
+/// Custom deleter for use in shared_ptrs to message.
+/**
+ * This is used to catch messages about to be deleted and offer the manager the
+ * ability to recycle them instead. Message::recycle will return true if it was
+ * successfully recycled and false otherwise. In the case of exceptions or error
+ * this deleter frees the memory.
+ */
+template <typename T>
+void message_deleter(T* msg) {
+ try {
+ if (!msg->recycle()) {
+ delete msg;
+ }
+ } catch (...) {
+ // TODO: is there a better way to ensure this function doesn't throw?
+ delete msg;
+ }
+}
+
+/// Represents a buffer for a single WebSocket message.
+/**
+ *
+ *
+ */
+template <typename con_msg_manager>
+class message {
+public:
+ typedef lib::shared_ptr<message> ptr;
+
+ typedef typename con_msg_manager::weak_ptr con_msg_man_ptr;
+
+ message(con_msg_man_ptr manager, size_t size = 128)
+ : m_manager(manager)
+ , m_payload(size) {}
+
+ frame::opcode::value get_opcode() const {
+ return m_opcode;
+ }
+ const std::string& get_header() const {
+ return m_header;
+ }
+ const std::string& get_extension_data() const {
+ return m_extension_data;
+ }
+ const std::string& get_payload() const {
+ return m_payload;
+ }
+
+ /// Recycle the message
+ /**
+ * A request to recycle this message was received. Forward that request to
+ * the connection message manager for processing. Errors and exceptions
+ * from the manager's recycle member function should be passed back up the
+ * call chain. The caller to message::recycle will deal with them.
+ *
+ * Recycle must *only* be called by the message shared_ptr's destructor.
+ * Once recycled successfully, ownership of the memory has been passed to
+ * another system and must not be accessed again.
+ *
+ * @return true if the message was successfully recycled, false otherwise.
+ */
+ bool recycle() {
+ typename con_msg_manager::ptr shared = m_manager.lock();
+
+ if (shared) {
+ return shared->(recycle(this));
+ } else {
+ return false;
+ }
+ }
+private:
+ con_msg_man_ptr m_manager;
+
+ frame::opcode::value m_opcode;
+ std::string m_header;
+ std::string m_extension_data;
+ std::string m_payload;
+};
+
+namespace alloc {
+
+/// A connection message manager that allocates a new message for each
+/// request.
+template <typename message>
+class con_msg_manager {
+public:
+ typedef lib::shared_ptr<con_msg_manager> ptr;
+ typedef lib::weak_ptr<con_msg_manager> weak_ptr;
+
+ typedef typename message::ptr message_ptr;
+
+ /// Get a message buffer with specified size
+ /**
+ * @param size Minimum size in bytes to request for the message payload.
+ *
+ * @return A shared pointer to a new message with specified size.
+ */
+ message_ptr get_message(size_t size) const {
+ return lib::make_shared<message>(size);
+ }
+
+ /// Recycle a message
+ /**
+ * This method shouldn't be called. If it is, return false to indicate an
+ * error. The rest of the method recycle chain should notice this and free
+ * the memory.
+ *
+ * @param msg The message to be recycled.
+ *
+ * @return true if the message was successfully recycled, false otherwse.
+ */
+ bool recycle(message * msg) {
+ return false;
+ }
+};
+
+/// An endpoint message manager that allocates a new manager for each
+/// connection.
+template <typename con_msg_manager>
+class endpoint_msg_manager {
+public:
+ typedef typename con_msg_manager::ptr con_msg_man_ptr;
+
+ /// Get a pointer to a connection message manager
+ /**
+ * @return A pointer to the requested connection message manager.
+ */
+ con_msg_man_ptr get_manager() const {
+ return lib::make_shared<con_msg_manager>();
+ }
+};
+
+} // namespace alloc
+
+namespace pool {
+
+/// A connection messages manager that maintains a pool of messages that is
+/// used to fulfill get_message requests.
+class con_msg_manager {
+
+};
+
+/// An endpoint manager that maintains a shared pool of connection managers
+/// and returns an appropriate one for the requesting connection.
+class endpoint_msg_manager {
+
+};
+
+} // namespace pool
+
+} // namespace message_buffer
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_MESSAGE_BUFFER_ALLOC_HPP
diff --git a/websocketpp/processors/base.hpp b/websocketpp/processors/base.hpp
new file mode 100644
index 00000000..ddb8b81a
--- /dev/null
+++ b/websocketpp/processors/base.hpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP
+#define WEBSOCKETPP_PROCESSOR_BASE_HPP
+
+#include <websocketpp/close.hpp>
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace processor {
+
+/// Constants related to processing WebSocket connections
+namespace constants {
+
+static char const upgrade_token[] = "websocket";
+static char const connection_token[] = "upgrade";
+static char const handshake_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+} // namespace constants
+
+
+/// Processor class related error codes
+namespace error_cat {
+enum value {
+ BAD_REQUEST = 0, // Error was the result of improperly formatted user input
+ INTERNAL_ERROR = 1, // Error was a logic error internal to WebSocket++
+ PROTOCOL_VIOLATION = 2,
+ MESSAGE_TOO_BIG = 3,
+ PAYLOAD_VIOLATION = 4 // Error was due to receiving invalid payload data
+};
+} // namespace error_cat
+
+/// Error code category and codes used by all processor types
+namespace error {
+enum processor_errors {
+ /// Catch-all error for processor policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// Error was the result of improperly formatted user input
+ bad_request,
+
+ /// Processor encountered a protocol violation in an incoming message
+ protocol_violation,
+
+ /// Processor encountered a message that was too large
+ message_too_big,
+
+ /// Processor encountered invalid payload data.
+ invalid_payload,
+
+ /// The processor method was called with invalid arguments
+ invalid_arguments,
+
+ /// Opcode was invalid for requested operation
+ invalid_opcode,
+
+ /// Control frame too large
+ control_too_big,
+
+ /// Illegal use of reserved bit
+ invalid_rsv_bit,
+
+ /// Fragmented control message
+ fragmented_control,
+
+ /// Continuation without message
+ invalid_continuation,
+
+ /// Clients may not send unmasked frames
+ masking_required,
+
+ /// Servers may not send masked frames
+ masking_forbidden,
+
+ /// Payload length not minimally encoded
+ non_minimal_encoding,
+
+ /// Not supported on 32 bit systems
+ requires_64bit,
+
+ /// Invalid UTF-8 encoding
+ invalid_utf8,
+
+ /// Operation required not implemented functionality
+ not_implemented,
+
+ /// Invalid HTTP method
+ invalid_http_method,
+
+ /// Invalid HTTP version
+ invalid_http_version,
+
+ /// Invalid HTTP status
+ invalid_http_status,
+
+ /// Missing Required Header
+ missing_required_header,
+
+ /// Embedded SHA-1 library error
+ sha1_library,
+
+ /// No support for this feature in this protocol version.
+ no_protocol_support,
+
+ /// Reserved close code used
+ reserved_close_code,
+
+ /// Invalid close code used
+ invalid_close_code,
+
+ /// Using a reason requires a close code
+ reason_requires_code,
+
+ /// Error parsing subprotocols
+ subprotocol_parse_error,
+
+ /// Error parsing extensions
+ extension_parse_error,
+
+ /// Extension related operation was ignored because extensions are disabled
+ extensions_disabled,
+
+ /// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes
+ /// after the handshake. Less than 8 bytes were read.
+ short_key3
+};
+
+/// Category for processor errors
+class processor_category : public lib::error_category {
+public:
+ processor_category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.processor";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::general:
+ return "Generic processor error";
+ case error::bad_request:
+ return "invalid user input";
+ case error::protocol_violation:
+ return "Generic protocol violation";
+ case error::message_too_big:
+ return "A message was too large";
+ case error::invalid_payload:
+ return "A payload contained invalid data";
+ case error::invalid_arguments:
+ return "invalid function arguments";
+ case error::invalid_opcode:
+ return "invalid opcode";
+ case error::control_too_big:
+ return "Control messages are limited to fewer than 125 characters";
+ case error::invalid_rsv_bit:
+ return "Invalid use of reserved bits";
+ case error::fragmented_control:
+ return "Control messages cannot be fragmented";
+ case error::invalid_continuation:
+ return "Invalid message continuation";
+ case error::masking_required:
+ return "Clients may not send unmasked frames";
+ case error::masking_forbidden:
+ return "Servers may not send masked frames";
+ case error::non_minimal_encoding:
+ return "Payload length was not minimally encoded";
+ case error::requires_64bit:
+ return "64 bit frames are not supported on 32 bit systems";
+ case error::invalid_utf8:
+ return "Invalid UTF8 encoding";
+ case error::not_implemented:
+ return "Operation required not implemented functionality";
+ case error::invalid_http_method:
+ return "Invalid HTTP method.";
+ case error::invalid_http_version:
+ return "Invalid HTTP version.";
+ case error::invalid_http_status:
+ return "Invalid HTTP status.";
+ case error::missing_required_header:
+ return "A required HTTP header is missing";
+ case error::sha1_library:
+ return "SHA-1 library error";
+ case error::no_protocol_support:
+ return "The WebSocket protocol version in use does not support this feature";
+ case error::reserved_close_code:
+ return "Reserved close code used";
+ case error::invalid_close_code:
+ return "Invalid close code used";
+ case error::reason_requires_code:
+ return "Using a close reason requires a valid close code";
+ case error::subprotocol_parse_error:
+ return "Error parsing subprotocol header";
+ case error::extension_parse_error:
+ return "Error parsing extension header";
+ case error::extensions_disabled:
+ return "Extensions are disabled";
+ case error::short_key3:
+ return "Short Hybi00 Key 3 read";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the processor error category
+inline lib::error_category const & get_processor_category() {
+ static processor_category instance;
+ return instance;
+}
+
+/// Create an error code with the given value and the processor category
+inline lib::error_code make_error_code(error::processor_errors e) {
+ return lib::error_code(static_cast<int>(e), get_processor_category());
+}
+
+/// Converts a processor error_code into a websocket close code
+/**
+ * Looks up the appropriate WebSocket close code that should be sent after an
+ * error of this sort occurred.
+ *
+ * If the error is not in the processor category close::status::blank is
+ * returned.
+ *
+ * If the error isn't normally associated with reasons to close a connection
+ * (such as errors intended to be used internally or delivered to client
+ * applications, ex: invalid arguments) then
+ * close::status::internal_endpoint_error is returned.
+ */
+inline close::status::value to_ws(lib::error_code ec) {
+ if (ec.category() != get_processor_category()) {
+ return close::status::blank;
+ }
+
+ switch (ec.value()) {
+ case error::protocol_violation:
+ case error::control_too_big:
+ case error::invalid_opcode:
+ case error::invalid_rsv_bit:
+ case error::fragmented_control:
+ case error::invalid_continuation:
+ case error::masking_required:
+ case error::masking_forbidden:
+ case error::reserved_close_code:
+ case error::invalid_close_code:
+ return close::status::protocol_error;
+ case error::invalid_payload:
+ case error::invalid_utf8:
+ return close::status::invalid_payload;
+ case error::message_too_big:
+ return close::status::message_too_big;
+ default:
+ return close::status::internal_endpoint_error;
+ }
+}
+
+} // namespace error
+} // namespace processor
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::processor::error::processor_errors>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif //WEBSOCKETPP_PROCESSOR_BASE_HPP
diff --git a/websocketpp/processors/hybi00.hpp b/websocketpp/processors/hybi00.hpp
new file mode 100644
index 00000000..95ad9dfa
--- /dev/null
+++ b/websocketpp/processors/hybi00.hpp
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
+
+#include <websocketpp/frame.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/utf8_validator.hpp>
+#include <websocketpp/common/network.hpp>
+#include <websocketpp/common/md5.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <websocketpp/processors/processor.hpp>
+
+#include <algorithm>
+#include <cstdlib>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 00
+/**
+ * There are many differences between Hybi 00 and Hybi 13
+ */
+template <typename config>
+class hybi00 : public processor<config> {
+public:
+ typedef processor<config> base;
+
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+
+ typedef typename config::message_type message_type;
+ typedef typename message_type::ptr message_ptr;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+
+ explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager)
+ : processor<config>(secure, p_is_server)
+ , msg_hdr(0x00)
+ , msg_ftr(0xff)
+ , m_state(HEADER)
+ , m_msg_manager(manager) {}
+
+ int get_version() const {
+ return 0;
+ }
+
+ lib::error_code validate_handshake(request_type const & r) const {
+ if (r.get_method() != "GET") {
+ return make_error_code(error::invalid_http_method);
+ }
+
+ if (r.get_version() != "HTTP/1.1") {
+ return make_error_code(error::invalid_http_version);
+ }
+
+ // required headers
+ // Host is required by HTTP/1.1
+ // Connection is required by is_websocket_handshake
+ // Upgrade is required by is_websocket_handshake
+ if (r.get_header("Sec-WebSocket-Key1").empty() ||
+ r.get_header("Sec-WebSocket-Key2").empty() ||
+ r.get_header("Sec-WebSocket-Key3").empty())
+ {
+ return make_error_code(error::missing_required_header);
+ }
+
+ return lib::error_code();
+ }
+
+ lib::error_code process_handshake(request_type const & req,
+ std::string const & subprotocol, response_type & res) const
+ {
+ char key_final[16];
+
+ // copy key1 into final key
+ decode_client_key(req.get_header("Sec-WebSocket-Key1"), &key_final[0]);
+
+ // copy key2 into final key
+ decode_client_key(req.get_header("Sec-WebSocket-Key2"), &key_final[4]);
+
+ // copy key3 into final key
+ // key3 should be exactly 8 bytes. If it is more it will be truncated
+ // if it is less the final key will almost certainly be wrong.
+ // TODO: decide if it is best to silently fail here or produce some sort
+ // of warning or exception.
+ std::string const & key3 = req.get_header("Sec-WebSocket-Key3");
+ std::copy(key3.c_str(),
+ key3.c_str()+(std::min)(static_cast<size_t>(8), key3.size()),
+ &key_final[8]);
+
+ res.append_header(
+ "Sec-WebSocket-Key3",
+ md5::md5_hash_string(std::string(key_final,16))
+ );
+
+ res.append_header("Upgrade","WebSocket");
+ res.append_header("Connection","Upgrade");
+
+ // Echo back client's origin unless our local application set a
+ // more restrictive one.
+ if (res.get_header("Sec-WebSocket-Origin").empty()) {
+ res.append_header("Sec-WebSocket-Origin",req.get_header("Origin"));
+ }
+
+ // Echo back the client's request host unless our local application
+ // set a different one.
+ if (res.get_header("Sec-WebSocket-Location").empty()) {
+ uri_ptr uri = get_uri(req);
+ res.append_header("Sec-WebSocket-Location",uri->str());
+ }
+
+ if (!subprotocol.empty()) {
+ res.replace_header("Sec-WebSocket-Protocol",subprotocol);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 00 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * The Hybi 00 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ lib::error_code validate_server_handshake_response(request_type const &,
+ response_type &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ std::string get_raw(response_type const & res) const {
+ response_type temp = res;
+ temp.remove_header("Sec-WebSocket-Key3");
+ return temp.raw() + res.get_header("Sec-WebSocket-Key3");
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Origin");
+ }
+
+ /// Extracts requested subprotocols from a handshake request
+ /**
+ * hybi00 does support subprotocols
+ * https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9
+ *
+ * @param [in] req The request to extract from
+ * @param [out] subprotocol_list A reference to a vector of strings to store
+ * the results in.
+ */
+ lib::error_code extract_subprotocols(request_type const & req,
+ std::vector<std::string> & subprotocol_list)
+ {
+ if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
+ http::parameter_list p;
+
+ if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
+ http::parameter_list::const_iterator it;
+
+ for (it = p.begin(); it != p.end(); ++it) {
+ subprotocol_list.push_back(it->first);
+ }
+ } else {
+ return error::make_error_code(error::subprotocol_parse_error);
+ }
+ }
+ return lib::error_code();
+ }
+
+ uri_ptr get_uri(request_type const & request) const {
+ std::string h = request.get_header("Host");
+
+ size_t last_colon = h.rfind(":");
+ size_t last_sbrace = h.rfind("]");
+
+ // no : = hostname with no port
+ // last : before ] = ipv6 literal with no port
+ // : with no ] = hostname with port
+ // : after ] = ipv6 literal with port
+
+ if (last_colon == std::string::npos ||
+ (last_sbrace != std::string::npos && last_sbrace > last_colon))
+ {
+ return lib::make_shared<uri>(base::m_secure, h, request.get_uri());
+ } else {
+ return lib::make_shared<uri>(base::m_secure,
+ h.substr(0,last_colon),
+ h.substr(last_colon+1),
+ request.get_uri());
+ }
+
+ // TODO: check if get_uri is a full uri
+ }
+
+ /// Get hybi00 handshake key3
+ /**
+ * @todo This doesn't appear to be used anymore. It might be able to be
+ * removed
+ */
+ std::string get_key3() const {
+ return "";
+ }
+
+ /// Process new websocket connection bytes
+ size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
+ // if in state header we are expecting a 0x00 byte, if we don't get one
+ // it is a fatal error
+ size_t p = 0; // bytes processed
+ size_t l = 0;
+
+ ec = lib::error_code();
+
+ while (p < len) {
+ if (m_state == HEADER) {
+ if (buf[p] == msg_hdr) {
+ p++;
+ m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
+
+ if (!m_msg_ptr) {
+ ec = make_error_code(websocketpp::error::no_incoming_buffers);
+ m_state = FATAL_ERROR;
+ } else {
+ m_state = PAYLOAD;
+ }
+ } else {
+ ec = make_error_code(error::protocol_violation);
+ m_state = FATAL_ERROR;
+ }
+ } else if (m_state == PAYLOAD) {
+ uint8_t *it = std::find(buf+p,buf+len,msg_ftr);
+
+ // 0 1 2 3 4 5
+ // 0x00 0x23 0x23 0x23 0xff 0xXX
+
+ // Copy payload bytes into message
+ l = static_cast<size_t>(it-(buf+p));
+ m_msg_ptr->append_payload(buf+p,l);
+ p += l;
+
+ if (it != buf+len) {
+ // message is done, copy it and the trailing
+ p++;
+ // TODO: validation
+ m_state = READY;
+ }
+ } else {
+ // TODO
+ break;
+ }
+ }
+ // If we get one, we create a new message and move to application state
+
+ // if in state application we are copying bytes into the output message
+ // and validating them for UTF8 until we hit a 0xff byte. Once we hit
+ // 0x00, the message is complete and is dispatched. Then we go back to
+ // header state.
+
+ //ec = make_error_code(error::not_implemented);
+ return p;
+ }
+
+ bool ready() const {
+ return (m_state == READY);
+ }
+
+ bool get_error() const {
+ return false;
+ }
+
+ message_ptr get_message() {
+ message_ptr ret = m_msg_ptr;
+ m_msg_ptr = message_ptr();
+ m_state = HEADER;
+ return ret;
+ }
+
+ /// Prepare a message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
+ {
+ if (!in || !out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ // TODO: check if the message is prepared already
+
+ // validate opcode
+ if (in->get_opcode() != frame::opcode::text) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ std::string& i = in->get_raw_payload();
+ //std::string& o = out->get_raw_payload();
+
+ // validate payload utf8
+ if (!utf8_validator::validate(i)) {
+ return make_error_code(error::invalid_payload);
+ }
+
+ // generate header
+ out->set_header(std::string(reinterpret_cast<char const *>(&msg_hdr),1));
+
+ // process payload
+ out->set_payload(i);
+ out->append_payload(std::string(reinterpret_cast<char const *>(&msg_ftr),1));
+
+ // hybi00 doesn't support compression
+ // hybi00 doesn't have masking
+
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+
+ /// Prepare a ping frame
+ /**
+ * Hybi 00 doesn't support pings so this will always return an error
+ *
+ * @param in The string to use for the ping payload
+ * @param out The message buffer to prepare the ping in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_ping(std::string const &, message_ptr) const
+ {
+ return lib::error_code(error::no_protocol_support);
+ }
+
+ /// Prepare a pong frame
+ /**
+ * Hybi 00 doesn't support pongs so this will always return an error
+ *
+ * @param in The string to use for the pong payload
+ * @param out The message buffer to prepare the pong in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_pong(std::string const &, message_ptr) const
+ {
+ return lib::error_code(error::no_protocol_support);
+ }
+
+ /// Prepare a close frame
+ /**
+ * Hybi 00 doesn't support the close code or reason so these parameters are
+ * ignored.
+ *
+ * @param code The close code to send
+ * @param reason The reason string to send
+ * @param out The message buffer to prepare the fame in
+ * @return Status code, zero on success, non-zero on failure
+ */
+ lib::error_code prepare_close(close::status::value, std::string const &,
+ message_ptr out) const
+ {
+ if (!out) {
+ return lib::error_code(error::invalid_arguments);
+ }
+
+ std::string val;
+ val.append(1,'\xff');
+ val.append(1,'\x00');
+ out->set_payload(val);
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+private:
+ void decode_client_key(std::string const & key, char * result) const {
+ unsigned int spaces = 0;
+ std::string digits;
+ uint32_t num;
+
+ // key2
+ for (size_t i = 0; i < key.size(); i++) {
+ if (key[i] == ' ') {
+ spaces++;
+ } else if (key[i] >= '0' && key[i] <= '9') {
+ digits += key[i];
+ }
+ }
+
+ num = static_cast<uint32_t>(strtoul(digits.c_str(), NULL, 10));
+ if (spaces > 0 && num > 0) {
+ num = htonl(num/spaces);
+ std::copy(reinterpret_cast<char*>(&num),
+ reinterpret_cast<char*>(&num)+4,
+ result);
+ } else {
+ std::fill(result,result+4,0);
+ }
+ }
+
+ enum state {
+ HEADER = 0,
+ PAYLOAD = 1,
+ READY = 2,
+ FATAL_ERROR = 3
+ };
+
+ uint8_t const msg_hdr;
+ uint8_t const msg_ftr;
+
+ state m_state;
+
+ msg_manager_ptr m_msg_manager;
+ message_ptr m_msg_ptr;
+ utf8_validator::validator m_validator;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI00_HPP
diff --git a/websocketpp/processors/hybi07.hpp b/websocketpp/processors/hybi07.hpp
new file mode 100644
index 00000000..14b67c21
--- /dev/null
+++ b/websocketpp/processors/hybi07.hpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_HYBI07_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI07_HPP
+
+#include <websocketpp/processors/hybi08.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 07
+/**
+ * The primary difference between 07 and 08 is a version number.
+ */
+template <typename config>
+class hybi07 : public hybi08<config> {
+public:
+ typedef typename config::request_type request_type;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : hybi08<config>(secure, p_is_server, manager, rng) {}
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 07 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ int get_version() const {
+ return 7;
+ }
+private:
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI07_HPP
diff --git a/websocketpp/processors/hybi08.hpp b/websocketpp/processors/hybi08.hpp
new file mode 100644
index 00000000..15f6e658
--- /dev/null
+++ b/websocketpp/processors/hybi08.hpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_HYBI08_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI08_HPP
+
+#include <websocketpp/processors/hybi13.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi Draft version 08
+/**
+ * The primary difference between 08 and 13 is a different origin header name
+ */
+template <typename config>
+class hybi08 : public hybi13<config> {
+public:
+ typedef hybi08<config> type;
+ typedef typename config::request_type request_type;
+
+ typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : hybi13<config>(secure, p_is_server, manager, rng) {}
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * The Hybi 08 processor only implements incoming connections so this will
+ * always return an error.
+ *
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type &, uri_ptr,
+ std::vector<std::string> const &) const
+ {
+ return error::make_error_code(error::no_protocol_support);
+ }
+
+ int get_version() const {
+ return 8;
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Sec-WebSocket-Origin");
+ }
+private:
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI08_HPP
diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp
new file mode 100644
index 00000000..79486654
--- /dev/null
+++ b/websocketpp/processors/hybi13.hpp
@@ -0,0 +1,1056 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_HYBI13_HPP
+#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP
+
+#include <websocketpp/processors/processor.hpp>
+
+#include <websocketpp/frame.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/utf8_validator.hpp>
+#include <websocketpp/sha1/sha1.hpp>
+#include <websocketpp/base64/base64.hpp>
+
+#include <websocketpp/common/network.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <vector>
+#include <utility>
+
+namespace websocketpp {
+namespace processor {
+
+/// Processor for Hybi version 13 (RFC6455)
+template <typename config>
+class hybi13 : public processor<config> {
+public:
+ typedef processor<config> base;
+
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+
+ typedef typename config::message_type message_type;
+ typedef typename message_type::ptr message_ptr;
+
+ typedef typename config::con_msg_manager_type msg_manager_type;
+ typedef typename msg_manager_type::ptr msg_manager_ptr;
+ typedef typename config::rng_type rng_type;
+
+ typedef typename config::permessage_deflate_type permessage_deflate_type;
+
+ typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+ explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
+ : processor<config>(secure, p_is_server)
+ , m_msg_manager(manager)
+ , m_rng(rng)
+ {
+ reset_headers();
+ }
+
+ int get_version() const {
+ return 13;
+ }
+
+ bool has_permessage_deflate() const {
+ return m_permessage_deflate.is_implemented();
+ }
+
+ err_str_pair negotiate_extensions(request_type const & request) {
+ return negotiate_extensions_helper(request);
+ }
+
+ err_str_pair negotiate_extensions(response_type const & response) {
+ return negotiate_extensions_helper(response);
+ }
+
+ /// Extension negotiation helper function
+ /**
+ * This exists mostly because the code for requests and responses is
+ * identical and I can't have virtual template methods.
+ */
+ template <typename header_type>
+ err_str_pair negotiate_extensions_helper(header_type const & header) {
+ err_str_pair ret;
+
+ // Respect blanket disabling of all extensions and don't even parse
+ // the extension header
+ if (!config::enable_extensions) {
+ ret.first = make_error_code(error::extensions_disabled);
+ return ret;
+ }
+
+ http::parameter_list p;
+
+ bool error = header.get_header_as_plist("Sec-WebSocket-Extensions",p);
+
+ if (error) {
+ ret.first = make_error_code(error::extension_parse_error);
+ return ret;
+ }
+
+ // If there are no extensions parsed then we are done!
+ if (p.size() == 0) {
+ return ret;
+ }
+
+ http::parameter_list::const_iterator it;
+
+ if (m_permessage_deflate.is_implemented()) {
+ err_str_pair neg_ret;
+ for (it = p.begin(); it != p.end(); ++it) {
+ // look through each extension, if the key is permessage-deflate
+ if (it->first == "permessage-deflate") {
+ // if we have already successfully negotiated this extension
+ // then skip any other requests to negotiate the same one
+ // with different parameters
+ if (m_permessage_deflate.is_enabled()) {
+ continue;
+ }
+
+
+ neg_ret = m_permessage_deflate.negotiate(it->second);
+
+ if (neg_ret.first) {
+ // Figure out if this is an error that should halt all
+ // extension negotiations or simply cause negotiation of
+ // this specific extension to fail.
+ //std::cout << "permessage-compress negotiation failed: "
+ // << neg_ret.first.message() << std::endl;
+ } else {
+ // Note: this list will need commas if WebSocket++ ever
+ // supports more than one extension
+ ret.second += neg_ret.second;
+ m_permessage_deflate.init(base::m_server);
+ continue;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ lib::error_code validate_handshake(request_type const & r) const {
+ if (r.get_method() != "GET") {
+ return make_error_code(error::invalid_http_method);
+ }
+
+ if (r.get_version() != "HTTP/1.1") {
+ return make_error_code(error::invalid_http_version);
+ }
+
+ // required headers
+ // Host is required by HTTP/1.1
+ // Connection is required by is_websocket_handshake
+ // Upgrade is required by is_websocket_handshake
+ if (r.get_header("Sec-WebSocket-Key").empty()) {
+ return make_error_code(error::missing_required_header);
+ }
+
+ return lib::error_code();
+ }
+
+ /* TODO: the 'subprotocol' parameter may need to be expanded into a more
+ * generic struct if other user input parameters to the processed handshake
+ * are found.
+ */
+ lib::error_code process_handshake(request_type const & request,
+ std::string const & subprotocol, response_type & response) const
+ {
+ std::string server_key = request.get_header("Sec-WebSocket-Key");
+
+ lib::error_code ec = process_handshake_key(server_key);
+
+ if (ec) {
+ return ec;
+ }
+
+ response.replace_header("Sec-WebSocket-Accept",server_key);
+ response.append_header("Upgrade",constants::upgrade_token);
+ response.append_header("Connection",constants::connection_token);
+
+ if (!subprotocol.empty()) {
+ response.replace_header("Sec-WebSocket-Protocol",subprotocol);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Fill in a set of request headers for a client connection request
+ /**
+ * @param [out] req Set of headers to fill in
+ * @param [in] uri The uri being connected to
+ * @param [in] subprotocols The list of subprotocols to request
+ */
+ lib::error_code client_handshake_request(request_type & req, uri_ptr
+ uri, std::vector<std::string> const & subprotocols) const
+ {
+ req.set_method("GET");
+ req.set_uri(uri->get_resource());
+ req.set_version("HTTP/1.1");
+
+ req.append_header("Upgrade","websocket");
+ req.append_header("Connection","Upgrade");
+ req.replace_header("Sec-WebSocket-Version","13");
+ req.replace_header("Host",uri->get_host_port());
+
+ if (!subprotocols.empty()) {
+ std::ostringstream result;
+ std::vector<std::string>::const_iterator it = subprotocols.begin();
+ result << *it++;
+ while (it != subprotocols.end()) {
+ result << ", " << *it++;
+ }
+
+ req.replace_header("Sec-WebSocket-Protocol",result.str());
+ }
+
+ // Generate handshake key
+ frame::uint32_converter conv;
+ unsigned char raw_key[16];
+
+ for (int i = 0; i < 4; i++) {
+ conv.i = m_rng();
+ std::copy(conv.c,conv.c+4,&raw_key[i*4]);
+ }
+
+ req.replace_header("Sec-WebSocket-Key",base64_encode(raw_key, 16));
+
+ if (m_permessage_deflate.is_implemented()) {
+ std::string offer = m_permessage_deflate.generate_offer();
+ if (!offer.empty()) {
+ req.replace_header("Sec-WebSocket-Extensions",offer);
+ }
+ }
+
+ return lib::error_code();
+ }
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ lib::error_code validate_server_handshake_response(request_type const & req,
+ response_type& res) const
+ {
+ // A valid response has an HTTP 101 switching protocols code
+ if (res.get_status_code() != http::status_code::switching_protocols) {
+ return error::make_error_code(error::invalid_http_status);
+ }
+
+ // And the upgrade token in an upgrade header
+ std::string const & upgrade_header = res.get_header("Upgrade");
+ if (utility::ci_find_substr(upgrade_header, constants::upgrade_token,
+ sizeof(constants::upgrade_token)-1) == upgrade_header.end())
+ {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // And the websocket token in the connection header
+ std::string const & con_header = res.get_header("Connection");
+ if (utility::ci_find_substr(con_header, constants::connection_token,
+ sizeof(constants::connection_token)-1) == con_header.end())
+ {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // And has a valid Sec-WebSocket-Accept value
+ std::string key = req.get_header("Sec-WebSocket-Key");
+ lib::error_code ec = process_handshake_key(key);
+
+ if (ec || key != res.get_header("Sec-WebSocket-Accept")) {
+ return error::make_error_code(error::missing_required_header);
+ }
+
+ // check extensions
+
+ return lib::error_code();
+ }
+
+ std::string get_raw(response_type const & res) const {
+ return res.raw();
+ }
+
+ std::string const & get_origin(request_type const & r) const {
+ return r.get_header("Origin");
+ }
+
+ lib::error_code extract_subprotocols(request_type const & req,
+ std::vector<std::string> & subprotocol_list)
+ {
+ if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
+ http::parameter_list p;
+
+ if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
+ http::parameter_list::const_iterator it;
+
+ for (it = p.begin(); it != p.end(); ++it) {
+ subprotocol_list.push_back(it->first);
+ }
+ } else {
+ return error::make_error_code(error::subprotocol_parse_error);
+ }
+ }
+ return lib::error_code();
+ }
+
+ uri_ptr get_uri(request_type const & request) const {
+ return get_uri_from_host(request,(base::m_secure ? "wss" : "ws"));
+ }
+
+ /// Process new websocket connection bytes
+ /**
+ *
+ * Hybi 13 data streams represent a series of variable length frames. Each
+ * frame is made up of a series of fixed length fields. The lengths of later
+ * fields are contained in earlier fields. The first field length is fixed
+ * by the spec.
+ *
+ * This processor represents a state machine that keeps track of what field
+ * is presently being read and how many more bytes are needed to complete it
+ *
+ *
+ *
+ *
+ * Read two header bytes
+ * Extract full frame length.
+ * Read extra header bytes
+ * Validate frame header (including extension validate)
+ * Read extension data into extension message state object
+ * Read payload data into payload
+ *
+ * @param buf Input buffer
+ *
+ * @param len Length of input buffer
+ *
+ * @return Number of bytes processed or zero on error
+ */
+ size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
+ size_t p = 0;
+
+ ec = lib::error_code();
+
+ //std::cout << "consume: " << utility::to_hex(buf,len) << std::endl;
+
+ // Loop while we don't have a message ready and we still have bytes
+ // left to process.
+ while (m_state != READY && m_state != FATAL_ERROR &&
+ (p < len || m_bytes_needed == 0))
+ {
+ if (m_state == HEADER_BASIC) {
+ p += this->copy_basic_header_bytes(buf+p,len-p);
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ ec = this->validate_incoming_basic_header(
+ m_basic_header, base::m_server, !m_data_msg.msg_ptr
+ );
+ if (ec) {break;}
+
+ // extract full header size and adjust consume state accordingly
+ m_state = HEADER_EXTENDED;
+ m_cursor = 0;
+ m_bytes_needed = frame::get_header_len(m_basic_header) -
+ frame::BASIC_HEADER_LENGTH;
+ } else if (m_state == HEADER_EXTENDED) {
+ p += this->copy_extended_header_bytes(buf+p,len-p);
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ ec = validate_incoming_extended_header(m_basic_header,m_extended_header);
+ if (ec){break;}
+
+ m_state = APPLICATION;
+ m_bytes_needed = static_cast<size_t>(get_payload_size(m_basic_header,m_extended_header));
+
+ // check if this frame is the start of a new message and set up
+ // the appropriate message metadata.
+ frame::opcode::value op = frame::get_opcode(m_basic_header);
+
+ // TODO: get_message failure conditions
+
+ if (frame::opcode::is_control(op)) {
+ m_control_msg = msg_metadata(
+ m_msg_manager->get_message(op,m_bytes_needed),
+ frame::get_masking_key(m_basic_header,m_extended_header)
+ );
+
+ m_current_msg = &m_control_msg;
+ } else {
+ if (!m_data_msg.msg_ptr) {
+ if (m_bytes_needed > base::m_max_message_size) {
+ ec = make_error_code(error::message_too_big);
+ break;
+ }
+
+ m_data_msg = msg_metadata(
+ m_msg_manager->get_message(op,m_bytes_needed),
+ frame::get_masking_key(m_basic_header,m_extended_header)
+ );
+
+ if (m_permessage_deflate.is_enabled()) {
+ m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header));
+ }
+ } else {
+ // Fetch the underlying payload buffer from the data message we
+ // are writing into.
+ std::string & out = m_data_msg.msg_ptr->get_raw_payload();
+
+ if (out.size() + m_bytes_needed > base::m_max_message_size) {
+ ec = make_error_code(error::message_too_big);
+ break;
+ }
+
+ // Each frame starts a new masking key. All other state
+ // remains between frames.
+ m_data_msg.prepared_key = prepare_masking_key(
+ frame::get_masking_key(
+ m_basic_header,
+ m_extended_header
+ )
+ );
+
+ out.reserve(out.size() + m_bytes_needed);
+ }
+ m_current_msg = &m_data_msg;
+ }
+ } else if (m_state == EXTENSION) {
+ m_state = APPLICATION;
+ } else if (m_state == APPLICATION) {
+ size_t bytes_to_process = (std::min)(m_bytes_needed,len-p);
+
+ if (bytes_to_process > 0) {
+ p += this->process_payload_bytes(buf+p,bytes_to_process,ec);
+
+ if (ec) {break;}
+ }
+
+ if (m_bytes_needed > 0) {
+ continue;
+ }
+
+ // If this was the last frame in the message set the ready flag.
+ // Otherwise, reset processor state to read additional frames.
+ if (frame::get_fin(m_basic_header)) {
+ ec = finalize_message();
+ if (ec) {
+ break;
+ }
+ } else {
+ this->reset_headers();
+ }
+ } else {
+ // shouldn't be here
+ ec = make_error_code(error::general);
+ return 0;
+ }
+ }
+
+ return p;
+ }
+
+ /// Perform any finalization actions on an incoming message
+ /**
+ * Called after the full message is received. Provides the opportunity for
+ * extensions to complete any data post processing as well as final UTF8
+ * validation checks for text messages.
+ *
+ * @return A code indicating errors, if any
+ */
+ lib::error_code finalize_message() {
+ std::string & out = m_current_msg->msg_ptr->get_raw_payload();
+
+ // if the frame is compressed, append the compression
+ // trailer and flush the compression buffer.
+ if (m_permessage_deflate.is_enabled()
+ && m_current_msg->msg_ptr->get_compressed())
+ {
+ uint8_t trailer[4] = {0x00, 0x00, 0xff, 0xff};
+
+ // Decompress current buffer into the message buffer
+ lib::error_code ec;
+ ec = m_permessage_deflate.decompress(trailer,4,out);
+ if (ec) {
+ return ec;
+ }
+ }
+
+ // ensure that text messages end on a valid UTF8 code point
+ if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) {
+ if (!m_current_msg->validator.complete()) {
+ return make_error_code(error::invalid_utf8);
+ }
+ }
+
+ m_state = READY;
+
+ return lib::error_code();
+ }
+
+ void reset_headers() {
+ m_state = HEADER_BASIC;
+ m_bytes_needed = frame::BASIC_HEADER_LENGTH;
+
+ m_basic_header.b0 = 0x00;
+ m_basic_header.b1 = 0x00;
+
+ std::fill_n(
+ m_extended_header.bytes,
+ frame::MAX_EXTENDED_HEADER_LENGTH,
+ 0x00
+ );
+ }
+
+ /// Test whether or not the processor has a message ready
+ bool ready() const {
+ return (m_state == READY);
+ }
+
+ message_ptr get_message() {
+ if (!ready()) {
+ return message_ptr();
+ }
+ message_ptr ret = m_current_msg->msg_ptr;
+ m_current_msg->msg_ptr.reset();
+
+ if (frame::opcode::is_control(ret->get_opcode())) {
+ m_control_msg.msg_ptr.reset();
+ } else {
+ m_data_msg.msg_ptr.reset();
+ }
+
+ this->reset_headers();
+
+ return ret;
+ }
+
+ /// Test whether or not the processor is in a fatal error state.
+ bool get_error() const {
+ return m_state == FATAL_ERROR;
+ }
+
+ size_t get_bytes_needed() const {
+ return m_bytes_needed;
+ }
+
+ /// Prepare a user data message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ *
+ * TODO: tests
+ *
+ * @param in An unprepared message to prepare
+ * @param out A message to be overwritten with the prepared message
+ * @return error code
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
+ {
+ if (!in || !out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ frame::opcode::value op = in->get_opcode();
+
+ // validate opcode: only regular data frames
+ if (frame::opcode::is_control(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ std::string& i = in->get_raw_payload();
+ std::string& o = out->get_raw_payload();
+
+ // validate payload utf8
+ if (op == frame::opcode::TEXT && !utf8_validator::validate(i)) {
+ return make_error_code(error::invalid_payload);
+ }
+
+ frame::masking_key_type key;
+ bool masked = !base::m_server;
+ bool compressed = m_permessage_deflate.is_enabled()
+ && in->get_compressed();
+ bool fin = in->get_fin();
+
+ if (masked) {
+ // Generate masking key.
+ key.i = m_rng();
+ } else {
+ key.i = 0;
+ }
+
+ // prepare payload
+ if (compressed) {
+ // compress and store in o after header.
+ m_permessage_deflate.compress(i,o);
+
+ if (o.size() < 4) {
+ return make_error_code(error::general);
+ }
+
+ // Strip trailing 4 0x00 0x00 0xff 0xff bytes before writing to the
+ // wire
+ o.resize(o.size()-4);
+
+ // mask in place if necessary
+ if (masked) {
+ this->masked_copy(o,o,key);
+ }
+ } else {
+ // no compression, just copy data into the output buffer
+ o.resize(i.size());
+
+ // if we are masked, have the masking function write to the output
+ // buffer directly to avoid another copy. If not masked, copy
+ // directly without masking.
+ if (masked) {
+ this->masked_copy(i,o,key);
+ } else {
+ std::copy(i.begin(),i.end(),o.begin());
+ }
+ }
+
+ // generate header
+ frame::basic_header h(op,o.size(),fin,masked,compressed);
+
+ if (masked) {
+ frame::extended_header e(o.size(),key.i);
+ out->set_header(frame::prepare_header(h,e));
+ } else {
+ frame::extended_header e(o.size());
+ out->set_header(frame::prepare_header(h,e));
+ }
+
+ out->set_prepared(true);
+ out->set_opcode(op);
+
+ return lib::error_code();
+ }
+
+ /// Get URI
+ lib::error_code prepare_ping(std::string const & in, message_ptr out) const {
+ return this->prepare_control(frame::opcode::PING,in,out);
+ }
+
+ lib::error_code prepare_pong(std::string const & in, message_ptr out) const {
+ return this->prepare_control(frame::opcode::PONG,in,out);
+ }
+
+ virtual lib::error_code prepare_close(close::status::value code,
+ std::string const & reason, message_ptr out) const
+ {
+ if (close::status::reserved(code)) {
+ return make_error_code(error::reserved_close_code);
+ }
+
+ if (close::status::invalid(code) && code != close::status::no_status) {
+ return make_error_code(error::invalid_close_code);
+ }
+
+ if (code == close::status::no_status && reason.size() > 0) {
+ return make_error_code(error::reason_requires_code);
+ }
+
+ if (reason.size() > frame:: limits::payload_size_basic-2) {
+ return make_error_code(error::control_too_big);
+ }
+
+ std::string payload;
+
+ if (code != close::status::no_status) {
+ close::code_converter val;
+ val.i = htons(code);
+
+ payload.resize(reason.size()+2);
+
+ payload[0] = val.c[0];
+ payload[1] = val.c[1];
+
+ std::copy(reason.begin(),reason.end(),payload.begin()+2);
+ }
+
+ return this->prepare_control(frame::opcode::CLOSE,payload,out);
+ }
+protected:
+ /// Convert a client handshake key into a server response key in place
+ lib::error_code process_handshake_key(std::string & key) const {
+ key.append(constants::handshake_guid);
+
+ unsigned char message_digest[20];
+ sha1::calc(key.c_str(),key.length(),message_digest);
+ key = base64_encode(message_digest,20);
+
+ return lib::error_code();
+ }
+
+ /// Reads bytes from buf into m_basic_header
+ size_t copy_basic_header_bytes(uint8_t const * buf, size_t len) {
+ if (len == 0 || m_bytes_needed == 0) {
+ return 0;
+ }
+
+ if (len > 1) {
+ // have at least two bytes
+ if (m_bytes_needed == 2) {
+ m_basic_header.b0 = buf[0];
+ m_basic_header.b1 = buf[1];
+ m_bytes_needed -= 2;
+ return 2;
+ } else {
+ m_basic_header.b1 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ }
+ } else {
+ // have exactly one byte
+ if (m_bytes_needed == 2) {
+ m_basic_header.b0 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ } else {
+ m_basic_header.b1 = buf[0];
+ m_bytes_needed--;
+ return 1;
+ }
+ }
+ }
+
+ /// Reads bytes from buf into m_extended_header
+ size_t copy_extended_header_bytes(uint8_t const * buf, size_t len) {
+ size_t bytes_to_read = (std::min)(m_bytes_needed,len);
+
+ std::copy(buf,buf+bytes_to_read,m_extended_header.bytes+m_cursor);
+ m_cursor += bytes_to_read;
+ m_bytes_needed -= bytes_to_read;
+
+ return bytes_to_read;
+ }
+
+ /// Reads bytes from buf into message payload
+ /**
+ * This function performs unmasking and uncompression, validates the
+ * decoded bytes, and writes them to the appropriate message buffer.
+ *
+ * This member function will use the input buffer as stratch space for its
+ * work. The raw input bytes will not be preserved. This applies only to the
+ * bytes actually needed. At most min(m_bytes_needed,len) will be processed.
+ *
+ * @param buf Input/working buffer
+ * @param len Length of buf
+ * @return Number of bytes processed or zero in case of an error
+ */
+ size_t process_payload_bytes(uint8_t * buf, size_t len, lib::error_code& ec)
+ {
+ // unmask if masked
+ if (frame::get_masked(m_basic_header)) {
+ m_current_msg->prepared_key = frame::byte_mask_circ(
+ buf, len, m_current_msg->prepared_key);
+ // TODO: SIMD masking
+ }
+
+ std::string & out = m_current_msg->msg_ptr->get_raw_payload();
+ size_t offset = out.size();
+
+ // decompress message if needed.
+ if (m_permessage_deflate.is_enabled()
+ && m_current_msg->msg_ptr->get_compressed())
+ {
+ // Decompress current buffer into the message buffer
+ ec = m_permessage_deflate.decompress(buf,len,out);
+ if (ec) {
+ return 0;
+ }
+ } else {
+ // No compression, straight copy
+ out.append(reinterpret_cast<char *>(buf),len);
+ }
+
+ // validate unmasked, decompressed values
+ if (m_current_msg->msg_ptr->get_opcode() == frame::opcode::TEXT) {
+ if (!m_current_msg->validator.decode(out.begin()+offset,out.end())) {
+ ec = make_error_code(error::invalid_utf8);
+ return 0;
+ }
+ }
+
+ m_bytes_needed -= len;
+
+ return len;
+ }
+
+ /// Validate an incoming basic header
+ /**
+ * Validates an incoming hybi13 basic header.
+ *
+ * @param h The basic header to validate
+ * @param is_server Whether or not the endpoint that received this frame
+ * is a server.
+ * @param new_msg Whether or not this is the first frame of the message
+ * @return 0 on success or a non-zero error code on failure
+ */
+ lib::error_code validate_incoming_basic_header(frame::basic_header const & h,
+ bool is_server, bool new_msg) const
+ {
+ frame::opcode::value op = frame::get_opcode(h);
+
+ // Check control frame size limit
+ if (frame::opcode::is_control(op) &&
+ frame::get_basic_size(h) > frame::limits::payload_size_basic)
+ {
+ return make_error_code(error::control_too_big);
+ }
+
+ // Check that RSV bits are clear
+ // The only RSV bits allowed are rsv1 if the permessage_compress
+ // extension is enabled for this connection and the message is not
+ // a control message.
+ //
+ // TODO: unit tests for this
+ if (frame::get_rsv1(h) && (!m_permessage_deflate.is_enabled()
+ || frame::opcode::is_control(op)))
+ {
+ return make_error_code(error::invalid_rsv_bit);
+ }
+
+ if (frame::get_rsv2(h) || frame::get_rsv3(h)) {
+ return make_error_code(error::invalid_rsv_bit);
+ }
+
+ // Check for reserved opcodes
+ if (frame::opcode::reserved(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ // Check for invalid opcodes
+ // TODO: unit tests for this?
+ if (frame::opcode::invalid(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ // Check for fragmented control message
+ if (frame::opcode::is_control(op) && !frame::get_fin(h)) {
+ return make_error_code(error::fragmented_control);
+ }
+
+ // Check for continuation without an active message
+ if (new_msg && op == frame::opcode::CONTINUATION) {
+ return make_error_code(error::invalid_continuation);
+ }
+
+ // Check for new data frame when expecting continuation
+ if (!new_msg && !frame::opcode::is_control(op) &&
+ op != frame::opcode::CONTINUATION)
+ {
+ return make_error_code(error::invalid_continuation);
+ }
+
+ // Servers should reject any unmasked frames from clients.
+ // Clients should reject any masked frames from servers.
+ if (is_server && !frame::get_masked(h)) {
+ return make_error_code(error::masking_required);
+ } else if (!is_server && frame::get_masked(h)) {
+ return make_error_code(error::masking_forbidden);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Validate an incoming extended header
+ /**
+ * Validates an incoming hybi13 full header.
+ *
+ * @todo unit test for the >32 bit frames on 32 bit systems case
+ *
+ * @param h The basic header to validate
+ * @param e The extended header to validate
+ * @return An error_code, non-zero values indicate why the validation
+ * failed
+ */
+ lib::error_code validate_incoming_extended_header(frame::basic_header h,
+ frame::extended_header e) const
+ {
+ uint8_t basic_size = frame::get_basic_size(h);
+ uint64_t payload_size = frame::get_payload_size(h,e);
+
+ // Check for non-minimally encoded payloads
+ if (basic_size == frame::payload_size_code_16bit &&
+ payload_size <= frame::limits::payload_size_basic)
+ {
+ return make_error_code(error::non_minimal_encoding);
+ }
+
+ if (basic_size == frame::payload_size_code_64bit &&
+ payload_size <= frame::limits::payload_size_extended)
+ {
+ return make_error_code(error::non_minimal_encoding);
+ }
+
+ // Check for >32bit frames on 32 bit systems
+ if (sizeof(size_t) == 4 && (payload_size >> 32)) {
+ return make_error_code(error::requires_64bit);
+ }
+
+ return lib::error_code();
+ }
+
+ /// Copy and mask/unmask in one operation
+ /**
+ * Reads input from one string and writes unmasked output to another.
+ *
+ * @param [in] i The input string.
+ * @param [out] o The output string.
+ * @param [in] key The masking key to use for masking/unmasking
+ */
+ void masked_copy (std::string const & i, std::string & o,
+ frame::masking_key_type key) const
+ {
+ frame::byte_mask(i.begin(),i.end(),o.begin(),key);
+ // TODO: SIMD masking
+ }
+
+ /// Generic prepare control frame with opcode and payload.
+ /**
+ * Internal control frame building method. Handles validation, masking, etc
+ *
+ * @param op The control opcode to use
+ * @param payload The payload to use
+ * @param out The message buffer to store the prepared frame in
+ * @return Status code, zero on success, non-zero on error
+ */
+ lib::error_code prepare_control(frame::opcode::value op,
+ std::string const & payload, message_ptr out) const
+ {
+ if (!out) {
+ return make_error_code(error::invalid_arguments);
+ }
+
+ if (!frame::opcode::is_control(op)) {
+ return make_error_code(error::invalid_opcode);
+ }
+
+ if (payload.size() > frame::limits::payload_size_basic) {
+ return make_error_code(error::control_too_big);
+ }
+
+ frame::masking_key_type key;
+ bool masked = !base::m_server;
+
+ frame::basic_header h(op,payload.size(),true,masked);
+
+ std::string & o = out->get_raw_payload();
+ o.resize(payload.size());
+
+ if (masked) {
+ // Generate masking key.
+ key.i = m_rng();
+
+ frame::extended_header e(payload.size(),key.i);
+ out->set_header(frame::prepare_header(h,e));
+ this->masked_copy(payload,o,key);
+ } else {
+ frame::extended_header e(payload.size());
+ out->set_header(frame::prepare_header(h,e));
+ std::copy(payload.begin(),payload.end(),o.begin());
+ }
+
+ out->set_opcode(op);
+ out->set_prepared(true);
+
+ return lib::error_code();
+ }
+
+ enum state {
+ HEADER_BASIC = 0,
+ HEADER_EXTENDED = 1,
+ EXTENSION = 2,
+ APPLICATION = 3,
+ READY = 4,
+ FATAL_ERROR = 5
+ };
+
+ /// This data structure holds data related to processing a message, such as
+ /// the buffer it is being written to, its masking key, its UTF8 validation
+ /// state, and sometimes its compression state.
+ struct msg_metadata {
+ msg_metadata() {}
+ msg_metadata(message_ptr m, size_t p) : msg_ptr(m),prepared_key(p) {}
+ msg_metadata(message_ptr m, frame::masking_key_type p)
+ : msg_ptr(m)
+ , prepared_key(prepare_masking_key(p)) {}
+
+ message_ptr msg_ptr; // pointer to the message data buffer
+ size_t prepared_key; // prepared masking key
+ utf8_validator::validator validator; // utf8 validation state
+ };
+
+ // Basic header of the frame being read
+ frame::basic_header m_basic_header;
+
+ // Pointer to a manager that can create message buffers for us.
+ msg_manager_ptr m_msg_manager;
+
+ // Number of bytes needed to complete the current operation
+ size_t m_bytes_needed;
+
+ // Number of extended header bytes read
+ size_t m_cursor;
+
+ // Metadata for the current data msg
+ msg_metadata m_data_msg;
+ // Metadata for the current control msg
+ msg_metadata m_control_msg;
+
+ // Pointer to the metadata associated with the frame being read
+ msg_metadata * m_current_msg;
+
+ // Extended header of current frame
+ frame::extended_header m_extended_header;
+
+ rng_type & m_rng;
+
+ // Overall state of the processor
+ state m_state;
+
+ // Extensions
+ permessage_deflate_type m_permessage_deflate;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HYBI13_HPP
diff --git a/websocketpp/processors/processor.hpp b/websocketpp/processors/processor.hpp
new file mode 100644
index 00000000..5131cc45
--- /dev/null
+++ b/websocketpp/processors/processor.hpp
@@ -0,0 +1,407 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_PROCESSOR_HPP
+#define WEBSOCKETPP_PROCESSOR_HPP
+
+#include <websocketpp/processors/base.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <websocketpp/close.hpp>
+#include <websocketpp/utilities.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace websocketpp {
+/// Processors encapsulate the protocol rules specific to each WebSocket version
+/**
+ * The processors namespace includes a number of free functions that operate on
+ * various WebSocket related data structures and perform processing that is not
+ * related to specific versions of the protocol.
+ *
+ * It also includes the abstract interface for the protocol specific processing
+ * engines. These engines wrap all of the logic necessary for parsing and
+ * validating WebSocket handshakes and messages of specific protocol version
+ * and set of allowed extensions.
+ *
+ * An instance of a processor represents the state of a single WebSocket
+ * connection of the associated version. One processor instance is needed per
+ * logical WebSocket connection.
+ */
+namespace processor {
+
+/// Determine whether or not a generic HTTP request is a WebSocket handshake
+/**
+ * @param r The HTTP request to read.
+ *
+ * @return True if the request is a WebSocket handshake, false otherwise
+ */
+template <typename request_type>
+bool is_websocket_handshake(request_type& r) {
+ using utility::ci_find_substr;
+
+ std::string const & upgrade_header = r.get_header("Upgrade");
+
+ if (ci_find_substr(upgrade_header, constants::upgrade_token,
+ sizeof(constants::upgrade_token)-1) == upgrade_header.end())
+ {
+ return false;
+ }
+
+ std::string const & con_header = r.get_header("Connection");
+
+ if (ci_find_substr(con_header, constants::connection_token,
+ sizeof(constants::connection_token)-1) == con_header.end())
+ {
+ return false;
+ }
+
+ return true;
+}
+
+/// Extract the version from a WebSocket handshake request
+/**
+ * A blank version header indicates a spec before versions were introduced.
+ * The only such versions in shipping products are Hixie Draft 75 and Hixie
+ * Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also
+ * known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As
+ * differentiating between these two sets of browsers is very difficult and
+ * Safari 5.0.1+ accounts for the vast majority of cases in the wild this
+ * function assumes that all handshakes without a valid version header are
+ * Hybi 00.
+ *
+ * @param r The WebSocket handshake request to read.
+ *
+ * @return The WebSocket handshake version or -1 if there was an extraction
+ * error.
+ */
+template <typename request_type>
+int get_websocket_version(request_type& r) {
+ if (!r.ready()) {
+ return -2;
+ }
+
+ if (r.get_header("Sec-WebSocket-Version").empty()) {
+ return 0;
+ }
+
+ int version;
+ std::istringstream ss(r.get_header("Sec-WebSocket-Version"));
+
+ if ((ss >> version).fail()) {
+ return -1;
+ }
+
+ return version;
+}
+
+/// Extract a URI ptr from the host header of the request
+/**
+ * @param request The request to extract the Host header from.
+ *
+ * @param scheme The scheme under which this request was received (ws, wss,
+ * http, https, etc)
+ *
+ * @return A uri_pointer that encodes the value of the host header.
+ */
+template <typename request_type>
+uri_ptr get_uri_from_host(request_type & request, std::string scheme) {
+ std::string h = request.get_header("Host");
+
+ size_t last_colon = h.rfind(":");
+ size_t last_sbrace = h.rfind("]");
+
+ // no : = hostname with no port
+ // last : before ] = ipv6 literal with no port
+ // : with no ] = hostname with port
+ // : after ] = ipv6 literal with port
+ if (last_colon == std::string::npos ||
+ (last_sbrace != std::string::npos && last_sbrace > last_colon))
+ {
+ return lib::make_shared<uri>(scheme, h, request.get_uri());
+ } else {
+ return lib::make_shared<uri>(scheme,
+ h.substr(0,last_colon),
+ h.substr(last_colon+1),
+ request.get_uri());
+ }
+}
+
+/// WebSocket protocol processor abstract base class
+template <typename config>
+class processor {
+public:
+ typedef processor<config> type;
+ typedef typename config::request_type request_type;
+ typedef typename config::response_type response_type;
+ typedef typename config::message_type::ptr message_ptr;
+ typedef std::pair<lib::error_code,std::string> err_str_pair;
+
+ explicit processor(bool secure, bool p_is_server)
+ : m_secure(secure)
+ , m_server(p_is_server)
+ , m_max_message_size(config::max_message_size)
+ {}
+
+ virtual ~processor() {}
+
+ /// Get the protocol version of this processor
+ virtual int get_version() const = 0;
+
+ /// Get maximum message size
+ /**
+ * Get maximum message size. Maximum message size determines the point at which the
+ * processor will fail a connection with the message_too_big protocol error.
+ *
+ * The default is retrieved from the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ */
+ size_t get_max_message_size() const {
+ return m_max_message_size;
+ }
+
+ /// Set maximum message size
+ /**
+ * Set maximum message size. Maximum message size determines the point at which the
+ * processor will fail a connection with the message_too_big protocol error.
+ *
+ * The default is retrieved from the max_message_size value from the template config
+ *
+ * @since 0.3.0
+ *
+ * @param new_value The value to set as the maximum message size.
+ */
+ void set_max_message_size(size_t new_value) {
+ m_max_message_size = new_value;
+ }
+
+ /// Returns whether or not the permessage_compress extension is implemented
+ /**
+ * Compile time flag that indicates whether this processor has implemented
+ * the permessage_compress extension. By default this is false.
+ */
+ virtual bool has_permessage_compress() const {
+ return false;
+ }
+
+ /// Initializes extensions based on the Sec-WebSocket-Extensions header
+ /**
+ * Reads the Sec-WebSocket-Extensions header and determines if any of the
+ * requested extensions are supported by this processor. If they are their
+ * settings data is initialized and an extension string to send to the
+ * is returned.
+ *
+ * @param request The request or response headers to look at.
+ */
+ virtual err_str_pair negotiate_extensions(request_type const &) {
+ return err_str_pair();
+ }
+
+ /// Initializes extensions based on the Sec-WebSocket-Extensions header
+ /**
+ * Reads the Sec-WebSocket-Extensions header and determines if any of the
+ * requested extensions were accepted by the server. If they are their
+ * settings data is initialized. If they are not a list of required
+ * extensions (if any) is returned. This list may be sent back to the server
+ * as a part of the 1010/Extension required close code.
+ *
+ * @param response The request or response headers to look at.
+ */
+ virtual err_str_pair negotiate_extensions(response_type const &) {
+ return err_str_pair();
+ }
+
+ /// validate a WebSocket handshake request for this version
+ /**
+ * @param request The WebSocket handshake request to validate.
+ * is_websocket_handshake(request) must be true and
+ * get_websocket_version(request) must equal this->get_version().
+ *
+ * @return A status code, 0 on success, non-zero for specific sorts of
+ * failure
+ */
+ virtual lib::error_code validate_handshake(request_type const & request) const = 0;
+
+ /// Calculate the appropriate response for this websocket request
+ /**
+ * @param req The request to process
+ *
+ * @param subprotocol The subprotocol in use
+ *
+ * @param res The response to store the processed response in
+ *
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code process_handshake(request_type const & req,
+ std::string const & subprotocol, response_type& res) const = 0;
+
+ /// Fill in an HTTP request for an outgoing connection handshake
+ /**
+ * @param req The request to process.
+ *
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code client_handshake_request(request_type & req,
+ uri_ptr uri, std::vector<std::string> const & subprotocols) const = 0;
+
+ /// Validate the server's response to an outgoing handshake request
+ /**
+ * @param req The original request sent
+ * @param res The reponse to generate
+ * @return An error code, 0 on success, non-zero for other errors
+ */
+ virtual lib::error_code validate_server_handshake_response(request_type
+ const & req, response_type & res) const = 0;
+
+ /// Given a completed response, get the raw bytes to put on the wire
+ virtual std::string get_raw(response_type const & request) const = 0;
+
+ /// Return the value of the header containing the CORS origin.
+ virtual std::string const & get_origin(request_type const & request) const = 0;
+
+ /// Extracts requested subprotocols from a handshake request
+ /**
+ * Extracts a list of all subprotocols that the client has requested in the
+ * given opening handshake request.
+ *
+ * @param [in] req The request to extract from
+ * @param [out] subprotocol_list A reference to a vector of strings to store
+ * the results in.
+ */
+ virtual lib::error_code extract_subprotocols(const request_type & req,
+ std::vector<std::string> & subprotocol_list) = 0;
+
+ /// Extracts client uri from a handshake request
+ virtual uri_ptr get_uri(request_type const & request) const = 0;
+
+ /// process new websocket connection bytes
+ /**
+ * WebSocket connections are a continous stream of bytes that must be
+ * interpreted by a protocol processor into discrete frames.
+ *
+ * @param buf Buffer from which bytes should be read.
+ * @param len Length of buffer
+ * @param ec Reference to an error code to return any errors in
+ * @return Number of bytes processed
+ */
+ virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0;
+
+ /// Checks if there is a message ready
+ /**
+ * Checks if the most recent consume operation processed enough bytes to
+ * complete a new WebSocket message. The message can be retrieved by calling
+ * get_message() which will reset the internal state to not-ready and allow
+ * consume to read more bytes.
+ *
+ * @return Whether or not a message is ready.
+ */
+ virtual bool ready() const = 0;
+
+ /// Retrieves the most recently processed message
+ /**
+ * Retrieves a shared pointer to the recently completed message if there is
+ * one. If ready() returns true then there is a message available.
+ * Retrieving the message with get_message will reset the state of ready.
+ * As such, each new message may be retrieved only once. Calling get_message
+ * when there is no message available will result in a null pointer being
+ * returned.
+ *
+ * @return A pointer to the most recently processed message or a null shared
+ * pointer.
+ */
+ virtual message_ptr get_message() = 0;
+
+ /// Tests whether the processor is in a fatal error state
+ virtual bool get_error() const = 0;
+
+ /// Retrieves the number of bytes presently needed by the processor
+ /// This value may be used as a hint to the transport layer as to how many
+ /// bytes to wait for before running consume again.
+ virtual size_t get_bytes_needed() const {
+ return 1;
+ }
+
+ /// Prepare a data message for writing
+ /**
+ * Performs validation, masking, compression, etc. will return an error if
+ * there was an error, otherwise msg will be ready to be written
+ */
+ virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
+
+ /// Prepare a ping frame
+ /**
+ * Ping preparation is entirely state free. There is no payload validation
+ * other than length. Payload need not be UTF-8.
+ *
+ * @param in The string to use for the ping payload
+ * @param out The message buffer to prepare the ping in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
+ = 0;
+
+ /// Prepare a pong frame
+ /**
+ * Pong preparation is entirely state free. There is no payload validation
+ * other than length. Payload need not be UTF-8.
+ *
+ * @param in The string to use for the pong payload
+ * @param out The message buffer to prepare the pong in.
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
+ = 0;
+
+ /// Prepare a close frame
+ /**
+ * Close preparation is entirely state free. The code and reason are both
+ * subject to validation. Reason must be valid UTF-8. Code must be a valid
+ * un-reserved WebSocket close code. Use close::status::no_status to
+ * indicate no code. If no code is supplied a reason may not be specified.
+ *
+ * @param code The close code to send
+ * @param reason The reason string to send
+ * @param out The message buffer to prepare the fame in
+ * @return Status code, zero on success, non-zero on failure
+ */
+ virtual lib::error_code prepare_close(close::status::value code,
+ std::string const & reason, message_ptr out) const = 0;
+protected:
+ bool const m_secure;
+ bool const m_server;
+ size_t m_max_message_size;
+};
+
+} // namespace processor
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_PROCESSOR_HPP
diff --git a/websocketpp/random/none.hpp b/websocketpp/random/none.hpp
new file mode 100644
index 00000000..2163e6f4
--- /dev/null
+++ b/websocketpp/random/none.hpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_RANDOM_NONE_HPP
+#define WEBSOCKETPP_RANDOM_NONE_HPP
+
+namespace websocketpp {
+/// Random number generation policies
+namespace random {
+/// Stub RNG policy that always returns 0
+namespace none {
+
+/// Thread safe stub "random" integer generator.
+/**
+ * This template class provides a random integer stub. The interface mimics the
+ * WebSocket++ RNG generator classes but the generater function always returns
+ * zero. This can be used to stub out the RNG for unit and performance testing.
+ *
+ * Call operator() to generate the next number
+ */
+template <typename int_type>
+class int_generator {
+ public:
+ int_generator() {}
+
+ /// advances the engine's state and returns the generated value
+ int_type operator()() {
+ return 0;
+ }
+};
+
+} // namespace none
+} // namespace random
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_RANDOM_NONE_HPP
diff --git a/websocketpp/random/random_device.hpp b/websocketpp/random/random_device.hpp
new file mode 100644
index 00000000..91e7dd16
--- /dev/null
+++ b/websocketpp/random/random_device.hpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP
+#define WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP
+
+#include <websocketpp/common/random.hpp>
+
+namespace websocketpp {
+namespace random {
+/// RNG policy based on std::random_device or boost::random_device
+namespace random_device {
+
+/// Thread safe non-deterministic random integer generator.
+/**
+ * This template class provides thread safe non-deterministic random integer
+ * generation. Numbers are produced in a uniformly distributed range from the
+ * smallest to largest value that int_type can store.
+ *
+ * Thread-safety is provided via locking based on the concurrency template
+ * parameter.
+ *
+ * Non-deterministic RNG is provided via websocketpp::lib which uses either
+ * C++11 or Boost 1.47+'s random_device class.
+ *
+ * Call operator() to generate the next number
+ */
+template <typename int_type, typename concurrency>
+class int_generator {
+ public:
+ typedef typename concurrency::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency::mutex_type mutex_type;
+
+ /// constructor
+ //mac TODO: figure out if signed types present a range problem
+ int_generator() {}
+
+ /// advances the engine's state and returns the generated value
+ int_type operator()() {
+ scoped_lock_type guard(m_lock);
+ return m_dis(m_rng);
+ }
+ private:
+
+
+ lib::random_device m_rng;
+ lib::uniform_int_distribution<int_type> m_dis;
+
+ mutex_type m_lock;
+};
+
+} // namespace random_device
+} // namespace random
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_RANDOM_RANDOM_DEVICE_HPP
diff --git a/websocketpp/roles/client_endpoint.hpp b/websocketpp/roles/client_endpoint.hpp
new file mode 100644
index 00000000..2de1a10a
--- /dev/null
+++ b/websocketpp/roles/client_endpoint.hpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_CLIENT_ENDPOINT_HPP
+#define WEBSOCKETPP_CLIENT_ENDPOINT_HPP
+
+#include <websocketpp/endpoint.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+
+namespace websocketpp {
+
+/// Client endpoint role based on the given config
+/**
+ *
+ */
+template <typename config>
+class client : public endpoint<connection<config>,config> {
+public:
+ /// Type of this endpoint
+ typedef client<config> type;
+
+ /// Type of the endpoint concurrency component
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of the endpoint transport component
+ typedef typename config::transport_type transport_type;
+
+ /// Type of the connections this server will create
+ typedef connection<config> connection_type;
+ /// Type of a shared pointer to the connections this server will create
+ typedef typename connection_type::ptr connection_ptr;
+
+ /// Type of the connection transport component
+ typedef typename transport_type::transport_con_type transport_con_type;
+ /// Type of a shared pointer to the connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ /// Type of the endpoint component of this server
+ typedef endpoint<connection_type,config> endpoint_type;
+
+ friend class connection<config>;
+
+ explicit client() : endpoint_type(false)
+ {
+ endpoint_type::m_alog.write(log::alevel::devel, "client constructor");
+ }
+
+ /// Get a new connection
+ /**
+ * Creates and returns a pointer to a new connection to the given URI
+ * suitable for passing to connect(connection_ptr). This method allows
+ * applying connection specific settings before performing the opening
+ * handshake.
+ *
+ * @param [in] location URI to open the connection to as a uri_ptr
+ * @param [out] ec An status code indicating failure reasons, if any
+ *
+ * @return A connection_ptr to the new connection
+ */
+ connection_ptr get_connection(uri_ptr location, lib::error_code & ec) {
+ if (location->get_secure() && !transport_type::is_secure()) {
+ ec = error::make_error_code(error::endpoint_not_secure);
+ return connection_ptr();
+ }
+
+ connection_ptr con = endpoint_type::create_connection();
+
+ if (!con) {
+ ec = error::make_error_code(error::con_creation_failed);
+ return con;
+ }
+
+ con->set_uri(location);
+
+ ec = lib::error_code();
+ return con;
+ }
+
+ /// Get a new connection (string version)
+ /**
+ * Creates and returns a pointer to a new connection to the given URI
+ * suitable for passing to connect(connection_ptr). This overload allows
+ * default construction of the uri_ptr from a standard string.
+ *
+ * @param [in] u URI to open the connection to as a string
+ * @param [out] ec An status code indicating failure reasons, if any
+ *
+ * @return A connection_ptr to the new connection
+ */
+ connection_ptr get_connection(std::string const & u, lib::error_code & ec) {
+ uri_ptr location = lib::make_shared<uri>(u);
+
+ if (!location->get_valid()) {
+ ec = error::make_error_code(error::invalid_uri);
+ return connection_ptr();
+ }
+
+ return get_connection(location, ec);
+ }
+
+ /// Begin the connection process for the given connection
+ /**
+ * Initiates the opening connection handshake for connection con. Exact
+ * behavior depends on the underlying transport policy.
+ *
+ * @param con The connection to connect
+ *
+ * @return The pointer to the connection originally passed in.
+ */
+ connection_ptr connect(connection_ptr con) {
+ // Ask transport to perform a connection
+ transport_type::async_connect(
+ lib::static_pointer_cast<transport_con_type>(con),
+ con->get_uri(),
+ lib::bind(
+ &type::handle_connect,
+ this,
+ con,
+ lib::placeholders::_1
+ )
+ );
+
+ return con;
+ }
+private:
+ // handle_connect
+ void handle_connect(connection_ptr con, lib::error_code const & ec) {
+ if (ec) {
+ con->terminate(ec);
+
+ endpoint_type::m_elog.write(log::elevel::rerror,
+ "handle_connect error: "+ec.message());
+ } else {
+ endpoint_type::m_alog.write(log::alevel::connect,
+ "Successful connection");
+
+ con->start();
+ }
+ }
+};
+
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_CLIENT_ENDPOINT_HPP
diff --git a/websocketpp/roles/server_endpoint.hpp b/websocketpp/roles/server_endpoint.hpp
new file mode 100644
index 00000000..d76eea8a
--- /dev/null
+++ b/websocketpp/roles/server_endpoint.hpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_SERVER_ENDPOINT_HPP
+#define WEBSOCKETPP_SERVER_ENDPOINT_HPP
+
+#include <websocketpp/endpoint.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/system_error.hpp>
+
+namespace websocketpp {
+
+/// Server endpoint role based on the given config
+/**
+ *
+ */
+template <typename config>
+class server : public endpoint<connection<config>,config> {
+public:
+ /// Type of this endpoint
+ typedef server<config> type;
+
+ /// Type of the endpoint concurrency component
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of the endpoint transport component
+ typedef typename config::transport_type transport_type;
+
+ /// Type of the connections this server will create
+ typedef connection<config> connection_type;
+ /// Type of a shared pointer to the connections this server will create
+ typedef typename connection_type::ptr connection_ptr;
+
+ /// Type of the connection transport component
+ typedef typename transport_type::transport_con_type transport_con_type;
+ /// Type of a shared pointer to the connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ /// Type of the endpoint component of this server
+ typedef endpoint<connection_type,config> endpoint_type;
+
+ friend class connection<config>;
+
+ explicit server() : endpoint_type(true)
+ {
+ endpoint_type::m_alog.write(log::alevel::devel, "server constructor");
+ }
+
+ /// Destructor
+ ~server<config>() {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no copy constructor because endpoints are not copyable
+ server<config>(server<config> &) = delete;
+
+ // no copy assignment operator because endpoints are not copyable
+ server<config> & operator=(server<config> const &) = delete;
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
+ /// Move constructor
+ server<config>(server<config> && o) : endpoint<connection<config>,config>(std::move(o)) {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ // no move assignment operator because of const member variables
+ server<config> & operator=(server<config> &&) = delete;
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
+
+ /// Create and initialize a new connection
+ /**
+ * The connection will be initialized and ready to begin. Call its start()
+ * method to begin the processing loop.
+ *
+ * Note: The connection must either be started or terminated using
+ * connection::terminate in order to avoid memory leaks.
+ *
+ * @return A pointer to the new connection.
+ */
+ connection_ptr get_connection() {
+ return endpoint_type::create_connection();
+ }
+
+ /// Starts the server's async connection acceptance loop (exception free)
+ /**
+ * Initiates the server connection acceptance loop. Must be called after
+ * listen. This method will have no effect until the underlying io_service
+ * starts running. It may be called after the io_service is already running.
+ *
+ * Refer to documentation for the transport policy you are using for
+ * instructions on how to stop this acceptance loop.
+ *
+ * @param [out] ec A status code indicating an error, if any.
+ */
+ void start_accept(lib::error_code & ec) {
+ if (!transport_type::is_listening()) {
+ ec = error::make_error_code(error::async_accept_not_listening);
+ return;
+ }
+
+ ec = lib::error_code();
+ connection_ptr con = get_connection();
+
+ transport_type::async_accept(
+ lib::static_pointer_cast<transport_con_type>(con),
+ lib::bind(&type::handle_accept,this,con,lib::placeholders::_1),
+ ec
+ );
+
+ if (ec && con) {
+ // If the connection was constructed but the accept failed,
+ // terminate the connection to prevent memory leaks
+ con->terminate(lib::error_code());
+ }
+ }
+
+ /// Starts the server's async connection acceptance loop
+ /**
+ * Initiates the server connection acceptance loop. Must be called after
+ * listen. This method will have no effect until the underlying io_service
+ * starts running. It may be called after the io_service is already running.
+ *
+ * Refer to documentation for the transport policy you are using for
+ * instructions on how to stop this acceptance loop.
+ */
+ void start_accept() {
+ lib::error_code ec;
+ start_accept(ec);
+ if (ec) {
+ throw exception(ec);
+ }
+ }
+
+ /// Handler callback for start_accept
+ void handle_accept(connection_ptr con, lib::error_code const & ec) {
+ if (ec) {
+ con->terminate(ec);
+
+ if (ec == error::operation_canceled) {
+ endpoint_type::m_elog.write(log::elevel::info,
+ "handle_accept error: "+ec.message());
+ } else {
+ endpoint_type::m_elog.write(log::elevel::rerror,
+ "handle_accept error: "+ec.message());
+ }
+ } else {
+ con->start();
+ }
+
+ lib::error_code start_ec;
+ start_accept(start_ec);
+ if (start_ec == error::async_accept_not_listening) {
+ endpoint_type::m_elog.write(log::elevel::info,
+ "Stopping acceptance of new connections because the underlying transport is no longer listening.");
+ } else if (start_ec) {
+ endpoint_type::m_elog.write(log::elevel::rerror,
+ "Restarting async_accept loop failed: "+ec.message());
+ }
+ }
+};
+
+} // namespace websocketpp
+
+#endif //WEBSOCKETPP_SERVER_ENDPOINT_HPP
diff --git a/websocketpp/server.hpp b/websocketpp/server.hpp
new file mode 100644
index 00000000..342fa8c5
--- /dev/null
+++ b/websocketpp/server.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_SERVER_HPP
+#define WEBSOCKETPP_SERVER_HPP
+
+#include <websocketpp/roles/server_endpoint.hpp>
+
+#endif //WEBSOCKETPP_SERVER_HPP
diff --git a/websocketpp/sha1/sha1.hpp b/websocketpp/sha1/sha1.hpp
new file mode 100644
index 00000000..43a84338
--- /dev/null
+++ b/websocketpp/sha1/sha1.hpp
@@ -0,0 +1,189 @@
+/*
+*****
+sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1
+library (http://code.google.com/p/smallsha1/) into a single header suitable for
+use as a header only library. This conversion was done by Peter Thorson
+(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed
+under the same license as the original, which is listed below.
+*****
+
+ Copyright (c) 2011, Micael Hildenborg
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Micael Hildenborg nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SHA1_DEFINED
+#define SHA1_DEFINED
+
+namespace websocketpp {
+namespace sha1 {
+
+namespace { // local
+
+// Rotate an integer value to left.
+inline unsigned int rol(unsigned int value, unsigned int steps) {
+ return ((value << steps) | (value >> (32 - steps)));
+}
+
+// Sets the first 16 integers in the buffert to zero.
+// Used for clearing the W buffert.
+inline void clearWBuffert(unsigned int * buffert)
+{
+ for (int pos = 16; --pos >= 0;)
+ {
+ buffert[pos] = 0;
+ }
+}
+
+inline void innerHash(unsigned int * result, unsigned int * w)
+{
+ unsigned int a = result[0];
+ unsigned int b = result[1];
+ unsigned int c = result[2];
+ unsigned int d = result[3];
+ unsigned int e = result[4];
+
+ int round = 0;
+
+ #define sha1macro(func,val) \
+ { \
+ const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
+ e = d; \
+ d = c; \
+ c = rol(b, 30); \
+ b = a; \
+ a = t; \
+ }
+
+ while (round < 16)
+ {
+ sha1macro((b & c) | (~b & d), 0x5a827999)
+ ++round;
+ }
+ while (round < 20)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro((b & c) | (~b & d), 0x5a827999)
+ ++round;
+ }
+ while (round < 40)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro(b ^ c ^ d, 0x6ed9eba1)
+ ++round;
+ }
+ while (round < 60)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
+ ++round;
+ }
+ while (round < 80)
+ {
+ w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
+ sha1macro(b ^ c ^ d, 0xca62c1d6)
+ ++round;
+ }
+
+ #undef sha1macro
+
+ result[0] += a;
+ result[1] += b;
+ result[2] += c;
+ result[3] += d;
+ result[4] += e;
+}
+
+} // namespace
+
+/// Calculate a SHA1 hash
+/**
+ * @param src points to any kind of data to be hashed.
+ * @param bytelength the number of bytes to hash from the src pointer.
+ * @param hash should point to a buffer of at least 20 bytes of size for storing
+ * the sha1 result in.
+ */
+inline void calc(void const * src, size_t bytelength, unsigned char * hash) {
+ // Init the result array.
+ unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe,
+ 0x10325476, 0xc3d2e1f0 };
+
+ // Cast the void src pointer to be the byte array we can work with.
+ unsigned char const * sarray = (unsigned char const *) src;
+
+ // The reusable round buffer
+ unsigned int w[80];
+
+ // Loop through all complete 64byte blocks.
+
+ size_t endCurrentBlock;
+ size_t currentBlock = 0;
+
+ if (bytelength >= 64) {
+ size_t const endOfFullBlocks = bytelength - 64;
+
+ while (currentBlock <= endOfFullBlocks) {
+ endCurrentBlock = currentBlock + 64;
+
+ // Init the round buffer with the 64 byte block data.
+ for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
+ {
+ // This line will swap endian on big endian and keep endian on
+ // little endian.
+ w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
+ | (((unsigned int) sarray[currentBlock + 2]) << 8)
+ | (((unsigned int) sarray[currentBlock + 1]) << 16)
+ | (((unsigned int) sarray[currentBlock]) << 24);
+ }
+ innerHash(result, w);
+ }
+ }
+
+ // Handle the last and not full 64 byte block if existing.
+ endCurrentBlock = bytelength - currentBlock;
+ clearWBuffert(w);
+ size_t lastBlockBytes = 0;
+ for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) {
+ w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
+ }
+
+ w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
+ if (endCurrentBlock >= 56) {
+ innerHash(result, w);
+ clearWBuffert(w);
+ }
+ w[15] = bytelength << 3;
+ innerHash(result, w);
+
+ // Store hash in result pointer, and make sure we get in in the correct
+ // order on both endian models.
+ for (int hashByte = 20; --hashByte >= 0;) {
+ hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
+ }
+}
+
+} // namespace sha1
+} // namespace websocketpp
+
+#endif // SHA1_DEFINED
diff --git a/websocketpp/transport/asio/base.hpp b/websocketpp/transport/asio/base.hpp
new file mode 100644
index 00000000..b945fe11
--- /dev/null
+++ b/websocketpp/transport/asio/base.hpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/type_traits.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Transport policy that uses asio
+/**
+ * This policy uses a single asio io_service to provide transport
+ * services to a WebSocket++ endpoint.
+ */
+namespace asio {
+
+// Class to manage the memory to be used for handler-based custom allocation.
+// It contains a single block of memory which may be returned for allocation
+// requests. If the memory is in use when an allocation request is made, the
+// allocator delegates allocation to the global heap.
+class handler_allocator {
+public:
+ static const size_t size = 1024;
+
+ handler_allocator() : m_in_use(false) {}
+
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ handler_allocator(handler_allocator const & cpy) = delete;
+ handler_allocator & operator =(handler_allocator const &) = delete;
+#endif
+
+ void * allocate(std::size_t memsize) {
+ if (!m_in_use && memsize < size) {
+ m_in_use = true;
+ return static_cast<void*>(&m_storage);
+ } else {
+ return ::operator new(memsize);
+ }
+ }
+
+ void deallocate(void * pointer) {
+ if (pointer == &m_storage) {
+ m_in_use = false;
+ } else {
+ ::operator delete(pointer);
+ }
+ }
+
+private:
+ // Storage space used for handler-based custom memory allocation.
+ lib::aligned_storage<size>::type m_storage;
+
+ // Whether the handler-based custom allocation storage has been used.
+ bool m_in_use;
+};
+
+// Wrapper class template for handler objects to allow handler memory
+// allocation to be customised. Calls to operator() are forwarded to the
+// encapsulated handler.
+template <typename Handler>
+class custom_alloc_handler {
+public:
+ custom_alloc_handler(handler_allocator& a, Handler h)
+ : allocator_(a),
+ handler_(h)
+ {}
+
+ template <typename Arg1>
+ void operator()(Arg1 arg1) {
+ handler_(arg1);
+ }
+
+ template <typename Arg1, typename Arg2>
+ void operator()(Arg1 arg1, Arg2 arg2) {
+ handler_(arg1, arg2);
+ }
+
+ friend void* asio_handler_allocate(std::size_t size,
+ custom_alloc_handler<Handler> * this_handler)
+ {
+ return this_handler->allocator_.allocate(size);
+ }
+
+ friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
+ custom_alloc_handler<Handler> * this_handler)
+ {
+ this_handler->allocator_.deallocate(pointer);
+ }
+
+private:
+ handler_allocator & allocator_;
+ Handler handler_;
+};
+
+// Helper function to wrap a handler object to add custom allocation.
+template <typename Handler>
+inline custom_alloc_handler<Handler> make_custom_alloc_handler(
+ handler_allocator & a, Handler h)
+{
+ return custom_alloc_handler<Handler>(a, h);
+}
+
+
+
+
+
+
+
+// Forward declaration of class endpoint so that it can be friended/referenced
+// before being included.
+template <typename config>
+class endpoint;
+
+typedef lib::function<void (lib::asio::error_code const & ec,
+ size_t bytes_transferred)> async_read_handler;
+
+typedef lib::function<void (lib::asio::error_code const & ec,
+ size_t bytes_transferred)> async_write_handler;
+
+typedef lib::function<void (lib::error_code const & ec)> pre_init_handler;
+
+// handle_timer: dynamic parameters, multiple copies
+// handle_proxy_write
+// handle_proxy_read
+// handle_async_write
+// handle_pre_init
+
+
+/// Asio transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// there was an error in the underlying transport library
+ pass_through,
+
+ /// The connection to the requested proxy server failed
+ proxy_failed,
+
+ /// Invalid Proxy URI
+ proxy_invalid,
+
+ /// Invalid host or service
+ invalid_host_service
+};
+
+/// Asio transport error category
+class category : public lib::error_category {
+public:
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.asio";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::general:
+ return "Generic asio transport policy error";
+ case error::invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case error::pass_through:
+ return "Underlying Transport Error";
+ case error::proxy_failed:
+ return "Proxy connection failed";
+ case error::proxy_invalid:
+ return "Invalid proxy URI";
+ case error::invalid_host_service:
+ return "Invalid host or service";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the asio transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Create an error code with the given value and the asio transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::asio::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP
diff --git a/websocketpp/transport/asio/connection.hpp b/websocketpp/transport/asio/connection.hpp
new file mode 100644
index 00000000..8eb8c759
--- /dev/null
+++ b/websocketpp/transport/asio/connection.hpp
@@ -0,0 +1,1204 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
+
+#include <websocketpp/transport/asio/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+#include <websocketpp/http/constants.hpp>
+
+#include <websocketpp/base64/base64.hpp>
+#include <websocketpp/error.hpp>
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/chrono.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <istream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+
+typedef lib::function<void(connection_hdl)> tcp_init_handler;
+
+/// Asio based connection transport component
+/**
+ * transport::asio::connection implements a connection transport component using
+ * Asio that works with the transport::asio::endpoint endpoint transport
+ * component.
+ */
+template <typename config>
+class connection : public config::socket_type::socket_con_type {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of the socket connection component
+ typedef typename config::socket_type::socket_con_type socket_con_type;
+ /// Type of a shared pointer to the socket connection component
+ typedef typename socket_con_type::ptr socket_con_ptr;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ typedef typename config::request_type request_type;
+ typedef typename request_type::ptr request_ptr;
+ typedef typename config::response_type response_type;
+ typedef typename response_type::ptr response_ptr;
+
+ /// Type of a pointer to the Asio io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a pointer to the Asio io_service::strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of a pointer to the Asio timer class
+ typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
+
+ // connection is friends with its associated endpoint to allow the endpoint
+ // to call private/protected utility methods that we don't want to expose
+ // to the public api.
+ friend class endpoint<config>;
+
+ // generate and manage our own io_service
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_is_server(is_server)
+ , m_alog(alog)
+ , m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"asio con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return lib::static_pointer_cast<type>(socket_con_type::get_shared());
+ }
+
+ bool is_secure() const {
+ return socket_con_type::is_secure();
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * This transport policy doesn't use the uri except to forward it to the
+ * socket layer.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr u) {
+ socket_con_type::set_uri(u);
+ }
+
+ /// Sets the tcp pre init handler
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_pre_init_handler(tcp_init_handler h) {
+ m_tcp_pre_init_handler = h;
+ }
+
+ /// Sets the tcp pre init handler (deprecated)
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @deprecated Use set_tcp_pre_init_handler instead
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_init_handler(tcp_init_handler h) {
+ set_tcp_pre_init_handler(h);
+ }
+
+ /// Sets the tcp post init handler
+ /**
+ * The tcp post init handler is called after the tcp connection has been
+ * established and all additional wrappers (proxy connects, TLS handshakes,
+ * etc have been performed. This is fired before any bytes are read or any
+ * WebSocket specific handshake logic has been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp post init.
+ */
+ void set_tcp_post_init_handler(tcp_init_handler h) {
+ m_tcp_post_init_handler = h;
+ }
+
+ /// Set the proxy to connect through (exception free)
+ /**
+ * The URI passed should be a complete URI including scheme. For example:
+ * http://proxy.example.com:8080/
+ *
+ * The proxy must be set up as an explicit (CONNECT) proxy allowed to
+ * connect to the port you specify. Traffic to the proxy is not encrypted.
+ *
+ * @param uri The full URI of the proxy to connect to.
+ *
+ * @param ec A status value
+ */
+ void set_proxy(std::string const & uri, lib::error_code & ec) {
+ // TODO: return errors for illegal URIs here?
+ // TODO: should https urls be illegal for the moment?
+ m_proxy = uri;
+ m_proxy_data = lib::make_shared<proxy_data>();
+ ec = lib::error_code();
+ }
+
+ /// Set the proxy to connect through (exception)
+ void set_proxy(std::string const & uri) {
+ lib::error_code ec;
+ set_proxy(uri,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set the basic auth credentials to use (exception free)
+ /**
+ * The URI passed should be a complete URI including scheme. For example:
+ * http://proxy.example.com:8080/
+ *
+ * The proxy must be set up as an explicit proxy
+ *
+ * @param username The username to send
+ *
+ * @param password The password to send
+ *
+ * @param ec A status value
+ */
+ void set_proxy_basic_auth(std::string const & username, std::string const &
+ password, lib::error_code & ec)
+ {
+ if (!m_proxy_data) {
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ // TODO: username can't contain ':'
+ std::string val = "Basic "+base64_encode(username + ":" + password);
+ m_proxy_data->req.replace_header("Proxy-Authorization",val);
+ ec = lib::error_code();
+ }
+
+ /// Set the basic auth credentials to use (exception)
+ void set_proxy_basic_auth(std::string const & username, std::string const &
+ password)
+ {
+ lib::error_code ec;
+ set_proxy_basic_auth(username,password,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set the proxy timeout duration (exception free)
+ /**
+ * Duration is in milliseconds. Default value is based on the transport
+ * config
+ *
+ * @param duration The number of milliseconds to wait before aborting the
+ * proxy connection.
+ *
+ * @param ec A status value
+ */
+ void set_proxy_timeout(long duration, lib::error_code & ec) {
+ if (!m_proxy_data) {
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_proxy_data->timeout_proxy = duration;
+ ec = lib::error_code();
+ }
+
+ /// Set the proxy timeout duration (exception)
+ void set_proxy_timeout(long duration) {
+ lib::error_code ec;
+ set_proxy_timeout(duration,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ std::string const & get_proxy() const {
+ return m_proxy;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ lib::error_code ec;
+
+ std::string ret = socket_con_type::get_remote_endpoint(ec);
+
+ if (ec) {
+ m_elog.write(log::elevel::info,ret);
+ return "Unknown";
+ } else {
+ return ret;
+ }
+ }
+
+ /// Get the connection handle
+ connection_hdl get_handle() const {
+ return m_connection_hdl;
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Sets a timer that calls back a function after the specified period of
+ * milliseconds. Returns a handle that can be used to cancel the timer.
+ * A cancelled timer will return the error code error::operation_aborted
+ * A timer that expired will return no error.
+ *
+ * @param duration Length of time to wait in milliseconds
+ *
+ * @param callback The function to call back when the timer has expired
+ *
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler callback) {
+ timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
+ lib::ref(*m_io_service),
+ lib::asio::milliseconds(duration)
+ );
+
+ if (config::enable_multithreading) {
+ new_timer->async_wait(m_strand->wrap(lib::bind(
+ &type::handle_timer, get_shared(),
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ )));
+ } else {
+ new_timer->async_wait(lib::bind(
+ &type::handle_timer, get_shared(),
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ ));
+ }
+
+ return new_timer;
+ }
+
+ /// Timer callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * TODO: candidate for protected status
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_timer(timer_ptr, timer_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (ec) {
+ if (ec == lib::asio::error::operation_aborted) {
+ callback(make_error_code(transport::error::operation_aborted));
+ } else {
+ log_err(log::elevel::info,"asio handle_timer",ec);
+ callback(make_error_code(error::pass_through));
+ }
+ } else {
+ callback(lib::error_code());
+ }
+ }
+
+ /// Get a pointer to this connection's strand
+ strand_ptr get_strand() {
+ return m_strand;
+ }
+
+ /// Get the internal transport error code for a closed/failed connection
+ /**
+ * Retrieves a machine readable detailed error code indicating the reason
+ * that the connection was closed or failed. Valid only after the close or
+ * fail handler is called.
+ *
+ * Primarily used if you are using mismatched asio / system_error
+ * implementations such as `boost::asio` with `std::system_error`. In these
+ * cases the transport error type is different than the library error type
+ * and some WebSocket++ functions that return transport errors via the
+ * library error code type will be coerced into a catch all `pass_through`
+ * or `tls_error` error. This method will return the original machine
+ * readable transport error in the native type.
+ *
+ * @since 0.7.0
+ *
+ * @return Error code indicating the reason the connection was closed or
+ * failed
+ */
+ lib::asio::error_code get_transport_ec() const {
+ return m_tec;
+ }
+
+ /// Initialize transport for reading
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * The transport initialization sequence consists of the following steps:
+ * - Pre-init: the underlying socket is initialized to the point where
+ * bytes may be written. No bytes are actually written in this stage
+ * - Proxy negotiation: if a proxy is set, a request is made to it to start
+ * a tunnel to the final destination. This stage ends when the proxy is
+ * ready to forward the
+ * next byte to the remote endpoint.
+ * - Post-init: Perform any i/o with the remote endpoint, such as setting up
+ * tunnels for encryption. This stage ends when the connection is ready to
+ * read or write the WebSocket handshakes. At this point the original
+ * callback function is called.
+ */
+protected:
+ void init(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection init");
+ }
+
+ // TODO: pre-init timeout. Right now no implemented socket policies
+ // actually have an asyncronous pre-init
+
+ socket_con_type::pre_init(
+ lib::bind(
+ &type::handle_pre_init,
+ get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// initialize the proxy buffers and http parsers
+ /**
+ *
+ * @param authority The address of the server we want the proxy to tunnel to
+ * in the format of a URI authority (host:port)
+ *
+ * @return Status code indicating what errors occurred, if any
+ */
+ lib::error_code proxy_init(std::string const & authority) {
+ if (!m_proxy_data) {
+ return websocketpp::error::make_error_code(
+ websocketpp::error::invalid_state);
+ }
+ m_proxy_data->req.set_version("HTTP/1.1");
+ m_proxy_data->req.set_method("CONNECT");
+
+ m_proxy_data->req.set_uri(authority);
+ m_proxy_data->req.replace_header("Host",authority);
+
+ return lib::error_code();
+ }
+
+ /// Finish constructing the transport
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service.
+ *
+ * @param io_service A pointer to the io_service to register with this
+ * connection
+ *
+ * @return Status code for the success or failure of the initialization
+ */
+ lib::error_code init_asio (io_service_ptr io_service) {
+ m_io_service = io_service;
+
+ if (config::enable_multithreading) {
+ m_strand = lib::make_shared<lib::asio::io_service::strand>(
+ lib::ref(*io_service));
+ }
+
+ lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
+ m_is_server);
+
+ return ec;
+ }
+
+ void handle_pre_init(init_handler callback, lib::error_code const & ec) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection handle pre_init");
+ }
+
+ if (m_tcp_pre_init_handler) {
+ m_tcp_pre_init_handler(m_connection_hdl);
+ }
+
+ if (ec) {
+ callback(ec);
+ }
+
+ // If we have a proxy set issue a proxy connect, otherwise skip to
+ // post_init
+ if (!m_proxy.empty()) {
+ proxy_write(callback);
+ } else {
+ post_init(callback);
+ }
+ }
+
+ void post_init(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection post_init");
+ }
+
+ timer_ptr post_timer;
+
+ if (config::timeout_socket_post_init > 0) {
+ post_timer = set_timer(
+ config::timeout_socket_post_init,
+ lib::bind(
+ &type::handle_post_init_timeout,
+ get_shared(),
+ post_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ socket_con_type::post_init(
+ lib::bind(
+ &type::handle_post_init,
+ get_shared(),
+ post_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// Post init timeout callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_post_init_timeout(timer_ptr, init_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio post init timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_post_init_timeout",ec);
+ ret_ec = ec;
+ } else {
+ if (socket_con_type::get_ec()) {
+ ret_ec = socket_con_type::get_ec();
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+ }
+
+ m_alog.write(log::alevel::devel, "Asio transport post-init timed out");
+ cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ /// Post init timeout callback
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param post_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_post_init(timer_ptr post_timer, init_handler callback,
+ lib::error_code const & ec)
+ {
+ if (ec == transport::error::operation_aborted ||
+ (post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
+ {
+ m_alog.write(log::alevel::devel,"post_init cancelled");
+ return;
+ }
+
+ if (post_timer) {
+ post_timer->cancel();
+ }
+
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection handle_post_init");
+ }
+
+ if (m_tcp_post_init_handler) {
+ m_tcp_post_init_handler(m_connection_hdl);
+ }
+
+ callback(ec);
+ }
+
+ void proxy_write(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection proxy_write");
+ }
+
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::proxy_write");
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ m_proxy_data->write_buf = m_proxy_data->req.raw();
+
+ m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
+ m_proxy_data->write_buf.size()));
+
+ m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
+
+ // Set a timer so we don't wait forever for the proxy to respond
+ m_proxy_data->timer = this->set_timer(
+ m_proxy_data->timeout_proxy,
+ lib::bind(
+ &type::handle_proxy_timeout,
+ get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ // Send proxy request
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_next_layer(),
+ m_bufs,
+ m_strand->wrap(lib::bind(
+ &type::handle_proxy_write, get_shared(),
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_next_layer(),
+ m_bufs,
+ lib::bind(
+ &type::handle_proxy_write, get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ void handle_proxy_timeout(init_handler callback, lib::error_code const & ec)
+ {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio handle_proxy_write timer cancelled");
+ return;
+ } else if (ec) {
+ log_err(log::elevel::devel,"asio handle_proxy_write",ec);
+ callback(ec);
+ } else {
+ m_alog.write(log::alevel::devel,
+ "asio handle_proxy_write timer expired");
+ cancel_socket_checked();
+ callback(make_error_code(transport::error::timeout));
+ }
+ }
+
+ void handle_proxy_write(init_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio connection handle_proxy_write");
+ }
+
+ m_bufs.clear();
+
+ // Timer expired or the operation was aborted for some reason.
+ // Whatever aborted it will be issuing the callback so we are safe to
+ // return
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
+ {
+ m_elog.write(log::elevel::devel,"write operation aborted");
+ return;
+ }
+
+ if (ec) {
+ log_err(log::elevel::info,"asio handle_proxy_write",ec);
+ m_proxy_data->timer->cancel();
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ proxy_read(callback);
+ }
+
+ void proxy_read(init_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection proxy_read");
+ }
+
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::proxy_read");
+ m_proxy_data->timer->cancel();
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ if (config::enable_multithreading) {
+ lib::asio::async_read_until(
+ socket_con_type::get_next_layer(),
+ m_proxy_data->read_buf,
+ "\r\n\r\n",
+ m_strand->wrap(lib::bind(
+ &type::handle_proxy_read, get_shared(),
+ callback,
+ lib::placeholders::_1, lib::placeholders::_2
+ ))
+ );
+ } else {
+ lib::asio::async_read_until(
+ socket_con_type::get_next_layer(),
+ m_proxy_data->read_buf,
+ "\r\n\r\n",
+ lib::bind(
+ &type::handle_proxy_read, get_shared(),
+ callback,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ );
+ }
+ }
+
+ /// Proxy read callback
+ /**
+ * @param init_handler The function to call back
+ * @param ec The status code
+ * @param bytes_transferred The number of bytes read
+ */
+ void handle_proxy_read(init_handler callback,
+ lib::asio::error_code const & ec, size_t)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio connection handle_proxy_read");
+ }
+
+ // Timer expired or the operation was aborted for some reason.
+ // Whatever aborted it will be issuing the callback so we are safe to
+ // return
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
+ {
+ m_elog.write(log::elevel::devel,"read operation aborted");
+ return;
+ }
+
+ // At this point there is no need to wait for the timer anymore
+ m_proxy_data->timer->cancel();
+
+ if (ec) {
+ m_elog.write(log::elevel::info,
+ "asio handle_proxy_read error: "+ec.message());
+ callback(make_error_code(error::pass_through));
+ } else {
+ if (!m_proxy_data) {
+ m_elog.write(log::elevel::library,
+ "assertion failed: !m_proxy_data in asio::connection::handle_proxy_read");
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ std::istream input(&m_proxy_data->read_buf);
+
+ m_proxy_data->res.consume(input);
+
+ if (!m_proxy_data->res.headers_ready()) {
+ // we read until the headers were done in theory but apparently
+ // they aren't. Internal endpoint error.
+ callback(make_error_code(error::general));
+ return;
+ }
+
+ m_alog.write(log::alevel::devel,m_proxy_data->res.raw());
+
+ if (m_proxy_data->res.get_status_code() != http::status_code::ok) {
+ // got an error response back
+ // TODO: expose this error in a programmatically accessible way?
+ // if so, see below for an option on how to do this.
+ std::stringstream s;
+ s << "Proxy connection error: "
+ << m_proxy_data->res.get_status_code()
+ << " ("
+ << m_proxy_data->res.get_status_msg()
+ << ")";
+ m_elog.write(log::elevel::info,s.str());
+ callback(make_error_code(error::proxy_failed));
+ return;
+ }
+
+ // we have successfully established a connection to the proxy, now
+ // we can continue and the proxy will transparently forward the
+ // WebSocket connection.
+
+ // TODO: decide if we want an on_proxy callback that would allow
+ // access to the proxy response.
+
+ // free the proxy buffers and req/res objects as they aren't needed
+ // anymore
+ m_proxy_data.reset();
+
+ // Continue with post proxy initialization
+ post_init(callback);
+ }
+ }
+
+ /// read at least num_bytes bytes into buf and then call handler.
+ void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ read_handler handler)
+ {
+ if (m_alog.static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "asio async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+ }
+
+ // TODO: safety vs speed ?
+ // maybe move into an if devel block
+ /*if (num_bytes > len) {
+ m_elog.write(log::elevel::devel,
+ "asio async_read_at_least error::invalid_num_bytes");
+ handler(make_error_code(transport::error::invalid_num_bytes),
+ size_t(0));
+ return;
+ }*/
+
+ if (config::enable_multithreading) {
+ lib::asio::async_read(
+ socket_con_type::get_socket(),
+ lib::asio::buffer(buf,len),
+ lib::asio::transfer_at_least(num_bytes),
+ m_strand->wrap(make_custom_alloc_handler(
+ m_read_handler_allocator,
+ lib::bind(
+ &type::handle_async_read, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_read(
+ socket_con_type::get_socket(),
+ lib::asio::buffer(buf,len),
+ lib::asio::transfer_at_least(num_bytes),
+ make_custom_alloc_handler(
+ m_read_handler_allocator,
+ lib::bind(
+ &type::handle_async_read, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+
+ }
+
+ void handle_async_read(read_handler handler, lib::asio::error_code const & ec,
+ size_t bytes_transferred)
+ {
+ m_alog.write(log::alevel::devel, "asio con handle_async_read");
+
+ // translate asio error codes into more lib::error_codes
+ lib::error_code tec;
+ if (ec == lib::asio::error::eof) {
+ tec = make_error_code(transport::error::eof);
+ } else if (ec) {
+ // We don't know much more about the error at this point. As our
+ // socket/security policy if it knows more:
+ tec = socket_con_type::translate_ec(ec);
+ m_tec = ec;
+
+ if (tec == transport::error::tls_error ||
+ tec == transport::error::pass_through)
+ {
+ // These are aggregate/catch all errors. Log some human readable
+ // information to the info channel to give library users some
+ // more details about why the upstream method may have failed.
+ log_err(log::elevel::info,"asio async_read_at_least",ec);
+ }
+ }
+ if (handler) {
+ handler(tec,bytes_transferred);
+ } else {
+ // This can happen in cases where the connection is terminated while
+ // the transport is waiting on a read.
+ m_alog.write(log::alevel::devel,
+ "handle_async_read called with null read handler");
+ }
+ }
+
+ /// Initiate a potentially asyncronous write of the given buffer
+ void async_write(const char* buf, size_t len, write_handler handler) {
+ m_bufs.push_back(lib::asio::buffer(buf,len));
+
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ m_strand->wrap(make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+ }
+
+ /// Initiate a potentially asyncronous write of the given buffers
+ void async_write(std::vector<buffer> const & bufs, write_handler handler) {
+ std::vector<buffer>::const_iterator it;
+
+ for (it = bufs.begin(); it != bufs.end(); ++it) {
+ m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
+ }
+
+ if (config::enable_multithreading) {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ m_strand->wrap(make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ ))
+ );
+ } else {
+ lib::asio::async_write(
+ socket_con_type::get_socket(),
+ m_bufs,
+ make_custom_alloc_handler(
+ m_write_handler_allocator,
+ lib::bind(
+ &type::handle_async_write, get_shared(),
+ handler,
+ lib::placeholders::_1, lib::placeholders::_2
+ )
+ )
+ );
+ }
+ }
+
+ /// Async write callback
+ /**
+ * @param ec The status code
+ * @param bytes_transferred The number of bytes read
+ */
+ void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) {
+ m_bufs.clear();
+ lib::error_code tec;
+ if (ec) {
+ log_err(log::elevel::info,"asio async_write",ec);
+ tec = make_error_code(transport::error::pass_through);
+ }
+ if (handler) {
+ handler(tec);
+ } else {
+ // This can happen in cases where the connection is terminated while
+ // the transport is waiting on a read.
+ m_alog.write(log::alevel::devel,
+ "handle_async_write called with null write handler");
+ }
+ }
+
+ /// Set Connection Handle
+ /**
+ * See common/connection_hdl.hpp for information
+ *
+ * @param hdl A connection_hdl that the transport will use to refer
+ * to itself
+ */
+ void set_handle(connection_hdl hdl) {
+ m_connection_hdl = hdl;
+ socket_con_type::set_handle(hdl);
+ }
+
+ /// Trigger the on_interrupt handler
+ /**
+ * This needs to be thread safe
+ */
+ lib::error_code interrupt(interrupt_handler handler) {
+ if (config::enable_multithreading) {
+ m_io_service->post(m_strand->wrap(handler));
+ } else {
+ m_io_service->post(handler);
+ }
+ return lib::error_code();
+ }
+
+ lib::error_code dispatch(dispatch_handler handler) {
+ if (config::enable_multithreading) {
+ m_io_service->post(m_strand->wrap(handler));
+ } else {
+ m_io_service->post(handler);
+ }
+ return lib::error_code();
+ }
+
+ /*void handle_interrupt(interrupt_handler handler) {
+ handler();
+ }*/
+
+ /// close and clean up the underlying socket
+ void async_shutdown(shutdown_handler callback) {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,"asio connection async_shutdown");
+ }
+
+ timer_ptr shutdown_timer;
+ shutdown_timer = set_timer(
+ config::timeout_socket_shutdown,
+ lib::bind(
+ &type::handle_async_shutdown_timeout,
+ get_shared(),
+ shutdown_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ socket_con_type::async_shutdown(
+ lib::bind(
+ &type::handle_async_shutdown,
+ get_shared(),
+ shutdown_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+
+ /// Async shutdown timeout handler
+ /**
+ * @param shutdown_timer A pointer to the timer to keep it in scope
+ * @param callback The function to call back
+ * @param ec The status code
+ */
+ void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog.write(log::alevel::devel,
+ "asio socket shutdown timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog.write(log::alevel::devel,
+ "Asio transport socket shutdown timed out");
+ cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
+ callback, lib::asio::error_code const & ec)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(shutdown_timer->expires_from_now()))
+ {
+ m_alog.write(log::alevel::devel,"async_shutdown cancelled");
+ return;
+ }
+
+ shutdown_timer->cancel();
+
+ lib::error_code tec;
+ if (ec) {
+ if (ec == lib::asio::error::not_connected) {
+ // The socket was already closed when we tried to close it. This
+ // happens periodically (usually if a read or write fails
+ // earlier and if it is a real error will be caught at another
+ // level of the stack.
+ } else {
+ // We don't know anything more about this error, give our
+ // socket/security policy a crack at it.
+ tec = socket_con_type::translate_ec(ec);
+ m_tec = ec;
+
+ if (tec == transport::error::tls_short_read) {
+ // TLS short read at this point is somewhat expected if both
+ // sides try and end the connection at the same time or if
+ // SSLv2 is being used. In general there is nothing that can
+ // be done here other than a low level development log.
+ } else {
+ // all other errors are effectively pass through errors of
+ // some sort so print some detail on the info channel for
+ // library users to look up if needed.
+ log_err(log::elevel::info,"asio async_shutdown",ec);
+ }
+ }
+ } else {
+ if (m_alog.static_test(log::alevel::devel)) {
+ m_alog.write(log::alevel::devel,
+ "asio con handle_async_shutdown");
+ }
+ }
+ callback(tec);
+ }
+
+ /// Cancel the underlying socket and log any errors
+ void cancel_socket_checked() {
+ lib::asio::error_code cec = socket_con_type::cancel_socket();
+ if (cec) {
+ if (cec == lib::asio::error::operation_not_supported) {
+ // cancel not supported on this OS, ignore and log at dev level
+ m_alog.write(log::alevel::devel, "socket cancel not supported");
+ } else {
+ log_err(log::elevel::warn, "socket cancel failed", cec);
+ }
+ }
+ }
+
+private:
+ /// Convenience method for logging the code and message for an error_code
+ template <typename error_type>
+ void log_err(log::level l, const char * msg, const error_type & ec) {
+ std::stringstream s;
+ s << msg << " error: " << ec << " (" << ec.message() << ")";
+ m_elog.write(l,s.str());
+ }
+
+ // static settings
+ const bool m_is_server;
+ alog_type& m_alog;
+ elog_type& m_elog;
+
+ struct proxy_data {
+ proxy_data() : timeout_proxy(config::timeout_proxy) {}
+
+ request_type req;
+ response_type res;
+ std::string write_buf;
+ lib::asio::streambuf read_buf;
+ long timeout_proxy;
+ timer_ptr timer;
+ };
+
+ std::string m_proxy;
+ lib::shared_ptr<proxy_data> m_proxy_data;
+
+ // transport resources
+ io_service_ptr m_io_service;
+ strand_ptr m_strand;
+ connection_hdl m_connection_hdl;
+
+ std::vector<lib::asio::const_buffer> m_bufs;
+
+ /// Detailed internal error code
+ lib::asio::error_code m_tec;
+
+ // Handlers
+ tcp_init_handler m_tcp_pre_init_handler;
+ tcp_init_handler m_tcp_post_init_handler;
+
+ handler_allocator m_read_handler_allocator;
+ handler_allocator m_write_handler_allocator;
+};
+
+
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
diff --git a/websocketpp/transport/asio/endpoint.hpp b/websocketpp/transport/asio/endpoint.hpp
new file mode 100644
index 00000000..46ff24c0
--- /dev/null
+++ b/websocketpp/transport/asio/endpoint.hpp
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_HPP
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/asio/connection.hpp>
+#include <websocketpp/transport/asio/security/none.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/functional.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+
+/// Asio based endpoint transport component
+/**
+ * transport::asio::endpoint implements an endpoint transport component using
+ * Asio.
+ */
+template <typename config>
+class endpoint : public config::socket_type {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint<config> type;
+
+ /// Type of the concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of the socket policy
+ typedef typename config::socket_type socket_type;
+ /// Type of the error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of the access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of the socket connection component
+ typedef typename socket_type::socket_con_type socket_con_type;
+ /// Type of a shared pointer to the socket connection component
+ typedef typename socket_con_type::ptr socket_con_ptr;
+
+ /// Type of the connection transport component associated with this
+ /// endpoint transport component
+ typedef asio::connection<config> transport_con_type;
+ /// Type of a shared pointer to the connection transport component
+ /// associated with this endpoint transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ /// Type of a pointer to the ASIO io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a shared pointer to the acceptor being used
+ typedef lib::shared_ptr<lib::asio::ip::tcp::acceptor> acceptor_ptr;
+ /// Type of a shared pointer to the resolver being used
+ typedef lib::shared_ptr<lib::asio::ip::tcp::resolver> resolver_ptr;
+ /// Type of timer handle
+ typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
+ /// Type of a shared pointer to an io_service work object
+ typedef lib::shared_ptr<lib::asio::io_service::work> work_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ : m_io_service(NULL)
+ , m_external_io_service(false)
+ , m_listen_backlog(0)
+ , m_reuse_addr(false)
+ , m_state(UNINITIALIZED)
+ {
+ //std::cout << "transport::asio::endpoint constructor" << std::endl;
+ }
+
+ ~endpoint() {
+ // clean up our io_service if we were initialized with an internal one.
+
+ // Explicitly destroy local objects
+ m_acceptor.reset();
+ m_resolver.reset();
+ m_work.reset();
+ if (m_state != UNINITIALIZED && !m_external_io_service) {
+ delete m_io_service;
+ }
+ }
+
+ /// transport::asio objects are moveable but not copyable or assignable.
+ /// The following code sets this situation up based on whether or not we
+ /// have C++11 support or not
+#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+ endpoint(const endpoint & src) = delete;
+ endpoint& operator= (const endpoint & rhs) = delete;
+#else
+private:
+ endpoint(const endpoint & src);
+ endpoint & operator= (const endpoint & rhs);
+public:
+#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
+
+#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
+ endpoint (endpoint && src)
+ : config::socket_type(std::move(src))
+ , m_tcp_pre_init_handler(src.m_tcp_pre_init_handler)
+ , m_tcp_post_init_handler(src.m_tcp_post_init_handler)
+ , m_io_service(src.m_io_service)
+ , m_external_io_service(src.m_external_io_service)
+ , m_acceptor(src.m_acceptor)
+ , m_listen_backlog(lib::asio::socket_base::max_connections)
+ , m_reuse_addr(src.m_reuse_addr)
+ , m_elog(src.m_elog)
+ , m_alog(src.m_alog)
+ , m_state(src.m_state)
+ {
+ src.m_io_service = NULL;
+ src.m_external_io_service = false;
+ src.m_acceptor = NULL;
+ src.m_state = UNINITIALIZED;
+ }
+
+ /*endpoint & operator= (const endpoint && rhs) {
+ if (this != &rhs) {
+ m_io_service = rhs.m_io_service;
+ m_external_io_service = rhs.m_external_io_service;
+ m_acceptor = rhs.m_acceptor;
+ m_listen_backlog = rhs.m_listen_backlog;
+ m_reuse_addr = rhs.m_reuse_addr;
+ m_state = rhs.m_state;
+
+ rhs.m_io_service = NULL;
+ rhs.m_external_io_service = false;
+ rhs.m_acceptor = NULL;
+ rhs.m_listen_backlog = lib::asio::socket_base::max_connections;
+ rhs.m_state = UNINITIALIZED;
+
+ // TODO: this needs to be updated
+ }
+ return *this;
+ }*/
+#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
+
+ /// Return whether or not the endpoint produces secure connections.
+ bool is_secure() const {
+ return socket_type::is_secure();
+ }
+
+ /// initialize asio transport with external io_service (exception free)
+ /**
+ * Initialize the ASIO transport policy for this endpoint using the provided
+ * io_service object. asio_init must be called exactly once on any endpoint
+ * that uses transport::asio before it can be used.
+ *
+ * @param ptr A pointer to the io_service to use for asio events
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void init_asio(io_service_ptr ptr, lib::error_code & ec) {
+ if (m_state != UNINITIALIZED) {
+ m_elog->write(log::elevel::library,
+ "asio::init_asio called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel,"asio::init_asio");
+
+ m_io_service = ptr;
+ m_external_io_service = true;
+ m_acceptor = lib::make_shared<lib::asio::ip::tcp::acceptor>(
+ lib::ref(*m_io_service));
+
+ m_state = READY;
+ ec = lib::error_code();
+ }
+
+ /// initialize asio transport with external io_service
+ /**
+ * Initialize the ASIO transport policy for this endpoint using the provided
+ * io_service object. asio_init must be called exactly once on any endpoint
+ * that uses transport::asio before it can be used.
+ *
+ * @param ptr A pointer to the io_service to use for asio events
+ */
+ void init_asio(io_service_ptr ptr) {
+ lib::error_code ec;
+ init_asio(ptr,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Initialize asio transport with internal io_service (exception free)
+ /**
+ * This method of initialization will allocate and use an internally managed
+ * io_service.
+ *
+ * @see init_asio(io_service_ptr ptr)
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void init_asio(lib::error_code & ec) {
+ // Use a smart pointer until the call is successful and ownership has
+ // successfully been taken. Use unique_ptr when available.
+ // TODO: remove the use of auto_ptr when C++98/03 support is no longer
+ // necessary.
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#else
+ lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#endif
+ init_asio(service.get(), ec);
+ if( !ec ) service.release(); // Call was successful, transfer ownership
+ m_external_io_service = false;
+ }
+
+ /// Initialize asio transport with internal io_service
+ /**
+ * This method of initialization will allocate and use an internally managed
+ * io_service.
+ *
+ * @see init_asio(io_service_ptr ptr)
+ */
+ void init_asio() {
+ // Use a smart pointer until the call is successful and ownership has
+ // successfully been taken. Use unique_ptr when available.
+ // TODO: remove the use of auto_ptr when C++98/03 support is no longer
+ // necessary.
+#ifdef _WEBSOCKETPP_CPP11_MEMORY_
+ lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#else
+ lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
+#endif
+ init_asio( service.get() );
+ // If control got this far without an exception, then ownership has successfully been taken
+ service.release();
+ m_external_io_service = false;
+ }
+
+ /// Sets the tcp pre init handler
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_pre_init_handler(tcp_init_handler h) {
+ m_tcp_pre_init_handler = h;
+ }
+
+ /// Sets the tcp pre init handler (deprecated)
+ /**
+ * The tcp pre init handler is called after the raw tcp connection has been
+ * established but before any additional wrappers (proxy connects, TLS
+ * handshakes, etc) have been performed.
+ *
+ * @deprecated Use set_tcp_pre_init_handler instead
+ *
+ * @param h The handler to call on tcp pre init.
+ */
+ void set_tcp_init_handler(tcp_init_handler h) {
+ set_tcp_pre_init_handler(h);
+ }
+
+ /// Sets the tcp post init handler
+ /**
+ * The tcp post init handler is called after the tcp connection has been
+ * established and all additional wrappers (proxy connects, TLS handshakes,
+ * etc have been performed. This is fired before any bytes are read or any
+ * WebSocket specific handshake logic has been performed.
+ *
+ * @since 0.3.0
+ *
+ * @param h The handler to call on tcp post init.
+ */
+ void set_tcp_post_init_handler(tcp_init_handler h) {
+ m_tcp_post_init_handler = h;
+ }
+
+ /// Sets the maximum length of the queue of pending connections.
+ /**
+ * Sets the maximum length of the queue of pending connections. Increasing
+ * this will allow WebSocket++ to queue additional incoming connections.
+ * Setting it higher may prevent failed connections at high connection rates
+ * but may cause additional latency.
+ *
+ * For this value to take effect you may need to adjust operating system
+ * settings.
+ *
+ * New values affect future calls to listen only.
+ *
+ * A value of zero will use the operating system default. This is the
+ * default value.
+ *
+ * @since 0.3.0
+ *
+ * @param backlog The maximum length of the queue of pending connections
+ */
+ void set_listen_backlog(int backlog) {
+ m_listen_backlog = backlog;
+ }
+
+ /// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
+ /**
+ * Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
+ * this flag does depends on your operating system. Please consult operating
+ * system documentation for more details.
+ *
+ * New values affect future calls to listen only.
+ *
+ * The default is false.
+ *
+ * @since 0.3.0
+ *
+ * @param value Whether or not to use the SO_REUSEADDR option
+ */
+ void set_reuse_addr(bool value) {
+ m_reuse_addr = value;
+ }
+
+ /// Retrieve a reference to the endpoint's io_service
+ /**
+ * The io_service may be an internal or external one. This may be used to
+ * call methods of the io_service that are not explicitly wrapped by the
+ * endpoint.
+ *
+ * This method is only valid after the endpoint has been initialized with
+ * `init_asio`. No error will be returned if it isn't.
+ *
+ * @return A reference to the endpoint's io_service
+ */
+ lib::asio::io_service & get_io_service() {
+ return *m_io_service;
+ }
+
+ /// Get local TCP endpoint
+ /**
+ * Extracts the local endpoint from the acceptor. This represents the
+ * address that WebSocket++ is listening on.
+ *
+ * Sets a bad_descriptor error if the acceptor is not currently listening
+ * or otherwise unavailable.
+ *
+ * @since 0.7.0
+ *
+ * @param ec Set to indicate what error occurred, if any.
+ * @return The local endpoint
+ */
+ lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
+ if (m_acceptor) {
+ return m_acceptor->local_endpoint(ec);
+ } else {
+ ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
+ return lib::asio::ip::tcp::endpoint();
+ }
+ }
+
+ /// Set up endpoint for listening manually (exception free)
+ /**
+ * Bind the internal acceptor using the specified settings. The endpoint
+ * must have been initialized by calling init_asio before listening.
+ *
+ * @param ep An endpoint to read settings from
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
+ {
+ if (m_state != READY) {
+ m_elog->write(log::elevel::library,
+ "asio::listen called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel,"asio::listen");
+
+ lib::asio::error_code bec;
+
+ m_acceptor->open(ep.protocol(),bec);
+ if (!bec) {
+ m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec);
+ }
+ if (!bec) {
+ m_acceptor->bind(ep,bec);
+ }
+ if (!bec) {
+ m_acceptor->listen(m_listen_backlog,bec);
+ }
+ if (bec) {
+ if (m_acceptor->is_open()) {
+ m_acceptor->close();
+ }
+ log_err(log::elevel::info,"asio listen",bec);
+ ec = make_error_code(error::pass_through);
+ } else {
+ m_state = LISTENING;
+ ec = lib::error_code();
+ }
+ }
+
+ /// Set up endpoint for listening manually
+ /**
+ * Bind the internal acceptor using the settings specified by the endpoint e
+ *
+ * @param ep An endpoint to read settings from
+ */
+ void listen(lib::asio::ip::tcp::endpoint const & ep) {
+ lib::error_code ec;
+ listen(ep,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Set up endpoint for listening with protocol and port (exception free)
+ /**
+ * Bind the internal acceptor using the given internet protocol and port.
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * Common options include:
+ * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
+ * - IPv4 only: lib::asio::ip::tcp::v4()
+ *
+ * @param internet_protocol The internet protocol to use.
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ template <typename InternetProtocol>
+ void listen(InternetProtocol const & internet_protocol, uint16_t port,
+ lib::error_code & ec)
+ {
+ lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
+ listen(ep,ec);
+ }
+
+ /// Set up endpoint for listening with protocol and port
+ /**
+ * Bind the internal acceptor using the given internet protocol and port.
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * Common options include:
+ * - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
+ * - IPv4 only: lib::asio::ip::tcp::v4()
+ *
+ * @param internet_protocol The internet protocol to use.
+ * @param port The port to listen on.
+ */
+ template <typename InternetProtocol>
+ void listen(InternetProtocol const & internet_protocol, uint16_t port)
+ {
+ lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
+ listen(ep);
+ }
+
+ /// Set up endpoint for listening on a port (exception free)
+ /**
+ * Bind the internal acceptor using the given port. The IPv6 protocol with
+ * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
+ * the overload that allows specifying the protocol explicitly.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(uint16_t port, lib::error_code & ec) {
+ listen(lib::asio::ip::tcp::v6(), port, ec);
+ }
+
+ /// Set up endpoint for listening on a port
+ /**
+ * Bind the internal acceptor using the given port. The IPv6 protocol with
+ * mapped IPv4 for dual stack hosts will be used. If you need IPv4 only use
+ * the overload that allows specifying the protocol explicitly.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param port The port to listen on.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(uint16_t port) {
+ listen(lib::asio::ip::tcp::v6(), port);
+ }
+
+ /// Set up endpoint for listening on a host and service (exception free)
+ /**
+ * Bind the internal acceptor using the given host and service. More details
+ * about what host and service can be are available in the Asio
+ * documentation for ip::basic_resolver_query::basic_resolver_query's
+ * constructors.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param host A string identifying a location. May be a descriptive name or
+ * a numeric address string.
+ * @param service A string identifying the requested service. This may be a
+ * descriptive name or a numeric string corresponding to a port number.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(std::string const & host, std::string const & service,
+ lib::error_code & ec)
+ {
+ using lib::asio::ip::tcp;
+ tcp::resolver r(*m_io_service);
+ tcp::resolver::query query(host, service);
+ tcp::resolver::iterator endpoint_iterator = r.resolve(query);
+ tcp::resolver::iterator end;
+ if (endpoint_iterator == end) {
+ m_elog->write(log::elevel::library,
+ "asio::listen could not resolve the supplied host or service");
+ ec = make_error_code(error::invalid_host_service);
+ return;
+ }
+ listen(*endpoint_iterator,ec);
+ }
+
+ /// Set up endpoint for listening on a host and service
+ /**
+ * Bind the internal acceptor using the given host and service. More details
+ * about what host and service can be are available in the Asio
+ * documentation for ip::basic_resolver_query::basic_resolver_query's
+ * constructors.
+ *
+ * The endpoint must have been initialized by calling init_asio before
+ * listening.
+ *
+ * @param host A string identifying a location. May be a descriptive name or
+ * a numeric address string.
+ * @param service A string identifying the requested service. This may be a
+ * descriptive name or a numeric string corresponding to a port number.
+ * @param ec Set to indicate what error occurred, if any.
+ */
+ void listen(std::string const & host, std::string const & service)
+ {
+ lib::error_code ec;
+ listen(host,service,ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Stop listening (exception free)
+ /**
+ * Stop listening and accepting new connections. This will not end any
+ * existing connections.
+ *
+ * @since 0.3.0-alpha4
+ * @param ec A status code indicating an error, if any.
+ */
+ void stop_listening(lib::error_code & ec) {
+ if (m_state != LISTENING) {
+ m_elog->write(log::elevel::library,
+ "asio::listen called from the wrong state");
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::invalid_state);
+ return;
+ }
+
+ m_acceptor->close();
+ m_state = READY;
+ ec = lib::error_code();
+ }
+
+ /// Stop listening
+ /**
+ * Stop listening and accepting new connections. This will not end any
+ * existing connections.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void stop_listening() {
+ lib::error_code ec;
+ stop_listening(ec);
+ if (ec) { throw exception(ec); }
+ }
+
+ /// Check if the endpoint is listening
+ /**
+ * @return Whether or not the endpoint is listening.
+ */
+ bool is_listening() const {
+ return (m_state == LISTENING);
+ }
+
+ /// wraps the run method of the internal io_service object
+ std::size_t run() {
+ return m_io_service->run();
+ }
+
+ /// wraps the run_one method of the internal io_service object
+ /**
+ * @since 0.3.0-alpha4
+ */
+ std::size_t run_one() {
+ return m_io_service->run_one();
+ }
+
+ /// wraps the stop method of the internal io_service object
+ void stop() {
+ m_io_service->stop();
+ }
+
+ /// wraps the poll method of the internal io_service object
+ std::size_t poll() {
+ return m_io_service->poll();
+ }
+
+ /// wraps the poll_one method of the internal io_service object
+ std::size_t poll_one() {
+ return m_io_service->poll_one();
+ }
+
+ /// wraps the reset method of the internal io_service object
+ void reset() {
+ m_io_service->reset();
+ }
+
+ /// wraps the stopped method of the internal io_service object
+ bool stopped() const {
+ return m_io_service->stopped();
+ }
+
+ /// Marks the endpoint as perpetual, stopping it from exiting when empty
+ /**
+ * Marks the endpoint as perpetual. Perpetual endpoints will not
+ * automatically exit when they run out of connections to process. To stop
+ * a perpetual endpoint call `end_perpetual`.
+ *
+ * An endpoint may be marked perpetual at any time by any thread. It must be
+ * called either before the endpoint has run out of work or before it was
+ * started
+ *
+ * @since 0.3.0
+ */
+ void start_perpetual() {
+ m_work = lib::make_shared<lib::asio::io_service::work>(
+ lib::ref(*m_io_service)
+ );
+ }
+
+ /// Clears the endpoint's perpetual flag, allowing it to exit when empty
+ /**
+ * Clears the endpoint's perpetual flag. This will cause the endpoint's run
+ * method to exit normally when it runs out of connections. If there are
+ * currently active connections it will not end until they are complete.
+ *
+ * @since 0.3.0
+ */
+ void stop_perpetual() {
+ m_work.reset();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Sets a timer that calls back a function after the specified period of
+ * milliseconds. Returns a handle that can be used to cancel the timer.
+ * A cancelled timer will return the error code error::operation_aborted
+ * A timer that expired will return no error.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler callback) {
+ timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
+ *m_io_service,
+ lib::asio::milliseconds(duration)
+ );
+
+ new_timer->async_wait(
+ lib::bind(
+ &type::handle_timer,
+ this,
+ new_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ return new_timer;
+ }
+
+ /// Timer handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param t Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_timer(timer_ptr, timer_handler callback,
+ lib::asio::error_code const & ec)
+ {
+ if (ec) {
+ if (ec == lib::asio::error::operation_aborted) {
+ callback(make_error_code(transport::error::operation_aborted));
+ } else {
+ m_elog->write(log::elevel::info,
+ "asio handle_timer error: "+ec.message());
+ log_err(log::elevel::info,"asio handle_timer",ec);
+ callback(make_error_code(error::pass_through));
+ }
+ } else {
+ callback(lib::error_code());
+ }
+ }
+
+ /// Accept the next connection attempt and assign it to con (exception free)
+ /**
+ * @param tcon The connection to accept into.
+ * @param callback The function to call when the operation is complete.
+ * @param ec A status code indicating an error, if any.
+ */
+ void async_accept(transport_con_ptr tcon, accept_handler callback,
+ lib::error_code & ec)
+ {
+ if (m_state != LISTENING) {
+ using websocketpp::error::make_error_code;
+ ec = make_error_code(websocketpp::error::async_accept_not_listening);
+ return;
+ }
+
+ m_alog->write(log::alevel::devel, "asio::async_accept");
+
+ if (config::enable_multithreading) {
+ m_acceptor->async_accept(
+ tcon->get_raw_socket(),
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_accept,
+ this,
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ m_acceptor->async_accept(
+ tcon->get_raw_socket(),
+ lib::bind(
+ &type::handle_accept,
+ this,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Accept the next connection attempt and assign it to con.
+ /**
+ * @param tcon The connection to accept into.
+ * @param callback The function to call when the operation is complete.
+ */
+ void async_accept(transport_con_ptr tcon, accept_handler callback) {
+ lib::error_code ec;
+ async_accept(tcon,callback,ec);
+ if (ec) { throw exception(ec); }
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ */
+ void init_logging(alog_type* a, elog_type* e) {
+ m_alog = a;
+ m_elog = e;
+ }
+
+ void handle_accept(accept_handler callback, lib::asio::error_code const &
+ asio_ec)
+ {
+ lib::error_code ret_ec;
+
+ m_alog->write(log::alevel::devel, "asio::handle_accept");
+
+ if (asio_ec) {
+ if (asio_ec == lib::asio::errc::operation_canceled) {
+ ret_ec = make_error_code(websocketpp::error::operation_canceled);
+ } else {
+ log_err(log::elevel::info,"asio handle_accept",asio_ec);
+ ret_ec = make_error_code(error::pass_through);
+ }
+ }
+
+ callback(ret_ec);
+ }
+
+ /// Initiate a new connection
+ // TODO: there have to be some more failure conditions here
+ void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
+ using namespace lib::asio::ip;
+
+ // Create a resolver
+ if (!m_resolver) {
+ m_resolver = lib::make_shared<lib::asio::ip::tcp::resolver>(
+ lib::ref(*m_io_service));
+ }
+
+ tcon->set_uri(u);
+
+ std::string proxy = tcon->get_proxy();
+ std::string host;
+ std::string port;
+
+ if (proxy.empty()) {
+ host = u->get_host();
+ port = u->get_port_str();
+ } else {
+ lib::error_code ec;
+
+ uri_ptr pu = lib::make_shared<uri>(proxy);
+
+ if (!pu->get_valid()) {
+ cb(make_error_code(error::proxy_invalid));
+ return;
+ }
+
+ ec = tcon->proxy_init(u->get_authority());
+ if (ec) {
+ cb(ec);
+ return;
+ }
+
+ host = pu->get_host();
+ port = pu->get_port_str();
+ }
+
+ tcp::resolver::query query(host,port);
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ m_alog->write(log::alevel::devel,
+ "starting async DNS resolve for "+host+":"+port);
+ }
+
+ timer_ptr dns_timer;
+
+ dns_timer = tcon->set_timer(
+ config::timeout_dns_resolve,
+ lib::bind(
+ &type::handle_resolve_timeout,
+ this,
+ dns_timer,
+ cb,
+ lib::placeholders::_1
+ )
+ );
+
+ if (config::enable_multithreading) {
+ m_resolver->async_resolve(
+ query,
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_resolve,
+ this,
+ tcon,
+ dns_timer,
+ cb,
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ ))
+ );
+ } else {
+ m_resolver->async_resolve(
+ query,
+ lib::bind(
+ &type::handle_resolve,
+ this,
+ tcon,
+ dns_timer,
+ cb,
+ lib::placeholders::_1,
+ lib::placeholders::_2
+ )
+ );
+ }
+ }
+
+ /// DNS resolution timeout handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param dns_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_resolve_timeout(timer_ptr, connect_handler callback,
+ lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog->write(log::alevel::devel,
+ "asio handle_resolve_timeout timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_resolve_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog->write(log::alevel::devel,"DNS resolution timed out");
+ m_resolver->cancel();
+ callback(ret_ec);
+ }
+
+ void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer,
+ connect_handler callback, lib::asio::error_code const & ec,
+ lib::asio::ip::tcp::resolver::iterator iterator)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(dns_timer->expires_from_now()))
+ {
+ m_alog->write(log::alevel::devel,"async_resolve cancelled");
+ return;
+ }
+
+ dns_timer->cancel();
+
+ if (ec) {
+ log_err(log::elevel::info,"asio async_resolve",ec);
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ std::stringstream s;
+ s << "Async DNS resolve successful. Results: ";
+
+ lib::asio::ip::tcp::resolver::iterator it, end;
+ for (it = iterator; it != end; ++it) {
+ s << (*it).endpoint() << " ";
+ }
+
+ m_alog->write(log::alevel::devel,s.str());
+ }
+
+ m_alog->write(log::alevel::devel,"Starting async connect");
+
+ timer_ptr con_timer;
+
+ con_timer = tcon->set_timer(
+ config::timeout_connect,
+ lib::bind(
+ &type::handle_connect_timeout,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+
+ if (config::enable_multithreading) {
+ lib::asio::async_connect(
+ tcon->get_raw_socket(),
+ iterator,
+ tcon->get_strand()->wrap(lib::bind(
+ &type::handle_connect,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ lib::asio::async_connect(
+ tcon->get_raw_socket(),
+ iterator,
+ lib::bind(
+ &type::handle_connect,
+ this,
+ tcon,
+ con_timer,
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Asio connect timeout handler
+ /**
+ * The timer pointer is included to ensure the timer isn't destroyed until
+ * after it has expired.
+ *
+ * @param tcon Pointer to the transport connection that is being connected
+ * @param con_timer Pointer to the timer in question
+ * @param callback The function to call back
+ * @param ec A status code indicating an error, if any.
+ */
+ void handle_connect_timeout(transport_con_ptr tcon, timer_ptr,
+ connect_handler callback, lib::error_code const & ec)
+ {
+ lib::error_code ret_ec;
+
+ if (ec) {
+ if (ec == transport::error::operation_aborted) {
+ m_alog->write(log::alevel::devel,
+ "asio handle_connect_timeout timer cancelled");
+ return;
+ }
+
+ log_err(log::elevel::devel,"asio handle_connect_timeout",ec);
+ ret_ec = ec;
+ } else {
+ ret_ec = make_error_code(transport::error::timeout);
+ }
+
+ m_alog->write(log::alevel::devel,"TCP connect timed out");
+ tcon->cancel_socket_checked();
+ callback(ret_ec);
+ }
+
+ void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
+ connect_handler callback, lib::asio::error_code const & ec)
+ {
+ if (ec == lib::asio::error::operation_aborted ||
+ lib::asio::is_neg(con_timer->expires_from_now()))
+ {
+ m_alog->write(log::alevel::devel,"async_connect cancelled");
+ return;
+ }
+
+ con_timer->cancel();
+
+ if (ec) {
+ log_err(log::elevel::info,"asio async_connect",ec);
+ callback(make_error_code(error::pass_through));
+ return;
+ }
+
+ if (m_alog->static_test(log::alevel::devel)) {
+ m_alog->write(log::alevel::devel,
+ "Async connect to "+tcon->get_remote_endpoint()+" successful.");
+ }
+
+ callback(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ *
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ m_alog->write(log::alevel::devel, "transport::asio::init");
+
+ // Initialize the connection socket component
+ socket_type::init(lib::static_pointer_cast<socket_con_type,
+ transport_con_type>(tcon));
+
+ lib::error_code ec;
+
+ ec = tcon->init_asio(m_io_service);
+ if (ec) {return ec;}
+
+ tcon->set_tcp_pre_init_handler(m_tcp_pre_init_handler);
+ tcon->set_tcp_post_init_handler(m_tcp_post_init_handler);
+
+ return lib::error_code();
+ }
+private:
+ /// Convenience method for logging the code and message for an error_code
+ template <typename error_type>
+ void log_err(log::level l, char const * msg, error_type const & ec) {
+ std::stringstream s;
+ s << msg << " error: " << ec << " (" << ec.message() << ")";
+ m_elog->write(l,s.str());
+ }
+
+ enum state {
+ UNINITIALIZED = 0,
+ READY = 1,
+ LISTENING = 2
+ };
+
+ // Handlers
+ tcp_init_handler m_tcp_pre_init_handler;
+ tcp_init_handler m_tcp_post_init_handler;
+
+ // Network Resources
+ io_service_ptr m_io_service;
+ bool m_external_io_service;
+ acceptor_ptr m_acceptor;
+ resolver_ptr m_resolver;
+ work_ptr m_work;
+
+ // Network constants
+ int m_listen_backlog;
+ bool m_reuse_addr;
+
+ elog_type* m_elog;
+ alog_type* m_alog;
+
+ // Transport state
+ state m_state;
+};
+
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_HPP
diff --git a/websocketpp/transport/asio/security/base.hpp b/websocketpp/transport/asio/security/base.hpp
new file mode 100644
index 00000000..0f08f404
--- /dev/null
+++ b/websocketpp/transport/asio/security/base.hpp
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <string>
+
+// Interface that sockets/security policies must implement
+
+/*
+ * Endpoint Interface
+ *
+ * bool is_secure() const;
+ * @return Whether or not the endpoint creates secure connections
+ *
+ * lib::error_code init(socket_con_ptr scon);
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ * @param scon Pointer to the socket component of the connection
+ * @return Error code (empty on success)
+ */
+
+
+// Connection
+// TODO
+// set_hostname(std::string hostname)
+// pre_init(init_handler);
+// post_init(init_handler);
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+namespace socket {
+
+typedef lib::function<void(lib::asio::error_code const &)> shutdown_handler;
+
+/**
+ * The transport::asio::socket::* classes are a set of security/socket related
+ * policies and support code for the ASIO transport types.
+ */
+
+/// Errors related to asio transport sockets
+namespace error {
+ enum value {
+ /// Catch-all error for security policy errors that don't fit in other
+ /// categories
+ security = 1,
+
+ /// Catch-all error for socket component errors that don't fit in other
+ /// categories
+ socket,
+
+ /// A function was called in a state that it was illegal to do so.
+ invalid_state,
+
+ /// The application was prompted to provide a TLS context and it was
+ /// empty or otherwise invalid
+ invalid_tls_context,
+
+ /// TLS Handshake Timeout
+ tls_handshake_timeout,
+
+ /// pass_through from underlying library
+ pass_through,
+
+ /// Required tls_init handler not present
+ missing_tls_init_handler,
+
+ /// TLS Handshake Failed
+ tls_handshake_failed,
+
+ /// Failed to set TLS SNI hostname
+ tls_failed_sni_hostname
+ };
+} // namespace error
+
+/// Error category related to asio transport socket policies
+class socket_category : public lib::error_category {
+public:
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.asio.socket";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case error::security:
+ return "Security policy error";
+ case error::socket:
+ return "Socket component error";
+ case error::invalid_state:
+ return "Invalid state";
+ case error::invalid_tls_context:
+ return "Invalid or empty TLS context supplied";
+ case error::tls_handshake_timeout:
+ return "TLS handshake timed out";
+ case error::pass_through:
+ return "Pass through from socket policy";
+ case error::missing_tls_init_handler:
+ return "Required tls_init handler not present.";
+ case error::tls_handshake_failed:
+ return "TLS handshake failed";
+ case error::tls_failed_sni_hostname:
+ return "Failed to set TLS SNI hostname";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+inline lib::error_category const & get_socket_category() {
+ static socket_category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_socket_category());
+}
+
+/// Type of asio transport socket policy initialization handlers
+typedef lib::function<void(const lib::error_code&)> init_handler;
+
+} // namespace socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
diff --git a/websocketpp/transport/asio/security/none.hpp b/websocketpp/transport/asio/security/none.hpp
new file mode 100644
index 00000000..0e68a65c
--- /dev/null
+++ b/websocketpp/transport/asio/security/none.hpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
+#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+#include <websocketpp/transport/asio/security/base.hpp>
+
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/memory.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+/// A socket policy for the asio transport that implements a plain, unencrypted
+/// socket
+namespace basic_socket {
+
+/// The signature of the socket init handler for this socket policy
+typedef lib::function<void(connection_hdl,lib::asio::ip::tcp::socket&)>
+ socket_init_handler;
+
+/// Basic Asio connection socket component
+/**
+ * transport::asio::basic_socket::connection implements a connection socket
+ * component using Asio ip::tcp::socket.
+ */
+class connection : public lib::enable_shared_from_this<connection> {
+public:
+ /// Type of this connection socket component
+ typedef connection type;
+ /// Type of a shared pointer to this connection socket component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of a pointer to the Asio io_service being used
+ typedef lib::asio::io_service* io_service_ptr;
+ /// Type of a pointer to the Asio io_service strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of the ASIO socket being used
+ typedef lib::asio::ip::tcp::socket socket_type;
+ /// Type of a shared pointer to the socket being used.
+ typedef lib::shared_ptr<socket_type> socket_ptr;
+
+ explicit connection() : m_state(UNINITIALIZED) {
+ //std::cout << "transport::asio::basic_socket::connection constructor"
+ // << std::endl;
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return shared_from_this();
+ }
+
+ /// Check whether or not this connection is secure
+ /**
+ * @return Whether or not this connection is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set the socket initialization handler
+ /**
+ * The socket initialization handler is called after the socket object is
+ * created but before it is used. This gives the application a chance to
+ * set any Asio socket options it needs.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ lib::asio::ip::tcp::socket & get_socket() {
+ return *m_socket;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally.
+ */
+ lib::asio::ip::tcp::socket & get_next_layer() {
+ return *m_socket;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ lib::asio::ip::tcp::socket & get_raw_socket() {
+ return *m_socket;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint(lib::error_code & ec) const {
+ std::stringstream s;
+
+ lib::asio::error_code aec;
+ lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec);
+
+ if (aec) {
+ ec = error::make_error_code(error::pass_through);
+ s << "Error getting remote endpoint: " << aec
+ << " (" << aec.message() << ")";
+ return s.str();
+ } else {
+ ec = lib::error_code();
+ s << ep;
+ return s.str();
+ }
+ }
+protected:
+ /// Perform one time initializations
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * @param service A pointer to the endpoint's io_service
+ * @param strand A shared pointer to the connection's asio strand
+ * @param is_server Whether or not the endpoint is a server or not.
+ */
+ lib::error_code init_asio (io_service_ptr service, strand_ptr, bool)
+ {
+ if (m_state != UNINITIALIZED) {
+ return socket::make_error_code(socket::error::invalid_state);
+ }
+
+ m_socket = lib::make_shared<lib::asio::ip::tcp::socket>(
+ lib::ref(*service));
+
+ m_state = READY;
+
+ return lib::error_code();
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the transport as a connection is being established to provide
+ * the uri being connected to to the security/socket layer.
+ *
+ * This socket policy doesn't use the uri so it is ignored.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Pre-initialize security policy
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection. This method is not allowed to
+ * write any bytes to the wire. This initialization happens before any
+ * proxies or other intermediate wrappers are negotiated.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void pre_init(init_handler callback) {
+ if (m_state != READY) {
+ callback(socket::make_error_code(socket::error::invalid_state));
+ return;
+ }
+
+ if (m_socket_init_handler) {
+ m_socket_init_handler(m_hdl,*m_socket);
+ }
+
+ m_state = READING;
+
+ callback(lib::error_code());
+ }
+
+ /// Post-initialize security policy
+ /**
+ * Called by the transport after all intermediate proxies have been
+ * negotiated. This gives the security policy the chance to talk with the
+ * real remote endpoint for a bit before the websocket handshake.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void post_init(init_handler callback) {
+ callback(lib::error_code());
+ }
+
+ /// Sets the connection handle
+ /**
+ * The connection handle is passed to any handlers to identify the
+ * connection
+ *
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_hdl = hdl;
+ }
+
+ /// Cancel all async operations on this socket
+ /**
+ * Attempts to cancel all async operations on this socket and reports any
+ * failures.
+ *
+ * NOTE: Windows XP and earlier do not support socket cancellation.
+ *
+ * @return The error that occurred, if any.
+ */
+ lib::asio::error_code cancel_socket() {
+ lib::asio::error_code ec;
+ m_socket->cancel(ec);
+ return ec;
+ }
+
+ void async_shutdown(socket::shutdown_handler h) {
+ lib::asio::error_code ec;
+ m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec);
+ h(ec);
+ }
+
+ lib::error_code get_ec() const {
+ return lib::error_code();
+ }
+
+ /// Translate any security policy specific information about an error code
+ /**
+ * Translate_ec takes an Asio error code and attempts to convert its value
+ * to an appropriate websocketpp error code. In the case that the Asio and
+ * Websocketpp error types are the same (such as using boost::asio and
+ * boost::system_error or using standalone asio and std::system_error the
+ * code will be passed through natively.
+ *
+ * In the case of a mismatch (boost::asio with std::system_error) a
+ * translated code will be returned. The plain socket policy does not have
+ * any additional information so all such errors will be reported as the
+ * generic transport pass_through error.
+ *
+ * @since 0.3.0
+ *
+ * @param ec The error code to translate_ec
+ * @return The translated error code
+ */
+ template <typename ErrorCodeType>
+ lib::error_code translate_ec(ErrorCodeType) {
+ // We don't know any more information about this error so pass through
+ return make_error_code(transport::error::pass_through);
+ }
+
+ /// Overload of translate_ec to catch cases where lib::error_code is the
+ /// same type as lib::asio::error_code
+ lib::error_code translate_ec(lib::error_code ec) {
+ // We don't know any more information about this error, but the error is
+ // the same type as the one we are translating to, so pass through
+ // untranslated.
+ return ec;
+ }
+private:
+ enum state {
+ UNINITIALIZED = 0,
+ READY = 1,
+ READING = 2
+ };
+
+ socket_ptr m_socket;
+ state m_state;
+
+ connection_hdl m_hdl;
+ socket_init_handler m_socket_init_handler;
+};
+
+/// Basic ASIO endpoint socket component
+/**
+ * transport::asio::basic_socket::endpoint implements an endpoint socket
+ * component that uses Boost ASIO's ip::tcp::socket.
+ */
+class endpoint {
+public:
+ /// The type of this endpoint socket component
+ typedef endpoint type;
+
+ /// The type of the corresponding connection socket component
+ typedef connection socket_con_type;
+ /// The type of a shared pointer to the corresponding connection socket
+ /// component.
+ typedef socket_con_type::ptr socket_con_ptr;
+
+ explicit endpoint() {}
+
+ /// Checks whether the endpoint creates secure connections
+ /**
+ * @return Whether or not the endpoint creates secure connections
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set socket init handler
+ /**
+ * The socket init handler is called after a connection's socket is created
+ * but before it is used. This gives the end application an opportunity to
+ * set asio socket specific parameters.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+protected:
+ /// Initialize a connection
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ *
+ * @param scon Pointer to the socket component of the connection
+ *
+ * @return Error code (empty on success)
+ */
+ lib::error_code init(socket_con_ptr scon) {
+ scon->set_socket_init_handler(m_socket_init_handler);
+ return lib::error_code();
+ }
+private:
+ socket_init_handler m_socket_init_handler;
+};
+
+} // namespace basic_socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
diff --git a/websocketpp/transport/asio/security/tls.hpp b/websocketpp/transport/asio/security/tls.hpp
new file mode 100644
index 00000000..7b32db81
--- /dev/null
+++ b/websocketpp/transport/asio/security/tls.hpp
@@ -0,0 +1,484 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
+#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
+
+#include <websocketpp/transport/asio/security/base.hpp>
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/common/asio_ssl.hpp>
+#include <websocketpp/common/asio.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/memory.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+namespace asio {
+/// A socket policy for the asio transport that implements a TLS encrypted
+/// socket by wrapping with an asio::ssl::stream
+namespace tls_socket {
+
+/// The signature of the socket_init_handler for this socket policy
+typedef lib::function<void(connection_hdl,lib::asio::ssl::stream<
+ lib::asio::ip::tcp::socket>&)> socket_init_handler;
+/// The signature of the tls_init_handler for this socket policy
+typedef lib::function<lib::shared_ptr<lib::asio::ssl::context>(connection_hdl)>
+ tls_init_handler;
+
+/// TLS enabled Asio connection socket component
+/**
+ * transport::asio::tls_socket::connection implements a secure connection socket
+ * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
+ */
+class connection : public lib::enable_shared_from_this<connection> {
+public:
+ /// Type of this connection socket component
+ typedef connection type;
+ /// Type of a shared pointer to this connection socket component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of the ASIO socket being used
+ typedef lib::asio::ssl::stream<lib::asio::ip::tcp::socket> socket_type;
+ /// Type of a shared pointer to the ASIO socket being used
+ typedef lib::shared_ptr<socket_type> socket_ptr;
+ /// Type of a pointer to the ASIO io_service being used
+ typedef lib::asio::io_service * io_service_ptr;
+ /// Type of a pointer to the ASIO io_service strand being used
+ typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
+ /// Type of a shared pointer to the ASIO TLS context being used
+ typedef lib::shared_ptr<lib::asio::ssl::context> context_ptr;
+
+ explicit connection() {
+ //std::cout << "transport::asio::tls_socket::connection constructor"
+ // << std::endl;
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return shared_from_this();
+ }
+
+ /// Check whether or not this connection is secure
+ /**
+ * @return Whether or not this connection is secure
+ */
+ bool is_secure() const {
+ return true;
+ }
+
+ /// Retrieve a pointer to the underlying socket
+ /**
+ * This is used internally. It can also be used to set socket options, etc
+ */
+ socket_type::lowest_layer_type & get_raw_socket() {
+ return m_socket->lowest_layer();
+ }
+
+ /// Retrieve a pointer to the layer below the ssl stream
+ /**
+ * This is used internally.
+ */
+ socket_type::next_layer_type & get_next_layer() {
+ return m_socket->next_layer();
+ }
+
+ /// Retrieve a pointer to the wrapped socket
+ /**
+ * This is used internally.
+ */
+ socket_type & get_socket() {
+ return *m_socket;
+ }
+
+ /// Set the socket initialization handler
+ /**
+ * The socket initialization handler is called after the socket object is
+ * created but before it is used. This gives the application a chance to
+ * set any ASIO socket options it needs.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Set TLS init handler
+ /**
+ * The tls init handler is called when needed to request a TLS context for
+ * the library to use. A TLS init handler must be set and it must return a
+ * valid TLS context in order for this endpoint to be able to initialize
+ * TLS connections
+ *
+ * @param h The new tls_init_handler
+ */
+ void set_tls_init_handler(tls_init_handler h) {
+ m_tls_init_handler = h;
+ }
+
+ /// Get the remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". To indicate
+ * this.
+ *
+ * TODO: allow user settable remote endpoint addresses if this seems useful
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint(lib::error_code & ec) const {
+ std::stringstream s;
+
+ lib::asio::error_code aec;
+ lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec);
+
+ if (aec) {
+ ec = error::make_error_code(error::pass_through);
+ s << "Error getting remote endpoint: " << aec
+ << " (" << aec.message() << ")";
+ return s.str();
+ } else {
+ ec = lib::error_code();
+ s << ep;
+ return s.str();
+ }
+ }
+protected:
+ /// Perform one time initializations
+ /**
+ * init_asio is called once immediately after construction to initialize
+ * Asio components to the io_service
+ *
+ * @param service A pointer to the endpoint's io_service
+ * @param strand A pointer to the connection's strand
+ * @param is_server Whether or not the endpoint is a server or not.
+ */
+ lib::error_code init_asio (io_service_ptr service, strand_ptr strand,
+ bool is_server)
+ {
+ if (!m_tls_init_handler) {
+ return socket::make_error_code(socket::error::missing_tls_init_handler);
+ }
+ m_context = m_tls_init_handler(m_hdl);
+
+ if (!m_context) {
+ return socket::make_error_code(socket::error::invalid_tls_context);
+ }
+ m_socket = lib::make_shared<socket_type>(
+ _WEBSOCKETPP_REF(*service),lib::ref(*m_context));
+
+ m_io_service = service;
+ m_strand = strand;
+ m_is_server = is_server;
+
+ return lib::error_code();
+ }
+
+ /// Set hostname hook
+ /**
+ * Called by the transport as a connection is being established to provide
+ * the hostname being connected to to the security/socket layer.
+ *
+ * This socket policy uses the hostname to set the appropriate TLS SNI
+ * header.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr u) {
+ m_uri = u;
+ }
+
+ /// Pre-initialize security policy
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection. This method is not allowed to
+ * write any bytes to the wire. This initialization happens before any
+ * proxies or other intermediate wrappers are negotiated.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void pre_init(init_handler callback) {
+ // TODO: is this the best way to check whether this function is
+ // available in the version of OpenSSL being used?
+ // TODO: consider case where host is an IP address
+#if OPENSSL_VERSION_NUMBER >= 0x90812f
+ if (!m_is_server) {
+ // For clients on systems with a suitable OpenSSL version, set the
+ // TLS SNI hostname header so connecting to TLS servers using SNI
+ // will work.
+ long res = SSL_set_tlsext_host_name(
+ get_socket().native_handle(), m_uri->get_host().c_str());
+ if (!(1 == res)) {
+ callback(socket::make_error_code(socket::error::tls_failed_sni_hostname));
+ }
+ }
+#endif
+
+ if (m_socket_init_handler) {
+ m_socket_init_handler(m_hdl,get_socket());
+ }
+
+ callback(lib::error_code());
+ }
+
+ /// Post-initialize security policy
+ /**
+ * Called by the transport after all intermediate proxies have been
+ * negotiated. This gives the security policy the chance to talk with the
+ * real remote endpoint for a bit before the websocket handshake.
+ *
+ * @param callback Handler to call back with completion information
+ */
+ void post_init(init_handler callback) {
+ m_ec = socket::make_error_code(socket::error::tls_handshake_timeout);
+
+ // TLS handshake
+ if (m_strand) {
+ m_socket->async_handshake(
+ get_handshake_type(),
+ m_strand->wrap(lib::bind(
+ &type::handle_init, get_shared(),
+ callback,
+ lib::placeholders::_1
+ ))
+ );
+ } else {
+ m_socket->async_handshake(
+ get_handshake_type(),
+ lib::bind(
+ &type::handle_init, get_shared(),
+ callback,
+ lib::placeholders::_1
+ )
+ );
+ }
+ }
+
+ /// Sets the connection handle
+ /**
+ * The connection handle is passed to any handlers to identify the
+ * connection
+ *
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_hdl = hdl;
+ }
+
+ void handle_init(init_handler callback,lib::asio::error_code const & ec) {
+ if (ec) {
+ m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
+ } else {
+ m_ec = lib::error_code();
+ }
+
+ callback(m_ec);
+ }
+
+ lib::error_code get_ec() const {
+ return m_ec;
+ }
+
+ /// Cancel all async operations on this socket
+ /**
+ * Attempts to cancel all async operations on this socket and reports any
+ * failures.
+ *
+ * NOTE: Windows XP and earlier do not support socket cancellation.
+ *
+ * @return The error that occurred, if any.
+ */
+ lib::asio::error_code cancel_socket() {
+ lib::asio::error_code ec;
+ get_raw_socket().cancel(ec);
+ return ec;
+ }
+
+ void async_shutdown(socket::shutdown_handler callback) {
+ if (m_strand) {
+ m_socket->async_shutdown(m_strand->wrap(callback));
+ } else {
+ m_socket->async_shutdown(callback);
+ }
+ }
+
+ /// Translate any security policy specific information about an error code
+ /**
+ * Translate_ec takes an Asio error code and attempts to convert its value
+ * to an appropriate websocketpp error code. In the case that the Asio and
+ * Websocketpp error types are the same (such as using boost::asio and
+ * boost::system_error or using standalone asio and std::system_error the
+ * code will be passed through natively.
+ *
+ * In the case of a mismatch (boost::asio with std::system_error) a
+ * translated code will be returned. Any error that is determined to be
+ * related to TLS but does not have a more specific websocketpp error code
+ * is returned under the catch all error `tls_error`. Non-TLS related errors
+ * are returned as the transport generic error `pass_through`
+ *
+ * @since 0.3.0
+ *
+ * @param ec The error code to translate_ec
+ * @return The translated error code
+ */
+ template <typename ErrorCodeType>
+ lib::error_code translate_ec(ErrorCodeType ec) {
+ if (ec.category() == lib::asio::error::get_ssl_category()) {
+ if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
+ return make_error_code(transport::error::tls_short_read);
+ } else {
+ // We know it is a TLS related error, but otherwise don't know
+ // more. Pass through as TLS generic.
+ return make_error_code(transport::error::tls_error);
+ }
+ } else {
+ // We don't know any more information about this error so pass
+ // through
+ return make_error_code(transport::error::pass_through);
+ }
+ }
+
+ /// Overload of translate_ec to catch cases where lib::error_code is the
+ /// same type as lib::asio::error_code
+ lib::error_code translate_ec(lib::error_code ec) {
+ // Normalize the tls_short_read error as it is used by the library and
+ // needs a consistent value. All other errors pass through natively.
+ // TODO: how to get the SSL category from std::error?
+ /*if (ec.category() == lib::asio::error::get_ssl_category()) {
+ if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
+ return make_error_code(transport::error::tls_short_read);
+ }
+ }*/
+ return ec;
+ }
+private:
+ socket_type::handshake_type get_handshake_type() {
+ if (m_is_server) {
+ return lib::asio::ssl::stream_base::server;
+ } else {
+ return lib::asio::ssl::stream_base::client;
+ }
+ }
+
+ io_service_ptr m_io_service;
+ strand_ptr m_strand;
+ context_ptr m_context;
+ socket_ptr m_socket;
+ uri_ptr m_uri;
+ bool m_is_server;
+
+ lib::error_code m_ec;
+
+ connection_hdl m_hdl;
+ socket_init_handler m_socket_init_handler;
+ tls_init_handler m_tls_init_handler;
+};
+
+/// TLS enabled Asio endpoint socket component
+/**
+ * transport::asio::tls_socket::endpoint implements a secure endpoint socket
+ * component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
+ */
+class endpoint {
+public:
+ /// The type of this endpoint socket component
+ typedef endpoint type;
+
+ /// The type of the corresponding connection socket component
+ typedef connection socket_con_type;
+ /// The type of a shared pointer to the corresponding connection socket
+ /// component.
+ typedef socket_con_type::ptr socket_con_ptr;
+
+ explicit endpoint() {}
+
+ /// Checks whether the endpoint creates secure connections
+ /**
+ * @return Whether or not the endpoint creates secure connections
+ */
+ bool is_secure() const {
+ return true;
+ }
+
+ /// Set socket init handler
+ /**
+ * The socket init handler is called after a connection's socket is created
+ * but before it is used. This gives the end application an opportunity to
+ * set asio socket specific parameters.
+ *
+ * @param h The new socket_init_handler
+ */
+ void set_socket_init_handler(socket_init_handler h) {
+ m_socket_init_handler = h;
+ }
+
+ /// Set TLS init handler
+ /**
+ * The tls init handler is called when needed to request a TLS context for
+ * the library to use. A TLS init handler must be set and it must return a
+ * valid TLS context in order for this endpoint to be able to initialize
+ * TLS connections
+ *
+ * @param h The new tls_init_handler
+ */
+ void set_tls_init_handler(tls_init_handler h) {
+ m_tls_init_handler = h;
+ }
+protected:
+ /// Initialize a connection
+ /**
+ * Called by the transport after a new connection is created to initialize
+ * the socket component of the connection.
+ *
+ * @param scon Pointer to the socket component of the connection
+ *
+ * @return Error code (empty on success)
+ */
+ lib::error_code init(socket_con_ptr scon) {
+ scon->set_socket_init_handler(m_socket_init_handler);
+ scon->set_tls_init_handler(m_tls_init_handler);
+ return lib::error_code();
+ }
+
+private:
+ socket_init_handler m_socket_init_handler;
+ tls_init_handler m_tls_init_handler;
+};
+
+} // namespace tls_socket
+} // namespace asio
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp
new file mode 100644
index 00000000..f76d4091
--- /dev/null
+++ b/websocketpp/transport/base/connection.hpp
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_BASE_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_BASE_CON_HPP
+
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+#include <string>
+
+namespace websocketpp {
+/// Transport policies provide network connectivity and timers
+/**
+ * ### Connection Interface
+ *
+ * Transport connection components needs to provide:
+ *
+ * **init**\n
+ * `void init(init_handler handler)`\n
+ * Called once shortly after construction to give the policy the chance to
+ * perform one time initialization. When complete, the policy must call the
+ * supplied `init_handler` to continue setup. The handler takes one argument
+ * with the error code if any. If an error is returned here setup will fail and
+ * the connection will be aborted or terminated.
+ *
+ * WebSocket++ will call init only once. The transport must call `handler`
+ * exactly once.
+ *
+ * **async_read_at_least**\n
+ * `void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ * read_handler handler)`\n
+ * start an async read for at least num_bytes and at most len
+ * bytes into buf. Call handler when done with number of bytes read.
+ *
+ * WebSocket++ promises to have only one async_read_at_least in flight at a
+ * time. The transport must promise to only call read_handler once per async
+ * read.
+ *
+ * **async_write**\n
+ * `void async_write(const char* buf, size_t len, write_handler handler)`\n
+ * `void async_write(std::vector<buffer> & bufs, write_handler handler)`\n
+ * Start a write of all of the data in buf or bufs. In second case data is
+ * written sequentially and in place without copying anything to a temporary
+ * location.
+ *
+ * Websocket++ promises to have only one async_write in flight at a time.
+ * The transport must promise to only call the write_handler once per async
+ * write
+ *
+ * **set_handle**\n
+ * `void set_handle(connection_hdl hdl)`\n
+ * Called by WebSocket++ to let this policy know the hdl to the connection. It
+ * may be stored for later use or ignored/discarded. This handle should be used
+ * if the policy adds any connection handlers. Connection handlers must be
+ * called with the handle as the first argument so that the handler code knows
+ * which connection generated the callback.
+ *
+ * **set_timer**\n
+ * `timer_ptr set_timer(long duration, timer_handler handler)`\n
+ * WebSocket++ uses the timers provided by the transport policy as the
+ * implementation of timers is often highly coupled with the implementation of
+ * the networking event loops.
+ *
+ * Transport timer support is an optional feature. A transport method may elect
+ * to implement a dummy timer object and have this method return an empty
+ * pointer. If so, all timer related features of WebSocket++ core will be
+ * disabled. This includes many security features designed to prevent denial of
+ * service attacks. Use timer-free transport policies with caution.
+ *
+ * **get_remote_endpoint**\n
+ * `std::string get_remote_endpoint()`\n
+ * retrieve address of remote endpoint
+ *
+ * **is_secure**\n
+ * `void is_secure()`\n
+ * whether or not the connection to the remote endpoint is secure
+ *
+ * **dispatch**\n
+ * `lib::error_code dispatch(dispatch_handler handler)`: invoke handler within
+ * the transport's event system if it uses one. Otherwise, this method should
+ * simply call `handler` immediately.
+ *
+ * **async_shutdown**\n
+ * `void async_shutdown(shutdown_handler handler)`\n
+ * Perform any cleanup necessary (if any). Call `handler` when complete.
+ */
+namespace transport {
+
+/// The type and signature of the callback passed to the init hook
+typedef lib::function<void(lib::error_code const &)> init_handler;
+
+/// The type and signature of the callback passed to the read method
+typedef lib::function<void(lib::error_code const &,size_t)> read_handler;
+
+/// The type and signature of the callback passed to the write method
+typedef lib::function<void(lib::error_code const &)> write_handler;
+
+/// The type and signature of the callback passed to the read method
+typedef lib::function<void(lib::error_code const &)> timer_handler;
+
+/// The type and signature of the callback passed to the shutdown method
+typedef lib::function<void(lib::error_code const &)> shutdown_handler;
+
+/// The type and signature of the callback passed to the interrupt method
+typedef lib::function<void()> interrupt_handler;
+
+/// The type and signature of the callback passed to the dispatch method
+typedef lib::function<void()> dispatch_handler;
+
+/// A simple utility buffer class
+struct buffer {
+ buffer(char const * b, size_t l) : buf(b),len(l) {}
+
+ char const * buf;
+ size_t len;
+};
+
+/// Generic transport related errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// underlying transport pass through
+ pass_through,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// async_read called while another async_read was in progress
+ double_read,
+
+ /// Operation aborted
+ operation_aborted,
+
+ /// Operation not supported
+ operation_not_supported,
+
+ /// End of file
+ eof,
+
+ /// TLS short read
+ tls_short_read,
+
+ /// Timer expired
+ timeout,
+
+ /// read or write after shutdown
+ action_after_shutdown,
+
+ /// Other TLS error
+ tls_error
+};
+
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic transport policy error";
+ case pass_through:
+ return "Underlying Transport Error";
+ case invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case operation_aborted:
+ return "The operation was aborted";
+ case operation_not_supported:
+ return "The operation is not supported by this transport";
+ case eof:
+ return "End of File";
+ case tls_short_read:
+ return "TLS Short Read";
+ case timeout:
+ return "Timer Expired";
+ case action_after_shutdown:
+ return "A transport action was requested after shutdown";
+ case tls_error:
+ return "Generic TLS related error";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_BASE_CON_HPP
diff --git a/websocketpp/transport/base/endpoint.hpp b/websocketpp/transport/base/endpoint.hpp
new file mode 100644
index 00000000..3b4b0d6d
--- /dev/null
+++ b/websocketpp/transport/base/endpoint.hpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_BASE_HPP
+
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/system_error.hpp>
+
+namespace websocketpp {
+/// Transport policies provide network connectivity and timers
+/**
+ * ### Endpoint Interface
+ *
+ * Transport endpoint components needs to provide:
+ *
+ * **init**\n
+ * `lib::error_code init(transport_con_ptr tcon)`\n
+ * init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * **is_secure**\n
+ * `bool is_secure() const`\n
+ * Test whether the transport component of this endpoint is capable of secure
+ * connections.
+ *
+ * **async_connect**\n
+ * `void async_connect(transport_con_ptr tcon, uri_ptr location,
+ * connect_handler handler)`\n
+ * Initiate a connection to `location` using the given connection `tcon`. `tcon`
+ * is a pointer to the transport connection component of the connection. When
+ * complete, `handler` should be called with the the connection's
+ * `connection_hdl` and any error that occurred.
+ *
+ * **init_logging**
+ * `void init_logging(alog_type * a, elog_type * e)`\n
+ * Called once after construction to provide pointers to the endpoint's access
+ * and error loggers. These may be stored and used to log messages or ignored.
+ */
+namespace transport {
+
+/// The type and signature of the callback passed to the accept method
+typedef lib::function<void(lib::error_code const &)> accept_handler;
+
+/// The type and signature of the callback passed to the connect method
+typedef lib::function<void(lib::error_code const &)> connect_handler;
+
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_BASE_HPP
diff --git a/websocketpp/transport/debug/base.hpp b/websocketpp/transport/debug/base.hpp
new file mode 100644
index 00000000..2e477b50
--- /dev/null
+++ b/websocketpp/transport/debug/base.hpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Debug transport policy that is used for various mocking and stubbing duties
+/// in unit tests.
+namespace debug {
+
+/// debug transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// not implemented
+ not_implemented,
+
+ invalid_num_bytes,
+
+ double_read
+};
+
+/// debug transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.debug";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic stub transport policy error";
+ case not_implemented:
+ return "feature not implemented";
+ case invalid_num_bytes:
+ return "Invalid number of bytes";
+ case double_read:
+ return "Read while another read was outstanding";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the debug transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the debug transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::debug::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
diff --git a/websocketpp/transport/debug/connection.hpp b/websocketpp/transport/debug/connection.hpp
new file mode 100644
index 00000000..36b282a2
--- /dev/null
+++ b/websocketpp/transport/debug/connection.hpp
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
+
+#include <websocketpp/transport/debug/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace debug {
+
+/// Empty timer class to stub out for timer functionality that stub
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_reading(false), m_is_server(is_server), m_alog(alog), m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"debug con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * Todo: docs
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * Implementation is optional and can be ignored if the transport has no
+ * need for this information.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set a default is returned.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string) {}
+
+ /// Get human readable remote endpoint address
+ /**
+ * TODO: docs
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return "unknown (debug transport)";
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return connection_hdl();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long, timer_handler handler) {
+ m_alog.write(log::alevel::devel,"debug connection set timer");
+ m_timer_handler = handler;
+ return timer_ptr();
+ }
+
+ /// Manual input supply (read all)
+ /**
+ * Similar to read_some, but continues to read until all bytes in the
+ * supplied buffer have been read or the connection runs out of read
+ * requests.
+ *
+ * This method still may not read all of the bytes in the input buffer. if
+ * it doesn't it indicates that the connection was most likely closed or
+ * is in an error state where it is no longer accepting new input.
+ *
+ * @since 0.3.0
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_all(char const * buf, size_t len) {
+ size_t total_read = 0;
+ size_t temp_read = 0;
+
+ do {
+ temp_read = this->read_some_impl(buf+total_read,len-total_read);
+ total_read += temp_read;
+ } while (temp_read != 0 && total_read < len);
+
+ return total_read;
+ }
+
+ // debug stuff to invoke the async handlers
+ void expire_timer(lib::error_code const & ec) {
+ m_timer_handler(ec);
+ }
+
+ void fullfil_write() {
+ m_write_handler(lib::error_code());
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"debug connection init");
+ handler(lib::error_code());
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char * buf, size_t len,
+ read_handler handler)
+ {
+ std::stringstream s;
+ s << "debug_con async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+
+ if (num_bytes > len) {
+ handler(make_error_code(error::invalid_num_bytes),size_t(0));
+ return;
+ }
+
+ if (m_reading == true) {
+ handler(make_error_code(error::double_read),size_t(0));
+ return;
+ }
+
+ if (num_bytes == 0 || len == 0) {
+ handler(lib::error_code(),size_t(0));
+ return;
+ }
+
+ m_buf = buf;
+ m_len = len;
+ m_bytes_needed = num_bytes;
+ m_read_handler = handler;
+ m_cursor = 0;
+ m_reading = true;
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const *, size_t, write_handler handler) {
+ m_alog.write(log::alevel::devel,"debug_con async_write");
+ m_write_handler = handler;
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const &, write_handler handler) {
+ m_alog.write(log::alevel::devel,"debug_con async_write buffer list");
+ m_write_handler = handler;
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl) {}
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * @param h The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(shutdown_handler handler) {
+ handler(lib::error_code());
+ }
+
+ size_t read_some_impl(char const * buf, size_t len) {
+ m_alog.write(log::alevel::devel,"debug_con read_some");
+
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ return 0;
+ }
+
+ size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
+
+ std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
+
+ m_cursor += bytes_to_copy;
+
+ if (m_cursor >= m_bytes_needed) {
+ complete_read(lib::error_code());
+ }
+
+ return bytes_to_copy;
+ }
+
+ /// Signal that a requested read is complete
+ /**
+ * Sets the reading flag to false and returns the handler that should be
+ * called back with the result of the read. The cursor position that is sent
+ * is whatever the value of m_cursor is.
+ *
+ * It MUST NOT be called when m_reading is false.
+ * it MUST be called while holding the read lock
+ *
+ * It is important to use this method rather than directly setting/calling
+ * m_read_handler back because this function makes sure to delete the
+ * locally stored handler which contains shared pointers that will otherwise
+ * cause circular reference based memory leaks.
+ *
+ * @param ec The error code to forward to the read handler
+ */
+ void complete_read(lib::error_code const & ec) {
+ m_reading = false;
+
+ read_handler handler = m_read_handler;
+ m_read_handler = read_handler();
+
+ handler(ec,m_cursor);
+ }
+private:
+ timer_handler m_timer_handler;
+
+ // Read space (Protected by m_read_mutex)
+ char * m_buf;
+ size_t m_len;
+ size_t m_bytes_needed;
+ read_handler m_read_handler;
+ size_t m_cursor;
+
+ // transport resources
+ connection_hdl m_connection_hdl;
+ write_handler m_write_handler;
+ shutdown_handler m_shutdown_handler;
+
+ bool m_reading;
+ bool const m_is_server;
+ bool m_is_secure;
+ alog_type & m_alog;
+ elog_type & m_elog;
+ std::string m_remote_endpoint;
+};
+
+
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
diff --git a/websocketpp/transport/debug/endpoint.hpp b/websocketpp/transport/debug/endpoint.hpp
new file mode 100644
index 00000000..1cca70c5
--- /dev/null
+++ b/websocketpp/transport/debug/endpoint.hpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_HPP
+#define WEBSOCKETPP_TRANSPORT_DEBUG_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/debug/connection.hpp>
+
+namespace websocketpp {
+namespace transport {
+namespace debug {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef debug::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * TODO: docs
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type *, elog_type *) {}
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
+ cb(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr) {
+ return lib::error_code();
+ }
+private:
+
+};
+
+} // namespace debug
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_DEBUG_HPP
diff --git a/websocketpp/transport/iostream/base.hpp b/websocketpp/transport/iostream/base.hpp
new file mode 100644
index 00000000..f8783987
--- /dev/null
+++ b/websocketpp/transport/iostream/base.hpp
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+#include <websocketpp/common/functional.hpp>
+#include <websocketpp/common/connection_hdl.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+/// Transport policy that uses STL iostream for I/O and does not support timers
+namespace iostream {
+
+/// The type and signature of the callback used by iostream transport to write
+typedef lib::function<lib::error_code(connection_hdl, char const *, size_t)>
+ write_handler;
+
+/// The type and signature of the callback used by iostream transport to perform
+/// vectored writes.
+/**
+ * If a vectored write handler is not set the standard write handler will be
+ * called multiple times.
+ */
+typedef lib::function<lib::error_code(connection_hdl, std::vector<transport::buffer> const
+ & bufs)> vector_write_handler;
+
+/// The type and signature of the callback used by iostream transport to signal
+/// a transport shutdown.
+typedef lib::function<lib::error_code(connection_hdl)> shutdown_handler;
+
+/// iostream transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// async_read_at_least call requested more bytes than buffer can store
+ invalid_num_bytes,
+
+ /// async_read called while another async_read was in progress
+ double_read,
+
+ /// An operation that requires an output stream was attempted before
+ /// setting one.
+ output_stream_required,
+
+ /// stream error
+ bad_stream
+};
+
+/// iostream transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.iostream";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic iostream transport policy error";
+ case invalid_num_bytes:
+ return "async_read_at_least call requested more bytes than buffer can store";
+ case double_read:
+ return "Async read already in progress";
+ case output_stream_required:
+ return "An output stream to be set before async_write can be used";
+ case bad_stream:
+ return "A stream operation returned ios::bad";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the iostream transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the iostream transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::iostream::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_BASE_HPP
diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp
new file mode 100644
index 00000000..81c4f411
--- /dev/null
+++ b/websocketpp/transport/iostream/connection.hpp
@@ -0,0 +1,714 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
+
+#include <websocketpp/transport/iostream/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace iostream {
+
+/// Empty timer class to stub out for timer functionality that iostream
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_output_stream(NULL)
+ , m_reading(false)
+ , m_is_server(is_server)
+ , m_is_secure(false)
+ , m_alog(alog)
+ , m_elog(elog)
+ , m_remote_endpoint("iostream transport")
+ {
+ m_alog.write(log::alevel::devel,"iostream con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Register a std::ostream with the transport for writing output
+ /**
+ * Register a std::ostream with the transport. All future writes will be
+ * done to this output stream.
+ *
+ * @param o A pointer to the ostream to use for output.
+ */
+ void register_ostream(std::ostream * o) {
+ // TODO: lock transport state?
+ scoped_lock_type lock(m_read_mutex);
+ m_output_stream = o;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * This transport policy doesn't use the uri so it is ignored.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Overloaded stream input operator
+ /**
+ * Attempts to read input from the given stream into the transport. Bytes
+ * will be extracted from the input stream to fulfill any pending reads.
+ * Input in this manner will only read until the current read buffer has
+ * been filled. Then it will signal the library to process the input. If the
+ * library's input handler adds a new async_read, additional bytes will be
+ * read, otherwise the input operation will end.
+ *
+ * When this function returns one of the following conditions is true:
+ * - There is no outstanding read operation
+ * - There are no more bytes available in the input stream
+ *
+ * You can use tellg() on the input stream to determine if all of the input
+ * bytes were read or not.
+ *
+ * If there is no pending read operation when the input method is called, it
+ * will return immediately and tellg() will not have changed.
+ */
+ friend std::istream & operator>> (std::istream & in, type & t) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(t.m_read_mutex);
+
+ t.read(in);
+
+ return in;
+ }
+
+ /// Manual input supply (read some)
+ /**
+ * Copies bytes from buf into WebSocket++'s input buffers. Bytes will be
+ * copied from the supplied buffer to fulfill any pending library reads. It
+ * will return the number of bytes successfully processed. If there are no
+ * pending reads read_some will return immediately. Not all of the bytes may
+ * be able to be read in one call.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_some(char const * buf, size_t len) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ return this->read_some_impl(buf,len);
+ }
+
+ /// Manual input supply (read all)
+ /**
+ * Similar to read_some, but continues to read until all bytes in the
+ * supplied buffer have been read or the connection runs out of read
+ * requests.
+ *
+ * This method still may not read all of the bytes in the input buffer. if
+ * it doesn't it indicates that the connection was most likely closed or
+ * is in an error state where it is no longer accepting new input.
+ *
+ * @since 0.3.0
+ *
+ * @param buf Char buffer to read into the websocket
+ * @param len Length of buf
+ * @return The number of characters from buf actually read.
+ */
+ size_t read_all(char const * buf, size_t len) {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ size_t total_read = 0;
+ size_t temp_read = 0;
+
+ do {
+ temp_read = this->read_some_impl(buf+total_read,len-total_read);
+ total_read += temp_read;
+ } while (temp_read != 0 && total_read < len);
+
+ return total_read;
+ }
+
+ /// Manual input supply (DEPRECATED)
+ /**
+ * @deprecated DEPRECATED in favor of read_some()
+ * @see read_some()
+ */
+ size_t readsome(char const * buf, size_t len) {
+ return this->read_some(buf,len);
+ }
+
+ /// Signal EOF
+ /**
+ * Signals to the transport that data stream being read has reached EOF and
+ * that no more bytes may be read or written to/from the transport.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void eof() {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ if (m_reading) {
+ complete_read(make_error_code(transport::error::eof));
+ }
+ }
+
+ /// Signal transport error
+ /**
+ * Signals to the transport that a fatal data stream error has occurred and
+ * that no more bytes may be read or written to/from the transport.
+ *
+ * @since 0.3.0-alpha4
+ */
+ void fatal_error() {
+ // this serializes calls to external read.
+ scoped_lock_type lock(m_read_mutex);
+
+ if (m_reading) {
+ complete_read(make_error_code(transport::error::pass_through));
+ }
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * The iostream transport does not provide any security features. As such
+ * it defaults to returning false when `is_secure` is called. However, the
+ * iostream transport may be used to wrap an external socket API that may
+ * provide secure transport. This method allows that external API to flag
+ * whether or not this connection is secure so that users of the WebSocket++
+ * API will get more accurate information.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool value) {
+ m_is_secure = value;
+ }
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * iostream transport will return false always because it has no information
+ * about the ultimate remote endpoint. This may or may not be accurate
+ * depending on the real source of bytes being input. The `set_secure`
+ * method may be used to flag connections that are secured by an external
+ * API
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return m_is_secure;
+ }
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set the default is "iostream transport".
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string value) {
+ m_remote_endpoint = value;
+ }
+
+ /// Get human readable remote endpoint address
+ /**
+ * The iostream transport has no information about the ultimate remote
+ * endpoint. It will return the string "iostream transport". The
+ * `set_remote_endpoint` method may be used by external network code to set
+ * a more accurate value.
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return m_remote_endpoint;
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return m_connection_hdl;
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long, timer_handler) {
+ return timer_ptr();
+ }
+
+ /// Sets the write handler
+ /**
+ * The write handler is called when the iostream transport receives data
+ * that needs to be written to the appropriate output location. This handler
+ * can be used in place of registering an ostream for output.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, char const *, size_t)` The
+ * code returned will be reported and logged by the core library.
+ *
+ * See also, set_vector_write_handler, for an optional write handler that
+ * allows more efficient handling of multiple writes at once.
+ *
+ * @see set_vector_write_handler
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call when data is to be written.
+ */
+ void set_write_handler(write_handler h) {
+ m_write_handler = h;
+ }
+
+ /// Sets the vectored write handler
+ /**
+ * The vectored write handler is called when the iostream transport receives
+ * multiple chunks of data that need to be written to the appropriate output
+ * location. This handler can be used in conjunction with the write_handler
+ * in place of registering an ostream for output.
+ *
+ * The sequence of buffers represents bytes that should be written
+ * consecutively and it is suggested to group the buffers into as few next
+ * layer packets as possible. Vector write is used to allow implementations
+ * that support it to coalesce writes into a single TCP packet or TLS
+ * segment for improved efficiency.
+ *
+ * This is an optional handler. If it is not defined then multiple calls
+ * will be made to the standard write handler.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, std::vector<websocketpp::transport::buffer>
+ * const & bufs)`. The code returned will be reported and logged by the core
+ * library. The `websocketpp::transport::buffer` type is a struct with two
+ * data members. buf (char const *) and len (size_t).
+ *
+ * @since 0.6.0
+ *
+ * @param h The handler to call when vectored data is to be written.
+ */
+ void set_vector_write_handler(vector_write_handler h) {
+ m_vector_write_handler = h;
+ }
+
+ /// Sets the shutdown handler
+ /**
+ * The shutdown handler is called when the iostream transport receives a
+ * notification from the core library that it is finished with all read and
+ * write operations and that the underlying transport can be cleaned up.
+ *
+ * If you are using iostream transport with another socket library, this is
+ * a good time to close/shutdown the socket for this connection.
+ *
+ * The signature of the handler is `lib::error_code (connection_hdl)`. The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_shutdown_handler(shutdown_handler h) {
+ m_shutdown_handler = h;
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"iostream connection init");
+ handler(lib::error_code());
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char *buf, size_t len,
+ read_handler handler)
+ {
+ std::stringstream s;
+ s << "iostream_con async_read_at_least: " << num_bytes;
+ m_alog.write(log::alevel::devel,s.str());
+
+ if (num_bytes > len) {
+ handler(make_error_code(error::invalid_num_bytes),size_t(0));
+ return;
+ }
+
+ if (m_reading == true) {
+ handler(make_error_code(error::double_read),size_t(0));
+ return;
+ }
+
+ if (num_bytes == 0 || len == 0) {
+ handler(lib::error_code(),size_t(0));
+ return;
+ }
+
+ m_buf = buf;
+ m_len = len;
+ m_bytes_needed = num_bytes;
+ m_read_handler = handler;
+ m_cursor = 0;
+ m_reading = true;
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output method. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success. Other possible errors (not exhaustive)
+ * output_stream_required: No output stream was registered to write to
+ * bad_stream: a ostream pass through error
+ *
+ * This method will attempt to write to the registered ostream first. If an
+ * ostream is not registered it will use the write handler. If neither are
+ * registered then an error is passed up to the connection.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const * buf, size_t len, transport::write_handler
+ handler)
+ {
+ m_alog.write(log::alevel::devel,"iostream_con async_write");
+ // TODO: lock transport state?
+
+ lib::error_code ec;
+
+ if (m_output_stream) {
+ m_output_stream->write(buf,len);
+
+ if (m_output_stream->bad()) {
+ ec = make_error_code(error::bad_stream);
+ }
+ } else if (m_write_handler) {
+ ec = m_write_handler(m_connection_hdl, buf, len);
+ } else {
+ ec = make_error_code(error::output_stream_required);
+ }
+
+ handler(ec);
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output method. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success. Other possible errors (not exhaustive)
+ * output_stream_required: No output stream was registered to write to
+ * bad_stream: a ostream pass through error
+ *
+ * This method will attempt to write to the registered ostream first. If an
+ * ostream is not registered it will use the write handler. If neither are
+ * registered then an error is passed up to the connection.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const & bufs, transport::write_handler
+ handler)
+ {
+ m_alog.write(log::alevel::devel,"iostream_con async_write buffer list");
+ // TODO: lock transport state?
+
+ lib::error_code ec;
+
+ if (m_output_stream) {
+ std::vector<buffer>::const_iterator it;
+ for (it = bufs.begin(); it != bufs.end(); it++) {
+ m_output_stream->write((*it).buf,(*it).len);
+
+ if (m_output_stream->bad()) {
+ ec = make_error_code(error::bad_stream);
+ break;
+ }
+ }
+ } else if (m_vector_write_handler) {
+ ec = m_vector_write_handler(m_connection_hdl, bufs);
+ } else if (m_write_handler) {
+ std::vector<buffer>::const_iterator it;
+ for (it = bufs.begin(); it != bufs.end(); it++) {
+ ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len);
+ if (ec) {break;}
+ }
+
+ } else {
+ ec = make_error_code(error::output_stream_required);
+ }
+
+ handler(ec);
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {
+ m_connection_hdl = hdl;
+ }
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * If a shutdown handler is set, call it and pass through its return error
+ * code. Otherwise assume there is nothing to do and pass through a success
+ * code.
+ *
+ * @param handler The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(transport::shutdown_handler handler) {
+ lib::error_code ec;
+
+ if (m_shutdown_handler) {
+ ec = m_shutdown_handler(m_connection_hdl);
+ }
+
+ handler(ec);
+ }
+private:
+ void read(std::istream &in) {
+ m_alog.write(log::alevel::devel,"iostream_con read");
+
+ while (in.good()) {
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ break;
+ }
+
+ in.read(m_buf+m_cursor,static_cast<std::streamsize>(m_len-m_cursor));
+
+ if (in.gcount() == 0) {
+ m_elog.write(log::elevel::devel,"read zero bytes");
+ break;
+ }
+
+ m_cursor += static_cast<size_t>(in.gcount());
+
+ // TODO: error handling
+ if (in.bad()) {
+ m_reading = false;
+ complete_read(make_error_code(error::bad_stream));
+ }
+
+ if (m_cursor >= m_bytes_needed) {
+ m_reading = false;
+ complete_read(lib::error_code());
+ }
+ }
+ }
+
+ size_t read_some_impl(char const * buf, size_t len) {
+ m_alog.write(log::alevel::devel,"iostream_con read_some");
+
+ if (!m_reading) {
+ m_elog.write(log::elevel::devel,"write while not reading");
+ return 0;
+ }
+
+ size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
+
+ std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
+
+ m_cursor += bytes_to_copy;
+
+ if (m_cursor >= m_bytes_needed) {
+ complete_read(lib::error_code());
+ }
+
+ return bytes_to_copy;
+ }
+
+ /// Signal that a requested read is complete
+ /**
+ * Sets the reading flag to false and returns the handler that should be
+ * called back with the result of the read. The cursor position that is sent
+ * is whatever the value of m_cursor is.
+ *
+ * It MUST NOT be called when m_reading is false.
+ * it MUST be called while holding the read lock
+ *
+ * It is important to use this method rather than directly setting/calling
+ * m_read_handler back because this function makes sure to delete the
+ * locally stored handler which contains shared pointers that will otherwise
+ * cause circular reference based memory leaks.
+ *
+ * @param ec The error code to forward to the read handler
+ */
+ void complete_read(lib::error_code const & ec) {
+ m_reading = false;
+
+ read_handler handler = m_read_handler;
+ m_read_handler = read_handler();
+
+ handler(ec,m_cursor);
+ }
+
+ // Read space (Protected by m_read_mutex)
+ char * m_buf;
+ size_t m_len;
+ size_t m_bytes_needed;
+ read_handler m_read_handler;
+ size_t m_cursor;
+
+ // transport resources
+ std::ostream * m_output_stream;
+ connection_hdl m_connection_hdl;
+ write_handler m_write_handler;
+ vector_write_handler m_vector_write_handler;
+ shutdown_handler m_shutdown_handler;
+
+ bool m_reading;
+ bool const m_is_server;
+ bool m_is_secure;
+ alog_type & m_alog;
+ elog_type & m_elog;
+ std::string m_remote_endpoint;
+
+ // This lock ensures that only one thread can edit read data for this
+ // connection. This is a very coarse lock that is basically locked all the
+ // time. The nature of the connection is such that it cannot be
+ // parallelized, the locking is here to prevent intra-connection concurrency
+ // in order to allow inter-connection concurrency.
+ mutex_type m_read_mutex;
+};
+
+
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp
new file mode 100644
index 00000000..14ec6537
--- /dev/null
+++ b/websocketpp/transport/iostream/endpoint.hpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
+#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/iostream/connection.hpp>
+
+#include <websocketpp/uri.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/memory.hpp>
+
+#include <ostream>
+
+namespace websocketpp {
+namespace transport {
+namespace iostream {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef iostream::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint() : m_output_stream(NULL), m_is_secure(false)
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Register a default output stream
+ /**
+ * The specified output stream will be assigned to future connections as the
+ * default output stream.
+ *
+ * @param o The ostream to use as the default output stream.
+ */
+ void register_ostream(std::ostream * o) {
+ m_alog->write(log::alevel::devel,"register_ostream");
+ m_output_stream = o;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * The iostream transport does not provide any security features. As such
+ * it defaults to returning false when `is_secure` is called. However, the
+ * iostream transport may be used to wrap an external socket API that may
+ * provide secure transport. This method allows that external API to flag
+ * whether or not it can create secure connections so that users of the
+ * WebSocket++ API will get more accurate information.
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool value) {
+ m_is_secure = value;
+ }
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * iostream transport will return false by default because it has no
+ * information about the ultimate remote endpoint. This may or may not be
+ * accurate depending on the real source of bytes being input. `set_secure`
+ * may be used by a wrapper API to correct the return value in the case that
+ * secure connections are in fact possible.
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return m_is_secure;
+ }
+
+ /// Sets the write handler
+ /**
+ * The write handler is called when the iostream transport receives data
+ * that needs to be written to the appropriate output location. This handler
+ * can be used in place of registering an ostream for output.
+ *
+ * The signature of the handler is
+ * `lib::error_code (connection_hdl, char const *, size_t)` The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_write_handler(write_handler h) {
+ m_write_handler = h;
+ }
+
+ /// Sets the shutdown handler
+ /**
+ * The shutdown handler is called when the iostream transport receives a
+ * notification from the core library that it is finished with all read and
+ * write operations and that the underlying transport can be cleaned up.
+ *
+ * If you are using iostream transport with another socket library, this is
+ * a good time to close/shutdown the socket for this connection.
+ *
+ * The signature of the handler is lib::error_code (connection_hdl). The
+ * code returned will be reported and logged by the core library.
+ *
+ * @since 0.5.0
+ *
+ * @param h The handler to call on connection shutdown.
+ */
+ void set_shutdown_handler(shutdown_handler h) {
+ m_shutdown_handler = h;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type * a, elog_type * e) {
+ m_elog = e;
+ m_alog = a;
+ }
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
+ cb(lib::error_code());
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ tcon->register_ostream(m_output_stream);
+ if (m_shutdown_handler) {
+ tcon->set_shutdown_handler(m_shutdown_handler);
+ }
+ if (m_write_handler) {
+ tcon->set_write_handler(m_write_handler);
+ }
+ return lib::error_code();
+ }
+private:
+ std::ostream * m_output_stream;
+ shutdown_handler m_shutdown_handler;
+ write_handler m_write_handler;
+
+ elog_type * m_elog;
+ alog_type * m_alog;
+ bool m_is_secure;
+};
+
+
+} // namespace iostream
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
diff --git a/websocketpp/transport/stub/base.hpp b/websocketpp/transport/stub/base.hpp
new file mode 100644
index 00000000..754981e2
--- /dev/null
+++ b/websocketpp/transport/stub/base.hpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP
+
+#include <websocketpp/common/system_error.hpp>
+#include <websocketpp/common/cpp11.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace transport {
+/// Stub transport policy that has no input or output.
+namespace stub {
+
+/// stub transport errors
+namespace error {
+enum value {
+ /// Catch-all error for transport policy errors that don't fit in other
+ /// categories
+ general = 1,
+
+ /// not implemented
+ not_implemented
+};
+
+/// stub transport error category
+class category : public lib::error_category {
+ public:
+ category() {}
+
+ char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
+ return "websocketpp.transport.stub";
+ }
+
+ std::string message(int value) const {
+ switch(value) {
+ case general:
+ return "Generic stub transport policy error";
+ case not_implemented:
+ return "feature not implemented";
+ default:
+ return "Unknown";
+ }
+ }
+};
+
+/// Get a reference to a static copy of the stub transport error category
+inline lib::error_category const & get_category() {
+ static category instance;
+ return instance;
+}
+
+/// Get an error code with the given value and the stub transport category
+inline lib::error_code make_error_code(error::value e) {
+ return lib::error_code(static_cast<int>(e), get_category());
+}
+
+} // namespace error
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
+template<> struct is_error_code_enum<websocketpp::transport::stub::error::value>
+{
+ static bool const value = true;
+};
+_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_BASE_HPP
diff --git a/websocketpp/transport/stub/connection.hpp b/websocketpp/transport/stub/connection.hpp
new file mode 100644
index 00000000..59bd4a0a
--- /dev/null
+++ b/websocketpp/transport/stub/connection.hpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
+
+#include <websocketpp/transport/stub/base.hpp>
+
+#include <websocketpp/transport/base/connection.hpp>
+
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/common/connection_hdl.hpp>
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/common/platforms.hpp>
+
+#include <string>
+#include <vector>
+
+namespace websocketpp {
+namespace transport {
+namespace stub {
+
+/// Empty timer class to stub out for timer functionality that stub
+/// transport doesn't support
+struct timer {
+ void cancel() {}
+};
+
+template <typename config>
+class connection : public lib::enable_shared_from_this< connection<config> > {
+public:
+ /// Type of this connection transport component
+ typedef connection<config> type;
+ /// Type of a shared pointer to this connection transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// transport concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this transport's access logging policy
+ typedef typename config::alog_type alog_type;
+ /// Type of this transport's error logging policy
+ typedef typename config::elog_type elog_type;
+
+ // Concurrency policy types
+ typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
+ typedef typename concurrency_type::mutex_type mutex_type;
+
+ typedef lib::shared_ptr<timer> timer_ptr;
+
+ explicit connection(bool is_server, alog_type & alog, elog_type & elog)
+ : m_alog(alog), m_elog(elog)
+ {
+ m_alog.write(log::alevel::devel,"stub con transport constructor");
+ }
+
+ /// Get a shared pointer to this component
+ ptr get_shared() {
+ return type::shared_from_this();
+ }
+
+ /// Set whether or not this connection is secure
+ /**
+ * Todo: docs
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not this connection is secure.
+ */
+ void set_secure(bool value) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+
+ /// Set uri hook
+ /**
+ * Called by the endpoint as a connection is being established to provide
+ * the uri being connected to to the transport layer.
+ *
+ * Implementation is optional and can be ignored if the transport has no
+ * need for this information.
+ *
+ * @since 0.6.0
+ *
+ * @param u The uri to set
+ */
+ void set_uri(uri_ptr) {}
+
+ /// Set human readable remote endpoint address
+ /**
+ * Sets the remote endpoint address returned by `get_remote_endpoint`. This
+ * value should be a human readable string that describes the remote
+ * endpoint. Typically an IP address or hostname, perhaps with a port. But
+ * may be something else depending on the nature of the underlying
+ * transport.
+ *
+ * If none is set a default is returned.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value The remote endpoint address to set.
+ */
+ void set_remote_endpoint(std::string value) {}
+
+ /// Get human readable remote endpoint address
+ /**
+ * TODO: docs
+ *
+ * This value is used in access and error logs and is available to the end
+ * application for including in user facing interfaces and messages.
+ *
+ * @return A string identifying the address of the remote endpoint
+ */
+ std::string get_remote_endpoint() const {
+ return "unknown (stub transport)";
+ }
+
+ /// Get the connection handle
+ /**
+ * @return The handle for this connection.
+ */
+ connection_hdl get_handle() const {
+ return connection_hdl();
+ }
+
+ /// Call back a function after a period of time.
+ /**
+ * Timers are not implemented in this transport. The timer pointer will
+ * always be empty. The handler will never be called.
+ *
+ * @param duration Length of time to wait in milliseconds
+ * @param callback The function to call back when the timer has expired
+ * @return A handle that can be used to cancel the timer if it is no longer
+ * needed.
+ */
+ timer_ptr set_timer(long duration, timer_handler handler) {
+ return timer_ptr();
+ }
+protected:
+ /// Initialize the connection transport
+ /**
+ * Initialize the connection's transport component.
+ *
+ * @param handler The `init_handler` to call when initialization is done
+ */
+ void init(init_handler handler) {
+ m_alog.write(log::alevel::devel,"stub connection init");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Initiate an async_read for at least num_bytes bytes into buf
+ /**
+ * Initiates an async_read request for at least num_bytes bytes. The input
+ * will be read into buf. A maximum of len bytes will be input. When the
+ * operation is complete, handler will be called with the status and number
+ * of bytes read.
+ *
+ * This method may or may not call handler from within the initial call. The
+ * application should be prepared to accept either.
+ *
+ * The application should never call this method a second time before it has
+ * been called back for the first read. If this is done, the second read
+ * will be called back immediately with a double_read error.
+ *
+ * If num_bytes or len are zero handler will be called back immediately
+ * indicating success.
+ *
+ * @param num_bytes Don't call handler until at least this many bytes have
+ * been read.
+ * @param buf The buffer to read bytes into
+ * @param len The size of buf. At maximum, this many bytes will be read.
+ * @param handler The callback to invoke when the operation is complete or
+ * ends in an error
+ */
+ void async_read_at_least(size_t num_bytes, char * buf, size_t len,
+ read_handler handler)
+ {
+ m_alog.write(log::alevel::devel, "stub_con async_read_at_least");
+ handler(make_error_code(error::not_implemented), 0);
+ }
+
+ /// Asyncronous Transport Write
+ /**
+ * Write len bytes in buf to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param buf buffer to read bytes from
+ * @param len number of bytes to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(char const * buf, size_t len, write_handler handler) {
+ m_alog.write(log::alevel::devel,"stub_con async_write");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Asyncronous Transport Write (scatter-gather)
+ /**
+ * Write a sequence of buffers to the output stream. Call handler to report
+ * success or failure. handler may or may not be called during async_write,
+ * but it must be safe for this to happen.
+ *
+ * Will return 0 on success.
+ *
+ * @param bufs vector of buffers to write
+ * @param handler Callback to invoke with operation status.
+ */
+ void async_write(std::vector<buffer> const & bufs, write_handler handler) {
+ m_alog.write(log::alevel::devel,"stub_con async_write buffer list");
+ handler(make_error_code(error::not_implemented));
+ }
+
+ /// Set Connection Handle
+ /**
+ * @param hdl The new handle
+ */
+ void set_handle(connection_hdl hdl) {}
+
+ /// Call given handler back within the transport's event system (if present)
+ /**
+ * Invoke a callback within the transport's event system if it has one. If
+ * it doesn't, the handler will be invoked immediately before this function
+ * returns.
+ *
+ * @param handler The callback to invoke
+ *
+ * @return Whether or not the transport was able to register the handler for
+ * callback.
+ */
+ lib::error_code dispatch(dispatch_handler handler) {
+ handler();
+ return lib::error_code();
+ }
+
+ /// Perform cleanup on socket shutdown_handler
+ /**
+ * @param h The `shutdown_handler` to call back when complete
+ */
+ void async_shutdown(shutdown_handler handler) {
+ handler(lib::error_code());
+ }
+private:
+ // member variables!
+ alog_type & m_alog;
+ elog_type & m_elog;
+};
+
+
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
diff --git a/websocketpp/transport/stub/endpoint.hpp b/websocketpp/transport/stub/endpoint.hpp
new file mode 100644
index 00000000..3bbb78f3
--- /dev/null
+++ b/websocketpp/transport/stub/endpoint.hpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_TRANSPORT_STUB_HPP
+#define WEBSOCKETPP_TRANSPORT_STUB_HPP
+
+#include <websocketpp/common/memory.hpp>
+#include <websocketpp/logger/levels.hpp>
+
+#include <websocketpp/transport/base/endpoint.hpp>
+#include <websocketpp/transport/stub/connection.hpp>
+
+namespace websocketpp {
+namespace transport {
+namespace stub {
+
+template <typename config>
+class endpoint {
+public:
+ /// Type of this endpoint transport component
+ typedef endpoint type;
+ /// Type of a pointer to this endpoint transport component
+ typedef lib::shared_ptr<type> ptr;
+
+ /// Type of this endpoint's concurrency policy
+ typedef typename config::concurrency_type concurrency_type;
+ /// Type of this endpoint's error logging policy
+ typedef typename config::elog_type elog_type;
+ /// Type of this endpoint's access logging policy
+ typedef typename config::alog_type alog_type;
+
+ /// Type of this endpoint transport component's associated connection
+ /// transport component.
+ typedef stub::connection<config> transport_con_type;
+ /// Type of a shared pointer to this endpoint transport component's
+ /// associated connection transport component
+ typedef typename transport_con_type::ptr transport_con_ptr;
+
+ // generate and manage our own io_service
+ explicit endpoint()
+ {
+ //std::cout << "transport::iostream::endpoint constructor" << std::endl;
+ }
+
+ /// Set whether or not endpoint can create secure connections
+ /**
+ * TODO: docs
+ *
+ * Setting this value only indicates whether or not the endpoint is capable
+ * of producing and managing secure connections. Connections produced by
+ * this endpoint must also be individually flagged as secure if they are.
+ *
+ * @since 0.3.0-alpha4
+ *
+ * @param value Whether or not the endpoint can create secure connections.
+ */
+ void set_secure(bool value) {}
+
+ /// Tests whether or not the underlying transport is secure
+ /**
+ * TODO: docs
+ *
+ * @return Whether or not the underlying transport is secure
+ */
+ bool is_secure() const {
+ return false;
+ }
+protected:
+ /// Initialize logging
+ /**
+ * The loggers are located in the main endpoint class. As such, the
+ * transport doesn't have direct access to them. This method is called
+ * by the endpoint constructor to allow shared logging from the transport
+ * component. These are raw pointers to member variables of the endpoint.
+ * In particular, they cannot be used in the transport constructor as they
+ * haven't been constructed yet, and cannot be used in the transport
+ * destructor as they will have been destroyed by then.
+ *
+ * @param a A pointer to the access logger to use.
+ * @param e A pointer to the error logger to use.
+ */
+ void init_logging(alog_type * a, elog_type * e) {}
+
+ /// Initiate a new connection
+ /**
+ * @param tcon A pointer to the transport connection component of the
+ * connection to connect.
+ * @param u A URI pointer to the URI to connect to.
+ * @param cb The function to call back with the results when complete.
+ */
+ void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
+ cb(make_error_code(error::not_implemented));
+ }
+
+ /// Initialize a connection
+ /**
+ * Init is called by an endpoint once for each newly created connection.
+ * It's purpose is to give the transport policy the chance to perform any
+ * transport specific initialization that couldn't be done via the default
+ * constructor.
+ *
+ * @param tcon A pointer to the transport portion of the connection.
+ * @return A status code indicating the success or failure of the operation
+ */
+ lib::error_code init(transport_con_ptr tcon) {
+ return make_error_code(error::not_implemented);
+ }
+private:
+
+};
+
+} // namespace stub
+} // namespace transport
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_TRANSPORT_STUB_HPP
diff --git a/websocketpp/uri.hpp b/websocketpp/uri.hpp
new file mode 100644
index 00000000..7159234f
--- /dev/null
+++ b/websocketpp/uri.hpp
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_URI_HPP
+#define WEBSOCKETPP_URI_HPP
+
+#include <websocketpp/error.hpp>
+
+#include <websocketpp/common/memory.hpp>
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+
+namespace websocketpp {
+
+// TODO: figure out why this fixes horrible linking errors.
+
+/// Default port for ws://
+static uint16_t const uri_default_port = 80;
+/// Default port for wss://
+static uint16_t const uri_default_secure_port = 443;
+
+class uri {
+public:
+ explicit uri(std::string const & uri_string) : m_valid(false) {
+ std::string::const_iterator it;
+ std::string::const_iterator temp;
+
+ int state = 0;
+
+ it = uri_string.begin();
+ size_t uri_len = uri_string.length();
+
+ if (uri_len >= 7 && std::equal(it,it+6,"wss://")) {
+ m_secure = true;
+ m_scheme = "wss";
+ it += 6;
+ } else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) {
+ m_secure = false;
+ m_scheme = "ws";
+ it += 5;
+ } else if (uri_len >= 8 && std::equal(it,it+7,"http://")) {
+ m_secure = false;
+ m_scheme = "http";
+ it += 7;
+ } else if (uri_len >= 9 && std::equal(it,it+8,"https://")) {
+ m_secure = true;
+ m_scheme = "https";
+ it += 8;
+ } else {
+ return;
+ }
+
+ // extract host.
+ // either a host string
+ // an IPv4 address
+ // or an IPv6 address
+ if (*it == '[') {
+ ++it;
+ // IPv6 literal
+ // extract IPv6 digits until ]
+
+ // TODO: this doesn't work on g++... not sure why
+ //temp = std::find(it,it2,']');
+
+ temp = it;
+ while (temp != uri_string.end()) {
+ if (*temp == ']') {
+ break;
+ }
+ ++temp;
+ }
+
+ if (temp == uri_string.end()) {
+ return;
+ } else {
+ // validate IPv6 literal parts
+ // can contain numbers, a-f and A-F
+ m_host.append(it,temp);
+ }
+ it = temp+1;
+ if (it == uri_string.end()) {
+ state = 2;
+ } else if (*it == '/') {
+ state = 2;
+ ++it;
+ } else if (*it == ':') {
+ state = 1;
+ ++it;
+ } else {
+ // problem
+ return;
+ }
+ } else {
+ // IPv4 or hostname
+ // extract until : or /
+ while (state == 0) {
+ if (it == uri_string.end()) {
+ state = 2;
+ break;
+ } else if (*it == '/') {
+ state = 2;
+ } else if (*it == ':') {
+ // end hostname start port
+ state = 1;
+ } else {
+ m_host += *it;
+ }
+ ++it;
+ }
+ }
+
+ // parse port
+ std::string port;
+ while (state == 1) {
+ if (it == uri_string.end()) {
+ // state is not used after this point presently.
+ // this should be re-enabled if it ever is needed in a future
+ // refactoring
+ //state = 3;
+ break;
+ } else if (*it == '/') {
+ state = 3;
+ } else {
+ port += *it;
+ }
+ ++it;
+ }
+
+ lib::error_code ec;
+ m_port = get_port_from_string(port, ec);
+
+ if (ec) {
+ return;
+ }
+
+ m_resource = "/";
+ m_resource.append(it,uri_string.end());
+
+
+ m_valid = true;
+ }
+
+ uri(bool secure, std::string const & host, uint16_t port,
+ std::string const & resource)
+ : m_scheme(secure ? "wss" : "ws")
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_port(port)
+ , m_secure(secure)
+ , m_valid(true) {}
+
+ uri(bool secure, std::string const & host, std::string const & resource)
+ : m_scheme(secure ? "wss" : "ws")
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_port(secure ? uri_default_secure_port : uri_default_port)
+ , m_secure(secure)
+ , m_valid(true) {}
+
+ uri(bool secure, std::string const & host, std::string const & port,
+ std::string const & resource)
+ : m_scheme(secure ? "wss" : "ws")
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_secure(secure)
+ {
+ lib::error_code ec;
+ m_port = get_port_from_string(port,ec);
+ m_valid = !ec;
+ }
+
+ uri(std::string const & scheme, std::string const & host, uint16_t port,
+ std::string const & resource)
+ : m_scheme(scheme)
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_port(port)
+ , m_secure(scheme == "wss" || scheme == "https")
+ , m_valid(true) {}
+
+ uri(std::string scheme, std::string const & host, std::string const & resource)
+ : m_scheme(scheme)
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port)
+ , m_secure(scheme == "wss" || scheme == "https")
+ , m_valid(true) {}
+
+ uri(std::string const & scheme, std::string const & host,
+ std::string const & port, std::string const & resource)
+ : m_scheme(scheme)
+ , m_host(host)
+ , m_resource(resource.empty() ? "/" : resource)
+ , m_secure(scheme == "wss" || scheme == "https")
+ {
+ lib::error_code ec;
+ m_port = get_port_from_string(port,ec);
+ m_valid = !ec;
+ }
+
+ bool get_valid() const {
+ return m_valid;
+ }
+
+ bool get_secure() const {
+ return m_secure;
+ }
+
+ std::string const & get_scheme() const {
+ return m_scheme;
+ }
+
+ std::string const & get_host() const {
+ return m_host;
+ }
+
+ std::string get_host_port() const {
+ if (m_port == (m_secure ? uri_default_secure_port : uri_default_port)) {
+ return m_host;
+ } else {
+ std::stringstream p;
+ p << m_host << ":" << m_port;
+ return p.str();
+ }
+ }
+
+ std::string get_authority() const {
+ std::stringstream p;
+ p << m_host << ":" << m_port;
+ return p.str();
+ }
+
+ uint16_t get_port() const {
+ return m_port;
+ }
+
+ std::string get_port_str() const {
+ std::stringstream p;
+ p << m_port;
+ return p.str();
+ }
+
+ std::string const & get_resource() const {
+ return m_resource;
+ }
+
+ std::string str() const {
+ std::stringstream s;
+
+ s << m_scheme << "://" << m_host;
+
+ if (m_port != (m_secure ? uri_default_secure_port : uri_default_port)) {
+ s << ":" << m_port;
+ }
+
+ s << m_resource;
+ return s.str();
+ }
+
+ /// Return the query portion
+ /**
+ * Returns the query portion (after the ?) of the URI or an empty string if
+ * there is none.
+ *
+ * @return query portion of the URI.
+ */
+ std::string get_query() const {
+ std::size_t found = m_resource.find('?');
+ if (found != std::string::npos) {
+ return m_resource.substr(found + 1);
+ } else {
+ return "";
+ }
+ }
+
+ // get fragment
+
+ // hi <3
+
+ // get the string representation of this URI
+
+ //std::string base() const; // is this still needed?
+
+ // setter methods set some or all (in the case of parse) based on the input.
+ // These functions throw a uri_exception on failure.
+ /*void set_uri(const std::string& uri);
+
+ void set_secure(bool secure);
+ void set_host(const std::string& host);
+ void set_port(uint16_t port);
+ void set_port(const std::string& port);
+ void set_resource(const std::string& resource);*/
+private:
+ uint16_t get_port_from_string(std::string const & port, lib::error_code &
+ ec) const
+ {
+ ec = lib::error_code();
+
+ if (port.empty()) {
+ return (m_secure ? uri_default_secure_port : uri_default_port);
+ }
+
+ unsigned int t_port = static_cast<unsigned int>(atoi(port.c_str()));
+
+ if (t_port > 65535) {
+ ec = error::make_error_code(error::invalid_port);
+ }
+
+ if (t_port == 0) {
+ ec = error::make_error_code(error::invalid_port);
+ }
+
+ return static_cast<uint16_t>(t_port);
+ }
+
+ std::string m_scheme;
+ std::string m_host;
+ std::string m_resource;
+ uint16_t m_port;
+ bool m_secure;
+ bool m_valid;
+};
+
+/// Pointer to a URI
+typedef lib::shared_ptr<uri> uri_ptr;
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_URI_HPP
diff --git a/websocketpp/utf8_validator.hpp b/websocketpp/utf8_validator.hpp
new file mode 100644
index 00000000..c057da62
--- /dev/null
+++ b/websocketpp/utf8_validator.hpp
@@ -0,0 +1,154 @@
+/*
+ * The following code is adapted from code originally written by Bjoern
+ * Hoehrmann <bjoern@hoehrmann.de>. See
+ * http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+ *
+ * The original license:
+ *
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+*/
+
+#ifndef UTF8_VALIDATOR_HPP
+#define UTF8_VALIDATOR_HPP
+
+#include <websocketpp/common/stdint.hpp>
+
+#include <string>
+
+namespace websocketpp {
+namespace utf8_validator {
+
+/// State that represents a valid utf8 input sequence
+static unsigned int const utf8_accept = 0;
+/// State that represents an invalid utf8 input sequence
+static unsigned int const utf8_reject = 1;
+
+/// Lookup table for the UTF8 decode state machine
+static uint8_t const utf8d[] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
+ 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
+ 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
+ 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
+ 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
+ 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
+ 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
+};
+
+/// Decode the next byte of a UTF8 sequence
+/**
+ * @param [out] state The decoder state to advance
+ * @param [out] codep The codepoint to fill in
+ * @param [in] byte The byte to input
+ * @return The ending state of the decode operation
+ */
+inline uint32_t decode(uint32_t * state, uint32_t * codep, uint8_t byte) {
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != utf8_accept) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state*16 + type];
+ return *state;
+}
+
+/// Provides streaming UTF8 validation functionality
+class validator {
+public:
+ /// Construct and initialize the validator
+ validator() : m_state(utf8_accept),m_codepoint(0) {}
+
+ /// Advance the state of the validator with the next input byte
+ /**
+ * @param byte The byte to advance the validation state with
+ * @return Whether or not the byte resulted in a validation error.
+ */
+ bool consume (uint8_t byte) {
+ if (utf8_validator::decode(&m_state,&m_codepoint,byte) == utf8_reject) {
+ return false;
+ }
+ return true;
+ }
+
+ /// Advance validator state with input from an iterator pair
+ /**
+ * @param begin Input iterator to the start of the input range
+ * @param end Input iterator to the end of the input range
+ * @return Whether or not decoding the bytes resulted in a validation error.
+ */
+ template <typename iterator_type>
+ bool decode (iterator_type begin, iterator_type end) {
+ for (iterator_type it = begin; it != end; ++it) {
+ unsigned int result = utf8_validator::decode(
+ &m_state,
+ &m_codepoint,
+ static_cast<uint8_t>(*it)
+ );
+
+ if (result == utf8_reject) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// Return whether the input sequence ended on a valid utf8 codepoint
+ /**
+ * @return Whether or not the input sequence ended on a valid codepoint.
+ */
+ bool complete() {
+ return m_state == utf8_accept;
+ }
+
+ /// Reset the validator to decode another message
+ void reset() {
+ m_state = utf8_accept;
+ m_codepoint = 0;
+ }
+private:
+ uint32_t m_state;
+ uint32_t m_codepoint;
+};
+
+/// Validate a UTF8 string
+/**
+ * convenience function that creates a validator, validates a complete string
+ * and returns the result.
+ */
+inline bool validate(std::string const & s) {
+ validator v;
+ if (!v.decode(s.begin(),s.end())) {
+ return false;
+ }
+ return v.complete();
+}
+
+} // namespace utf8_validator
+} // namespace websocketpp
+
+#endif // UTF8_VALIDATOR_HPP
diff --git a/websocketpp/utilities.hpp b/websocketpp/utilities.hpp
new file mode 100644
index 00000000..747f1199
--- /dev/null
+++ b/websocketpp/utilities.hpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2014, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_UTILITIES_HPP
+#define WEBSOCKETPP_UTILITIES_HPP
+
+#include <websocketpp/common/stdint.hpp>
+
+#include <algorithm>
+#include <string>
+#include <locale>
+
+namespace websocketpp {
+/// Generic non-websocket specific utility functions and data structures
+namespace utility {
+
+/// Helper functor for case insensitive find
+/**
+ * Based on code from
+ * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find
+ *
+ * templated version of my_equal so it could work with both char and wchar_t
+ */
+template<typename charT>
+struct my_equal {
+ /// Construct the functor with the given locale
+ /**
+ * @param [in] loc The locale to use for determining the case of values
+ */
+ my_equal(std::locale const & loc ) : m_loc(loc) {}
+
+ /// Perform a case insensitive comparison
+ /**
+ * @param ch1 The first value to compare
+ * @param ch2 The second value to compare
+ * @return Whether or not the two values are equal when both are converted
+ * to uppercase using the given locale.
+ */
+ bool operator()(charT ch1, charT ch2) {
+ return std::toupper(ch1, m_loc) == std::toupper(ch2, m_loc);
+ }
+private:
+ std::locale const & m_loc;
+};
+
+/// Helper less than functor for case insensitive find
+/**
+ * Based on code from
+ * http://stackoverflow.com/questions/3152241/case-insensitive-stdstring-find
+ */
+struct ci_less : std::binary_function<std::string, std::string, bool> {
+ // case-independent (ci) compare_less binary function
+ struct nocase_compare
+ : public std::binary_function<unsigned char,unsigned char,bool>
+ {
+ bool operator() (unsigned char const & c1, unsigned char const & c2) const {
+ return tolower (c1) < tolower (c2);
+ }
+ };
+ bool operator() (std::string const & s1, std::string const & s2) const {
+ return std::lexicographical_compare
+ (s1.begin (), s1.end (), // source range
+ s2.begin (), s2.end (), // dest range
+ nocase_compare ()); // comparison
+ }
+};
+
+/// Find substring (case insensitive)
+/**
+ * @param [in] haystack The string to search in
+ * @param [in] needle The string to search for
+ * @param [in] loc The locale to use for determining the case of values.
+ * Defaults to the current locale.
+ * @return An iterator to the first element of the first occurrance of needle in
+ * haystack. If the sequence is not found, the function returns
+ * haystack.end()
+ */
+template<typename T>
+typename T::const_iterator ci_find_substr(T const & haystack, T const & needle,
+ std::locale const & loc = std::locale())
+{
+ return std::search( haystack.begin(), haystack.end(),
+ needle.begin(), needle.end(), my_equal<typename T::value_type>(loc) );
+}
+
+/// Find substring (case insensitive)
+/**
+ * @todo Is this still used? This method may not make sense.. should use
+ * iterators or be less generic. As is it is too tightly coupled to std::string
+ *
+ * @param [in] haystack The string to search in
+ * @param [in] needle The string to search for as a char array of values
+ * @param [in] size Length of needle
+ * @param [in] loc The locale to use for determining the case of values.
+ * Defaults to the current locale.
+ * @return An iterator to the first element of the first occurrance of needle in
+ * haystack. If the sequence is not found, the function returns
+ * haystack.end()
+ */
+template<typename T>
+typename T::const_iterator ci_find_substr(T const & haystack,
+ typename T::value_type const * needle, typename T::size_type size,
+ std::locale const & loc = std::locale())
+{
+ return std::search( haystack.begin(), haystack.end(),
+ needle, needle+size, my_equal<typename T::value_type>(loc) );
+}
+
+/// Convert a string to lowercase
+/**
+ * @param [in] in The string to convert
+ * @return The converted string
+ */
+std::string to_lower(std::string const & in);
+
+/// Replace all occurrances of a substring with another
+/**
+ * @param [in] subject The string to search in
+ * @param [in] search The string to search for
+ * @param [in] replace The string to replace with
+ * @return A copy of `subject` with all occurances of `search` replaced with
+ * `replace`
+ */
+std::string string_replace_all(std::string subject, std::string const & search,
+ std::string const & replace);
+
+/// Convert std::string to ascii printed string of hex digits
+/**
+ * @param [in] input The string to print
+ * @return A copy of `input` converted to the printable representation of the
+ * hex values of its data.
+ */
+std::string to_hex(std::string const & input);
+
+/// Convert byte array (uint8_t) to ascii printed string of hex digits
+/**
+ * @param [in] input The byte array to print
+ * @param [in] length The length of input
+ * @return A copy of `input` converted to the printable representation of the
+ * hex values of its data.
+ */
+std::string to_hex(uint8_t const * input, size_t length);
+
+/// Convert char array to ascii printed string of hex digits
+/**
+ * @param [in] input The char array to print
+ * @param [in] length The length of input
+ * @return A copy of `input` converted to the printable representation of the
+ * hex values of its data.
+ */
+std::string to_hex(char const * input, size_t length);
+
+} // namespace utility
+} // namespace websocketpp
+
+#include <websocketpp/impl/utilities_impl.hpp>
+
+#endif // WEBSOCKETPP_UTILITIES_HPP
diff --git a/websocketpp/version.hpp b/websocketpp/version.hpp
new file mode 100644
index 00000000..b88cc110
--- /dev/null
+++ b/websocketpp/version.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, Peter Thorson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the WebSocket++ Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef WEBSOCKETPP_VERSION_HPP
+#define WEBSOCKETPP_VERSION_HPP
+
+/// Namespace for the WebSocket++ project
+namespace websocketpp {
+
+/*
+ other places where version information is kept
+ - readme.md
+ - changelog.md
+ - Doxyfile
+ - CMakeLists.txt
+*/
+
+/// Library major version number
+static int const major_version = 0;
+/// Library minor version number
+static int const minor_version = 7;
+/// Library patch version number
+static int const patch_version = 0;
+/// Library pre-release flag
+/**
+ * This is a textual flag indicating the type and number for pre-release
+ * versions (dev, alpha, beta, rc). This will be blank for release versions.
+ */
+
+static char const prerelease_flag[] = "";
+
+/// Default user agent string
+static char const user_agent[] = "WebSocket++/0.7.0";
+
+} // namespace websocketpp
+
+#endif // WEBSOCKETPP_VERSION_HPP