mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Squashed 'src/cpp/websocketpp/' content from commit 1ec36a4
git-subtree-dir: src/cpp/websocketpp git-subtree-split: 1ec36a47468a23f01754fa3a086874e13a4d52d9
This commit is contained in:
20
src/SConscript
Normal file
20
src/SConscript
Normal file
@@ -0,0 +1,20 @@
|
||||
## websocket++ library
|
||||
##
|
||||
|
||||
Import('env')
|
||||
|
||||
localenv = env.Clone ()
|
||||
|
||||
sources = ["base64/base64.cpp",
|
||||
"md5/md5.c",
|
||||
"messages/data.cpp",
|
||||
"network_utilities.cpp",
|
||||
"processors/hybi_header.cpp",
|
||||
"processors/hybi_util.cpp",
|
||||
"sha1/sha1.cpp",
|
||||
"uri.cpp"]
|
||||
|
||||
static_lib = localenv.StaticLibrary('websocketpp', sources)
|
||||
shared_lib = None # localenv.SharedLibrary('websocketpp', sources)
|
||||
|
||||
Return('static_lib', 'shared_lib')
|
||||
123
src/base64/base64.cpp
Normal file
123
src/base64/base64.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
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
|
||||
|
||||
*/
|
||||
|
||||
#include "base64.h"
|
||||
#include <iostream>
|
||||
|
||||
static const std::string base64_chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
|
||||
static inline bool is_base64(unsigned char c) {
|
||||
return (isalnum(c) || (c == '+') || (c == '/'));
|
||||
}
|
||||
|
||||
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
|
||||
std::string ret;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
unsigned char char_array_3[3];
|
||||
unsigned char char_array_4[4];
|
||||
|
||||
while (in_len--) {
|
||||
char_array_3[i++] = *(bytes_to_encode++);
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
std::string base64_decode(std::string const& encoded_string) {
|
||||
size_t in_len = encoded_string.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-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
|
||||
char_array_4[i++] = encoded_string[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 += char_array_3[j];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
4
src/base64/base64.h
Normal file
4
src/base64/base64.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include <string>
|
||||
|
||||
std::string base64_encode(unsigned char const* , unsigned int len);
|
||||
std::string base64_decode(std::string const& s);
|
||||
223
src/common.hpp
Normal file
223
src/common.hpp
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_CONSTANTS_HPP
|
||||
#define WEBSOCKET_CONSTANTS_HPP
|
||||
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
// SIZE_MAX appears to be a compiler thing not an OS header thing.
|
||||
// make sure it is defined.
|
||||
#ifndef SIZE_MAX
|
||||
#define SIZE_MAX ((size_t)(-1))
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#ifndef _WEBSOCKETPP_CPP11_FRIEND_
|
||||
#define _WEBSOCKETPP_CPP11_FRIEND_
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
// Defaults
|
||||
namespace websocketpp {
|
||||
static const std::string USER_AGENT = "WebSocket++/0.2.1dev";
|
||||
|
||||
typedef std::vector<unsigned char> binary_string;
|
||||
typedef boost::shared_ptr<binary_string> binary_string_ptr;
|
||||
|
||||
typedef std::string utf8_string;
|
||||
typedef boost::shared_ptr<utf8_string> utf8_string_ptr;
|
||||
|
||||
const uint64_t DEFAULT_MAX_MESSAGE_SIZE = 0xFFFFFF; // ~16MB
|
||||
|
||||
const size_t DEFAULT_READ_THRESHOLD = 1; // 512 would be a more sane value for this
|
||||
const bool DEFAULT_SILENT_CLOSE = false; // true
|
||||
|
||||
const size_t MAX_THREAD_POOL_SIZE = 64;
|
||||
|
||||
const uint16_t DEFAULT_PORT = 80;
|
||||
const uint16_t DEFAULT_SECURE_PORT = 443;
|
||||
|
||||
inline uint16_t default_port(bool secure) {
|
||||
return (secure ? DEFAULT_SECURE_PORT : DEFAULT_PORT);
|
||||
}
|
||||
|
||||
namespace session {
|
||||
namespace state {
|
||||
enum value {
|
||||
CONNECTING = 0,
|
||||
OPEN = 1,
|
||||
CLOSING = 2,
|
||||
CLOSED = 3
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
namespace close {
|
||||
namespace status {
|
||||
enum value {
|
||||
INVALID_END = 999,
|
||||
NORMAL = 1000,
|
||||
GOING_AWAY = 1001,
|
||||
PROTOCOL_ERROR = 1002,
|
||||
UNSUPPORTED_DATA = 1003,
|
||||
RSV_ADHOC_1 = 1004,
|
||||
NO_STATUS = 1005,
|
||||
ABNORMAL_CLOSE = 1006,
|
||||
INVALID_PAYLOAD = 1007,
|
||||
POLICY_VIOLATION = 1008,
|
||||
MESSAGE_TOO_BIG = 1009,
|
||||
EXTENSION_REQUIRE = 1010,
|
||||
INTERNAL_ENDPOINT_ERROR = 1011,
|
||||
RSV_ADHOC_2 = 1012,
|
||||
RSV_ADHOC_3 = 1013,
|
||||
RSV_ADHOC_4 = 1014,
|
||||
TLS_HANDSHAKE = 1015,
|
||||
RSV_START = 1016,
|
||||
RSV_END = 2999,
|
||||
INVALID_START = 5000
|
||||
};
|
||||
|
||||
inline bool reserved(value s) {
|
||||
return ((s >= RSV_START && s <= RSV_END) || s == RSV_ADHOC_1
|
||||
|| s == RSV_ADHOC_2 || s == RSV_ADHOC_3 || s == RSV_ADHOC_4);
|
||||
}
|
||||
|
||||
// Codes invalid on the wire
|
||||
inline bool invalid(value s) {
|
||||
return ((s <= INVALID_END || s >= INVALID_START) ||
|
||||
s == NO_STATUS ||
|
||||
s == ABNORMAL_CLOSE ||
|
||||
s == TLS_HANDSHAKE);
|
||||
}
|
||||
|
||||
// TODO functions for application ranges?
|
||||
} // namespace status
|
||||
} // namespace close
|
||||
|
||||
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 avaliable
|
||||
TIMEOUT_TLS = 4, // TLS handshake timed out
|
||||
TIMEOUT_WS = 5 // WS handshake timed out
|
||||
};
|
||||
} // namespace status
|
||||
} // namespace fail
|
||||
|
||||
namespace frame {
|
||||
// Opcodes are 4 bits
|
||||
// See spec 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
|
||||
};
|
||||
|
||||
inline bool reserved(value v) {
|
||||
return (v >= RSV3 && v <= RSV7) ||
|
||||
(v >= CONTROL_RSVB && v <= CONTROL_RSVF);
|
||||
}
|
||||
|
||||
inline bool invalid(value v) {
|
||||
return (v > 0xF || v < 0);
|
||||
}
|
||||
|
||||
inline bool is_control(value v) {
|
||||
return v >= 0x8;
|
||||
}
|
||||
}
|
||||
|
||||
namespace limits {
|
||||
static const uint8_t PAYLOAD_SIZE_BASIC = 125;
|
||||
static const uint16_t PAYLOAD_SIZE_EXTENDED = 0xFFFF; // 2^16, 65535
|
||||
static const uint64_t PAYLOAD_SIZE_JUMBO = 0x7FFFFFFFFFFFFFFFLL;//2^63
|
||||
}
|
||||
} // namespace frame
|
||||
|
||||
// exception class for errors that should be propogated back to the user.
|
||||
namespace error {
|
||||
enum value {
|
||||
GENERIC = 0,
|
||||
// send attempted when endpoint write queue was full
|
||||
SEND_QUEUE_FULL = 1,
|
||||
PAYLOAD_VIOLATION = 2,
|
||||
ENDPOINT_UNSECURE = 3,
|
||||
ENDPOINT_UNAVAILABLE = 4,
|
||||
INVALID_URI = 5,
|
||||
NO_OUTGOING_MESSAGES = 6,
|
||||
INVALID_STATE = 7
|
||||
};
|
||||
}
|
||||
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
exception(const std::string& msg,
|
||||
error::value code = error::GENERIC)
|
||||
: m_msg(msg),m_code(code) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
error::value code() const throw() {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
error::value m_code;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_CONSTANTS_HPP
|
||||
1605
src/connection.hpp
Normal file
1605
src/connection.hpp
Normal file
File diff suppressed because it is too large
Load Diff
606
src/endpoint.hpp
Normal file
606
src/endpoint.hpp
Normal file
@@ -0,0 +1,606 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 "connection.hpp"
|
||||
#include "sockets/autotls.hpp" // should this be here?
|
||||
#include "logger/logger.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
/// endpoint_base provides core functionality that needs to be constructed
|
||||
/// before endpoint policy classes are constructed.
|
||||
class endpoint_base {
|
||||
protected:
|
||||
/// Start the run method of the endpoint's io_service object.
|
||||
void run_internal() {
|
||||
for (;;) {
|
||||
try {
|
||||
m_io_service.run();
|
||||
break;
|
||||
} catch (const std::exception & e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boost::asio::io_service m_io_service;
|
||||
};
|
||||
|
||||
/// Describes a configurable WebSocket endpoint.
|
||||
/**
|
||||
* The endpoint class template provides a configurable WebSocket endpoint
|
||||
* capable that manages WebSocket connection lifecycles. endpoint is a host
|
||||
* class to a series of enriched policy classes that together provide the public
|
||||
* interface for a specific type of WebSocket endpoint.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Will be safe when complete.
|
||||
*/
|
||||
template <
|
||||
template <class> class role,
|
||||
template <class> class socket = socket::autotls,
|
||||
template <class> class logger = log::logger>
|
||||
class endpoint
|
||||
: public endpoint_base,
|
||||
public role< endpoint<role,socket,logger> >,
|
||||
public socket< endpoint<role,socket,logger> >
|
||||
{
|
||||
public:
|
||||
/// Type of the traits class that stores endpoint related types.
|
||||
typedef endpoint_traits< endpoint<role,socket,logger> > traits;
|
||||
|
||||
/// The type of the endpoint itself.
|
||||
typedef typename traits::type type;
|
||||
/// The type of a shared pointer to the endpoint.
|
||||
typedef typename traits::ptr ptr;
|
||||
/// The type of the role policy.
|
||||
typedef typename traits::role_type role_type;
|
||||
/// The type of the socket policy.
|
||||
typedef typename traits::socket_type socket_type;
|
||||
/// The type of the access logger based on the logger policy.
|
||||
typedef typename traits::alogger_type alogger_type;
|
||||
typedef typename traits::alogger_ptr alogger_ptr;
|
||||
/// The type of the error logger based on the logger policy.
|
||||
typedef typename traits::elogger_type elogger_type;
|
||||
typedef typename traits::elogger_ptr elogger_ptr;
|
||||
/// The type of the connection that this endpoint creates.
|
||||
typedef typename traits::connection_type connection_type;
|
||||
/// A shared pointer to the type of connection that this endpoint creates.
|
||||
typedef typename traits::connection_ptr connection_ptr;
|
||||
/// Interface (ABC) that handlers for this type of endpoint must impliment
|
||||
/// role policy and socket policy both may add methods to this interface
|
||||
typedef typename traits::handler handler;
|
||||
/// A shared pointer to the base class that all handlers for this endpoint
|
||||
/// must derive from.
|
||||
typedef typename traits::handler_ptr handler_ptr;
|
||||
|
||||
// Friend is used here to allow the CRTP base classes to access member
|
||||
// functions in the derived endpoint. This is done to limit the use of
|
||||
// public methods in endpoint and its CRTP bases to only those methods
|
||||
// intended for end-application use.
|
||||
#ifdef _WEBSOCKETPP_CPP11_FRIEND_
|
||||
// Highly simplified and preferred C++11 version:
|
||||
friend role_type;
|
||||
friend socket_type;
|
||||
friend connection_type;
|
||||
#else
|
||||
friend class role< endpoint<role,socket> >;
|
||||
friend class socket< endpoint<role,socket> >;
|
||||
friend class connection<type,role< type >::template connection,socket< type >::template connection>;
|
||||
#endif
|
||||
|
||||
|
||||
/// Construct an endpoint.
|
||||
/**
|
||||
* This constructor creates an endpoint and registers the default connection
|
||||
* handler.
|
||||
*
|
||||
* @param handler A shared_ptr to the handler to use as the default handler
|
||||
* when creating new connections.
|
||||
*/
|
||||
explicit endpoint(handler_ptr handler)
|
||||
: role_type(endpoint_base::m_io_service)
|
||||
, socket_type(endpoint_base::m_io_service)
|
||||
, m_handler(handler)
|
||||
, m_read_threshold(DEFAULT_READ_THRESHOLD)
|
||||
, m_silent_close(DEFAULT_SILENT_CLOSE)
|
||||
, m_state(IDLE)
|
||||
, m_alog(new alogger_type())
|
||||
, m_elog(new elogger_type())
|
||||
, m_pool(new message::pool<message::data>(1000))
|
||||
, m_pool_control(new message::pool<message::data>(SIZE_MAX))
|
||||
{
|
||||
m_pool->set_callback(boost::bind(&type::on_new_message,this));
|
||||
}
|
||||
|
||||
/// Destroy an endpoint
|
||||
~endpoint() {
|
||||
m_alog->at(log::alevel::DEVEL) << "Endpoint destructor called" << log::endl;
|
||||
// Tell the memory pool we don't want to be notified about newly freed
|
||||
// messages any more (because we wont be here)
|
||||
m_pool->set_callback(NULL);
|
||||
|
||||
// Detach any connections that are still alive at this point
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
typename std::set<connection_ptr>::iterator it;
|
||||
|
||||
while (!m_connections.empty()) {
|
||||
remove_connection(*m_connections.begin());
|
||||
}
|
||||
m_alog->at(log::alevel::DEVEL) << "Endpoint destructor done" << log::endl;
|
||||
}
|
||||
|
||||
// copy/assignment constructors require C++11
|
||||
// boost::noncopyable is being used in the meantime.
|
||||
// endpoint(endpoint const&) = delete;
|
||||
// endpoint& operator=(endpoint const&) = delete
|
||||
|
||||
/// Returns a reference to the endpoint's access logger.
|
||||
/**
|
||||
* Visibility: public
|
||||
* State: Any
|
||||
* Concurrency: Callable from anywhere
|
||||
*
|
||||
* @return A reference to the endpoint's access logger. See @ref logger
|
||||
* for more details about WebSocket++ logging policy classes.
|
||||
*
|
||||
* @par Example
|
||||
* To print a message to the access log of endpoint e at access level DEVEL:
|
||||
* @code
|
||||
* e.alog().at(log::alevel::DEVEL) << "message" << log::endl;
|
||||
* @endcode
|
||||
*/
|
||||
alogger_type& alog() {
|
||||
return *m_alog;
|
||||
}
|
||||
alogger_ptr alog_ptr() {
|
||||
return m_alog;
|
||||
}
|
||||
|
||||
/// Returns a reference to the endpoint's error logger.
|
||||
/**
|
||||
* @returns A reference to the endpoint's error logger. See @ref logger
|
||||
* for more details about WebSocket++ logging policy classes.
|
||||
*
|
||||
* @par Example
|
||||
* To print a message to the error log of endpoint e at access level DEVEL:
|
||||
* @code
|
||||
* e.elog().at(log::elevel::DEVEL) << "message" << log::endl;
|
||||
* @endcode
|
||||
*/
|
||||
elogger_type& elog() {
|
||||
return *m_elog;
|
||||
}
|
||||
elogger_ptr elog_ptr() {
|
||||
return m_elog;
|
||||
}
|
||||
|
||||
/// Get default handler
|
||||
/**
|
||||
* Visibility: public
|
||||
* State: valid always
|
||||
* Concurrency: callable from anywhere
|
||||
*
|
||||
* @return A pointer to the default handler
|
||||
*/
|
||||
handler_ptr get_handler() const {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
return m_handler;
|
||||
}
|
||||
|
||||
/// Sets the default handler to be used for future connections
|
||||
/**
|
||||
* Does not affect existing connections.
|
||||
*
|
||||
* @param new_handler A shared pointer to the new default handler. Must not
|
||||
* be NULL.
|
||||
*/
|
||||
void set_handler(handler_ptr new_handler) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
if (!new_handler) {
|
||||
elog().at(log::elevel::FATAL)
|
||||
<< "Tried to switch to a NULL handler." << log::endl;
|
||||
throw websocketpp::exception("TODO: handlers can't be null");
|
||||
}
|
||||
|
||||
m_handler = new_handler;
|
||||
}
|
||||
|
||||
/// Set endpoint read threshold
|
||||
/**
|
||||
* Sets the default read threshold value that will be passed to new connections.
|
||||
* Changing this value will only affect new connections, not existing ones. The read
|
||||
* threshold represents the largest block of payload bytes that will be processed in
|
||||
* a single async read. Lower values may experience better callback latency at the
|
||||
* expense of additional ASIO context switching overhead. This value also affects the
|
||||
* maximum number of bytes to be buffered before performing utf8 and other streaming
|
||||
* validation.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: valid always
|
||||
* Concurrency: callable from anywhere
|
||||
*
|
||||
* @param val Size of the threshold in bytes
|
||||
*/
|
||||
void set_read_threshold(size_t val) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
m_read_threshold = val;
|
||||
}
|
||||
|
||||
/// Get endpoint read threshold
|
||||
/**
|
||||
* Returns the endpoint read threshold. See set_read_threshold for more information
|
||||
* about the read threshold.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: valid always
|
||||
* Concurrency: callable from anywhere
|
||||
*
|
||||
* @return Size of the threshold in bytes
|
||||
* @see set_read_threshold()
|
||||
*/
|
||||
size_t get_read_threshold() const {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
return m_read_threshold;
|
||||
}
|
||||
|
||||
/// Set connection silent close setting
|
||||
/**
|
||||
* Silent close suppresses the return of detailed connection close information during
|
||||
* the closing handshake. This information is critically useful for debugging but may
|
||||
* be undesirable for security reasons for some production environments. Close reasons
|
||||
* could be used to by an attacker to confirm that the implementation is out of
|
||||
* resources or be used to identify the WebSocket library in use.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: valid always
|
||||
* Concurrency: callable from anywhere
|
||||
*
|
||||
* @param val New silent close value
|
||||
*/
|
||||
void set_silent_close(bool val) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
m_silent_close = val;
|
||||
}
|
||||
|
||||
/// Get connection silent close setting
|
||||
/**
|
||||
* Visibility: public
|
||||
* State: valid always
|
||||
* Concurrency: callable from anywhere
|
||||
*
|
||||
* @return Current silent close value
|
||||
* @see set_silent_close()
|
||||
*/
|
||||
bool get_silent_close() const {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
return m_silent_close;
|
||||
}
|
||||
|
||||
/// Cleanly closes all websocket connections
|
||||
/**
|
||||
* Sends a close signal to every connection with the specified code and
|
||||
* reason. The default code is 1001/Going Away and the default reason is
|
||||
* blank.
|
||||
*
|
||||
* @param code The WebSocket close code to send to remote clients as the
|
||||
* reason that the connection is being closed.
|
||||
* @param reason The WebSocket close reason to send to remote clients as the
|
||||
* text reason that the connection is being closed. Must be valid UTF-8.
|
||||
*/
|
||||
void close_all(close::status::value code = close::status::GOING_AWAY,
|
||||
const std::string& reason = "")
|
||||
{
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
m_alog->at(log::alevel::ENDPOINT)
|
||||
<< "Endpoint received signal to close all connections cleanly with code "
|
||||
<< code << " and reason " << reason << log::endl;
|
||||
|
||||
// TODO: is there a more elegant way to do this? In some code paths
|
||||
// close can call terminate immediately which removes the connection
|
||||
// from m_connections, invalidating the iterator.
|
||||
typename std::set<connection_ptr>::iterator it;
|
||||
|
||||
for (it = m_connections.begin(); it != m_connections.end();) {
|
||||
const connection_ptr con = *it++;
|
||||
con->close(code,reason);
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop the endpoint's ASIO loop
|
||||
/**
|
||||
* Signals the endpoint to call the io_service stop member function. If
|
||||
* clean is true the endpoint will be put into an intermediate state where
|
||||
* it signals all connections to close cleanly and only calls stop once that
|
||||
* process is complete. Otherwise stop is called immediately and all
|
||||
* io_service operations will be aborted.
|
||||
*
|
||||
* If clean is true stop will use code and reason for the close code and
|
||||
* close reason when it closes open connections. The default code is
|
||||
* 1001/Going Away and the default reason is blank.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from RUNNING only
|
||||
* Concurrency: Callable from anywhere
|
||||
*
|
||||
* @param clean Whether or not to wait until all connections have been
|
||||
* cleanly closed to stop io_service operations.
|
||||
* @param code The WebSocket close code to send to remote clients as the
|
||||
* reason that the connection is being closed.
|
||||
* @param reason The WebSocket close reason to send to remote clients as the
|
||||
* text reason that the connection is being closed. Must be valid UTF-8.
|
||||
*/
|
||||
void stop(bool clean = true,
|
||||
close::status::value code = close::status::GOING_AWAY,
|
||||
const std::string& reason = "")
|
||||
{
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
if (clean) {
|
||||
m_alog->at(log::alevel::ENDPOINT)
|
||||
<< "Endpoint is stopping cleanly" << log::endl;
|
||||
|
||||
m_state = STOPPING;
|
||||
close_all(code,reason);
|
||||
} else {
|
||||
m_alog->at(log::alevel::ENDPOINT)
|
||||
<< "Endpoint is stopping immediately" << log::endl;
|
||||
|
||||
endpoint_base::m_io_service.stop();
|
||||
m_state = STOPPED;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
/// Creates and returns a new connection
|
||||
/**
|
||||
* This function creates a new connection of the type and passes it a
|
||||
* reference to this as well as a shared pointer to the default connection
|
||||
* handler. The newly created connection is added to the endpoint's
|
||||
* management list. The endpoint will retain this pointer until
|
||||
* remove_connection is called to remove it.
|
||||
*
|
||||
* If the endpoint is in a state where it is trying to stop or has already
|
||||
* stopped an empty shared pointer is returned.
|
||||
*
|
||||
* Visibility: protected
|
||||
* State: Always valid, behavior differs based on state
|
||||
* Concurrency: Callable from anywhere
|
||||
*
|
||||
* @return A shared pointer to the newly created connection or an empty
|
||||
* shared pointer if one could not be created.
|
||||
*/
|
||||
connection_ptr create_connection() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
if (m_state == STOPPING || m_state == STOPPED) {
|
||||
return connection_ptr();
|
||||
}
|
||||
|
||||
connection_ptr new_connection(new connection_type(*this,m_handler));
|
||||
m_connections.insert(new_connection);
|
||||
|
||||
m_alog->at(log::alevel::DEVEL) << "Connection created: count is now: "
|
||||
<< m_connections.size() << log::endl;
|
||||
|
||||
return new_connection;
|
||||
}
|
||||
|
||||
/// Removes a connection from the list managed by this endpoint.
|
||||
/**
|
||||
* This function erases a connection from the list managed by the endpoint.
|
||||
* After this function returns, endpoint all async events related to this
|
||||
* connection should be canceled and neither ASIO nor this endpoint should
|
||||
* have a pointer to this connection. Unless the end user retains a copy of
|
||||
* the shared pointer the connection will be freed and any state it
|
||||
* contained (close code status, etc) will be lost.
|
||||
*
|
||||
* Visibility: protected
|
||||
* State: Always valid, behavior differs based on state
|
||||
* Concurrency: Callable from anywhere
|
||||
*
|
||||
* @param con A shared pointer to a connection created by this endpoint.
|
||||
*/
|
||||
void remove_connection(connection_ptr con) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
// TODO: is this safe to use?
|
||||
// Detaching signals to the connection that the endpoint is no longer aware of it
|
||||
// and it is no longer safe to assume the endpoint exists.
|
||||
con->detach();
|
||||
|
||||
m_connections.erase(con);
|
||||
|
||||
m_alog->at(log::alevel::DEVEL) << "Connection removed: count is now: "
|
||||
<< m_connections.size() << log::endl;
|
||||
|
||||
if (m_state == STOPPING && m_connections.empty()) {
|
||||
// If we are in the process of stopping and have reached zero
|
||||
// connections stop the io_service.
|
||||
m_alog->at(log::alevel::ENDPOINT)
|
||||
<< "Endpoint has reached zero connections in STOPPING state. Stopping io_service now."
|
||||
<< log::endl;
|
||||
stop(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a shared pointer to a read/write data message.
|
||||
// TODO: thread safety
|
||||
message::data::ptr get_data_message() {
|
||||
return m_pool->get();
|
||||
}
|
||||
|
||||
/// Gets a shared pointer to a read/write control message.
|
||||
// TODO: thread safety
|
||||
message::data::ptr get_control_message() {
|
||||
return m_pool_control->get();
|
||||
}
|
||||
|
||||
/// Asks the endpoint to restart this connection's handle_read_frame loop
|
||||
/// when there are avaliable data messages.
|
||||
void wait(connection_ptr con) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
m_read_waiting.push(con);
|
||||
m_alog->at(log::alevel::DEVEL) << "connection " << con << " is waiting. " << m_read_waiting.size() << log::endl;
|
||||
}
|
||||
|
||||
/// Message pool callback indicating that there is a free data message
|
||||
/// avaliable. Causes one waiting connection to get restarted.
|
||||
void on_new_message() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
if (!m_read_waiting.empty()) {
|
||||
connection_ptr next = m_read_waiting.front();
|
||||
|
||||
m_alog->at(log::alevel::DEVEL) << "Waking connection " << next << ". " << m_read_waiting.size()-1 << log::endl;
|
||||
|
||||
(*next).handle_read_frame(boost::system::error_code());
|
||||
m_read_waiting.pop();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
private:
|
||||
enum state {
|
||||
IDLE = 0,
|
||||
RUNNING = 1,
|
||||
STOPPING = 2,
|
||||
STOPPED = 3
|
||||
};
|
||||
|
||||
// default settings to pass to connections
|
||||
handler_ptr m_handler;
|
||||
size_t m_read_threshold;
|
||||
bool m_silent_close;
|
||||
|
||||
// other stuff
|
||||
state m_state;
|
||||
|
||||
std::set<connection_ptr> m_connections;
|
||||
alogger_ptr m_alog;
|
||||
elogger_ptr m_elog;
|
||||
|
||||
// resource pools for read/write message buffers
|
||||
message::pool<message::data>::ptr m_pool;
|
||||
message::pool<message::data>::ptr m_pool_control;
|
||||
std::queue<connection_ptr> m_read_waiting;
|
||||
|
||||
// concurrency support
|
||||
mutable boost::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
/// traits class that allows looking up relevant endpoint types by the fully
|
||||
/// defined endpoint type.
|
||||
template <
|
||||
template <class> class role,
|
||||
template <class> class socket,
|
||||
template <class> class logger>
|
||||
struct endpoint_traits< endpoint<role, socket, logger> > {
|
||||
/// The type of the endpoint itself.
|
||||
typedef endpoint<role,socket,logger> type;
|
||||
typedef boost::shared_ptr<type> ptr;
|
||||
|
||||
/// The type of the role policy.
|
||||
typedef role< type > role_type;
|
||||
/// The type of the socket policy.
|
||||
typedef socket< type > socket_type;
|
||||
|
||||
/// The type of the access logger based on the logger policy.
|
||||
typedef logger<log::alevel::value> alogger_type;
|
||||
typedef boost::shared_ptr<alogger_type> alogger_ptr;
|
||||
/// The type of the error logger based on the logger policy.
|
||||
typedef logger<log::elevel::value> elogger_type;
|
||||
typedef boost::shared_ptr<elogger_type> elogger_ptr;
|
||||
|
||||
/// The type of the connection that this endpoint creates.
|
||||
typedef connection<type,
|
||||
role< type >::template connection,
|
||||
socket< type >::template connection> connection_type;
|
||||
/// A shared pointer to the type of connection that this endpoint creates.
|
||||
typedef boost::shared_ptr<connection_type> connection_ptr;
|
||||
|
||||
class handler;
|
||||
|
||||
/// A shared pointer to the base class that all handlers for this endpoint
|
||||
/// must derive from.
|
||||
typedef boost::shared_ptr<handler> handler_ptr;
|
||||
|
||||
/// Interface (ABC) that handlers for this type of endpoint may impliment.
|
||||
/// role policy and socket policy both may add methods to this interface
|
||||
class handler : public role_type::handler_interface,
|
||||
public socket_type::handler_interface
|
||||
{
|
||||
public:
|
||||
// convenience typedefs for use in end application handlers.
|
||||
// TODO: figure out how to not duplicate the definition of connection_ptr
|
||||
typedef boost::shared_ptr<handler> ptr;
|
||||
typedef typename connection_type::ptr connection_ptr;
|
||||
typedef typename message::data::ptr message_ptr;
|
||||
|
||||
virtual ~handler() {}
|
||||
|
||||
/// on_load is the first callback called for a handler after a new
|
||||
/// connection has been transferred to it mid flight.
|
||||
/**
|
||||
* @param connection A shared pointer to the connection that was transferred
|
||||
* @param old_handler A shared pointer to the previous handler
|
||||
*/
|
||||
virtual void on_load(connection_ptr con, handler_ptr old_handler) {}
|
||||
/// on_unload is the last callback called for a handler before control
|
||||
/// of a connection is handed over to a new handler mid flight.
|
||||
/**
|
||||
* @param connection A shared pointer to the connection being transferred
|
||||
* @param old_handler A shared pointer to the new handler
|
||||
*/
|
||||
virtual void on_unload(connection_ptr con, handler_ptr new_handler) {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_ENDPOINT_HPP
|
||||
213
src/http/constants.hpp
Normal file
213
src/http/constants.hpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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
|
||||
|
||||
namespace websocketpp {
|
||||
namespace http {
|
||||
namespace status_code {
|
||||
enum value {
|
||||
CONTINUE = 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_IMPLIMENTED = 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 CONTINUE:
|
||||
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 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_IMPLIMENTED:
|
||||
return "Not Implimented";
|
||||
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 = "",
|
||||
const std::string& body = "")
|
||||
: m_msg(log_msg),
|
||||
m_error_code(error_code),
|
||||
m_error_msg(error_msg),
|
||||
m_body(body) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
status_code::value m_error_code;
|
||||
std::string m_error_msg;
|
||||
std::string m_body;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HTTP_CONSTANTS_HPP
|
||||
294
src/http/parser.hpp
Normal file
294
src/http/parser.hpp
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "constants.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace http {
|
||||
namespace parser {
|
||||
|
||||
namespace state {
|
||||
enum value {
|
||||
METHOD,
|
||||
RESOURCE,
|
||||
VERSION,
|
||||
HEADERS
|
||||
};
|
||||
}
|
||||
|
||||
typedef std::map<std::string,std::string> header_list;
|
||||
|
||||
static std::string tolower(const std::string& in)
|
||||
{
|
||||
std::string out = in;
|
||||
for (int i = 0; i < out.size(); ++i)
|
||||
if (isupper(out[i]))
|
||||
out[i] = ::tolower(out[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
class parser {
|
||||
public:
|
||||
// consumes bytes from the stream and returns true if enough bytes have
|
||||
// been read
|
||||
bool consume (std::istream&) {
|
||||
throw "Not Implemented";
|
||||
}
|
||||
|
||||
void set_version(const std::string& version) {
|
||||
// TODO: validation?
|
||||
m_version = version;
|
||||
}
|
||||
|
||||
const std::string& version() const {
|
||||
return m_version;
|
||||
}
|
||||
|
||||
std::string header(const std::string& key) const {
|
||||
header_list::const_iterator h = m_headers.find(tolower(key));
|
||||
|
||||
if (h == m_headers.end()) {
|
||||
return "";
|
||||
} else {
|
||||
return h->second;
|
||||
}
|
||||
}
|
||||
|
||||
// multiple calls to add header will result in values aggregating.
|
||||
// use replace_header if you do not want this behavior.
|
||||
void add_header(const std::string &key, const std::string &val) {
|
||||
// TODO: prevent use of reserved headers?
|
||||
if (this->header(key) == "") {
|
||||
m_headers[tolower(key)] = val;
|
||||
} else {
|
||||
m_headers[tolower(key)] += ", " + val;
|
||||
}
|
||||
}
|
||||
|
||||
void replace_header(const std::string &key, const std::string &val) {
|
||||
m_headers[tolower(key)] = val;
|
||||
}
|
||||
|
||||
void remove_header(const std::string &key) {
|
||||
m_headers.erase(tolower(key));
|
||||
}
|
||||
protected:
|
||||
bool parse_headers(std::istream& s) {
|
||||
std::string header;
|
||||
std::string::size_type end;
|
||||
|
||||
// get headers
|
||||
while (std::getline(s, header) && header != "\r") {
|
||||
if (header[header.size()-1] != '\r') {
|
||||
continue; // ignore malformed header lines?
|
||||
} else {
|
||||
header.erase(header.end()-1);
|
||||
}
|
||||
|
||||
end = header.find(": ",0);
|
||||
|
||||
if (end != std::string::npos) {
|
||||
add_header(header.substr(0,end),header.substr(end+2));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string raw_headers() {
|
||||
std::stringstream raw;
|
||||
|
||||
header_list::iterator it;
|
||||
for (it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
raw << it->first << ": " << it->second << "\r\n";
|
||||
}
|
||||
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_version;
|
||||
header_list m_headers;
|
||||
};
|
||||
|
||||
class request : public parser {
|
||||
public:
|
||||
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
|
||||
bool parse_complete(std::istream& s) {
|
||||
std::string request;
|
||||
|
||||
// get status line
|
||||
std::getline(s, request);
|
||||
|
||||
if (request[request.size()-1] == '\r') {
|
||||
request.erase(request.end()-1);
|
||||
|
||||
std::stringstream ss(request);
|
||||
std::string val;
|
||||
|
||||
ss >> val;
|
||||
set_method(val);
|
||||
|
||||
ss >> val;
|
||||
set_uri(val);
|
||||
|
||||
ss >> val;
|
||||
set_version(val);
|
||||
} else {
|
||||
set_method(request);
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse_headers(s);
|
||||
}
|
||||
|
||||
// TODO: validation. Make sure all required fields have been set?
|
||||
std::string raw() {
|
||||
std::stringstream raw;
|
||||
|
||||
raw << m_method << " " << m_uri << " " << version() << "\r\n";
|
||||
raw << raw_headers() << "\r\n";
|
||||
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
void set_method(const std::string& method) {
|
||||
// TODO: validation?
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
const std::string& method() const {
|
||||
return m_method;
|
||||
}
|
||||
|
||||
void set_uri(const std::string& uri) {
|
||||
// TODO: validation?
|
||||
m_uri = uri;
|
||||
}
|
||||
|
||||
const std::string& uri() const {
|
||||
return m_uri;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_method;
|
||||
std::string m_uri;
|
||||
};
|
||||
|
||||
class response : public parser {
|
||||
public:
|
||||
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
|
||||
bool parse_complete(std::istream& s) {
|
||||
std::string response;
|
||||
|
||||
// get status line
|
||||
std::getline(s, response);
|
||||
|
||||
if (response[response.size()-1] == '\r') {
|
||||
response.erase(response.end()-1);
|
||||
|
||||
std::stringstream ss(response);
|
||||
std::string str_val;
|
||||
int int_val;
|
||||
char char_val[256];
|
||||
|
||||
ss >> str_val;
|
||||
set_version(str_val);
|
||||
|
||||
ss >> int_val;
|
||||
ss.getline(char_val,256);
|
||||
set_status(status_code::value(int_val),std::string(char_val));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parse_headers(s);
|
||||
}
|
||||
|
||||
// TODO: validation. Make sure all required fields have been set?
|
||||
std::string raw() {
|
||||
std::stringstream raw;
|
||||
|
||||
raw << version() << " " << m_status_code << " " << m_status_msg << "\r\n";
|
||||
raw << raw_headers() << "\r\n";
|
||||
|
||||
raw << m_body;
|
||||
|
||||
return raw.str();
|
||||
}
|
||||
|
||||
void set_status(status_code::value code) {
|
||||
// TODO: validation?
|
||||
m_status_code = code;
|
||||
m_status_msg = get_string(code);
|
||||
}
|
||||
|
||||
void set_status(status_code::value code, const std::string& msg) {
|
||||
// TODO: validation?
|
||||
m_status_code = code;
|
||||
m_status_msg = msg;
|
||||
}
|
||||
|
||||
void set_body(const std::string& value) {
|
||||
if (value.size() == 0) {
|
||||
remove_header("Content-Length");
|
||||
m_body = "";
|
||||
return;
|
||||
}
|
||||
|
||||
std::stringstream foo;
|
||||
foo << value.size();
|
||||
replace_header("Content-Length", foo.str());
|
||||
m_body = value;
|
||||
}
|
||||
|
||||
status_code::value get_status_code() const {
|
||||
return m_status_code;
|
||||
}
|
||||
|
||||
const std::string& get_status_msg() const {
|
||||
return m_status_msg;
|
||||
}
|
||||
private:
|
||||
status_code::value m_status_code;
|
||||
std::string m_status_msg;
|
||||
std::string m_body;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HTTP_PARSER_HPP
|
||||
168
src/logger/logger.hpp
Normal file
168
src/logger/logger.hpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 ZS_LOGGER_HPP
|
||||
#define ZS_LOGGER_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace log {
|
||||
|
||||
namespace alevel {
|
||||
typedef uint16_t value;
|
||||
|
||||
static const value OFF = 0x0;
|
||||
|
||||
// A single line on connect with connecting ip, websocket version,
|
||||
// request resource, user agent, and the response code.
|
||||
static const value CONNECT = 0x1;
|
||||
// A single line on disconnect with wasClean status and local and remote
|
||||
// close codes and reasons.
|
||||
static const value DISCONNECT = 0x2;
|
||||
// A single line on incoming and outgoing control messages.
|
||||
static const value CONTROL = 0x4;
|
||||
// A single line on incoming and outgoing frames with full frame headers
|
||||
static const value FRAME_HEADER = 0x10;
|
||||
// Adds payloads to frame logs. Note these can be long!
|
||||
static const value FRAME_PAYLOAD = 0x20;
|
||||
// A single line on incoming and outgoing messages with metadata about type,
|
||||
// length, etc
|
||||
static const value MESSAGE_HEADER = 0x40;
|
||||
// Adds payloads to message logs. Note these can be long!
|
||||
static const value MESSAGE_PAYLOAD = 0x80;
|
||||
|
||||
// Notices about internal endpoint operations
|
||||
static const value ENDPOINT = 0x100;
|
||||
|
||||
// DEBUG values
|
||||
static const value DEBUG_HANDSHAKE = 0x8000;
|
||||
static const value DEBUG_CLOSE = 0x4000;
|
||||
static const value DEVEL = 0x2000;
|
||||
|
||||
static const value ALL = 0xFFFF;
|
||||
}
|
||||
|
||||
namespace elevel {
|
||||
typedef uint32_t value; // make these two values different types DJS
|
||||
|
||||
static const value OFF = 0x0;
|
||||
|
||||
static const value DEVEL = 0x1; // debugging
|
||||
static const value LIBRARY = 0x2; // library usage exceptions
|
||||
static const value INFO = 0x4; //
|
||||
static const value WARN = 0x8; //
|
||||
static const value RERROR = 0x10; // recoverable error
|
||||
static const value FATAL = 0x20; // unrecoverable error
|
||||
|
||||
static const value ALL = 0xFFFF;
|
||||
}
|
||||
|
||||
extern void websocketLog(alevel::value, const std::string&);
|
||||
extern void websocketLog(elevel::value, const std::string&);
|
||||
|
||||
template <typename level_type>
|
||||
class logger {
|
||||
public:
|
||||
template <typename T>
|
||||
logger<level_type>& operator<<(T a) {
|
||||
m_oss << a; // For now, make this unconditional DJS
|
||||
return *this;
|
||||
}
|
||||
|
||||
logger<level_type>& operator<<(logger<level_type>& (*f)(logger<level_type>&)) {
|
||||
return f(*this);
|
||||
}
|
||||
|
||||
bool test_level(level_type l) {
|
||||
return (m_level & l) != 0;
|
||||
}
|
||||
|
||||
void set_level(level_type l) {
|
||||
m_level |= l;
|
||||
}
|
||||
|
||||
void set_levels(level_type l1, level_type l2) {
|
||||
level_type i = l1;
|
||||
|
||||
while (i <= l2) {
|
||||
set_level(i);
|
||||
i *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void unset_level(level_type l) {
|
||||
m_level &= ~l;
|
||||
}
|
||||
|
||||
void set_prefix(const std::string& prefix) {
|
||||
if (prefix == "") {
|
||||
m_prefix = prefix;
|
||||
} else {
|
||||
m_prefix = prefix + " ";
|
||||
}
|
||||
}
|
||||
|
||||
logger<level_type>& print() {
|
||||
websocketLog(m_write_level, m_oss.str()); // Hand to our logger DJS
|
||||
m_oss.str("");
|
||||
#if 0
|
||||
if (test_level(m_write_level)) {
|
||||
std::cout << m_prefix <<
|
||||
boost::posix_time::to_iso_extended_string(
|
||||
boost::posix_time::second_clock::local_time()
|
||||
) << " [" << m_write_level << "] " << m_oss.str() << std::endl;
|
||||
m_oss.str("");
|
||||
}
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
|
||||
logger<level_type>& at(level_type l) {
|
||||
m_write_level = l;
|
||||
return *this;
|
||||
}
|
||||
private:
|
||||
std::ostringstream m_oss;
|
||||
level_type m_write_level;
|
||||
level_type m_level;
|
||||
std::string m_prefix;
|
||||
};
|
||||
|
||||
template <typename level_type>
|
||||
logger<level_type>& endl(logger<level_type>& out)
|
||||
{
|
||||
return out.print();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ZS_LOGGER_HPP
|
||||
381
src/md5/md5.c
Normal file
381
src/md5/md5.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 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.c,v 1.6 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.c 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 Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <string.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
# define BYTE_ORDER 0
|
||||
#endif
|
||||
|
||||
#define T_MASK ((md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
|
||||
static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *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 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];
|
||||
const md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if 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 const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if 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 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 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 F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + F(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, 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 G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, 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 H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, 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 I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, 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*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes)
|
||||
{
|
||||
const md5_byte_t *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 : nbytes);
|
||||
|
||||
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)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t 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));
|
||||
}
|
||||
93
src/md5/md5.h
Normal file
93
src/md5/md5.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
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 md5_INCLUDED
|
||||
# define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* 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>
|
||||
|
||||
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;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
void md5_init(md5_state_t *pms);
|
||||
|
||||
/* Append a string to the message. */
|
||||
void md5_append(md5_state_t *pms, const md5_byte_t *data, size_t nbytes);
|
||||
|
||||
/* Finish the message and return the digest. */
|
||||
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
||||
69
src/md5/md5.hpp
Normal file
69
src/md5/md5.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_MD5_WRAPPER_HPP
|
||||
#define WEBSOCKETPP_MD5_WRAPPER_HPP
|
||||
|
||||
#include "md5.h"
|
||||
#include <string>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
// could be compiled separately
|
||||
inline std::string md5_hash_string(const std::string& s) {
|
||||
char digest[16];
|
||||
|
||||
md5_state_t state;
|
||||
|
||||
md5_init(&state);
|
||||
md5_append(&state, (const md5_byte_t *)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(const std::string& 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;
|
||||
}
|
||||
|
||||
} // websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_MD5_WRAPPER_HPP
|
||||
182
src/messages/control.hpp
Normal file
182
src/messages/control.hpp
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_CONTROL_MESSAGE_HPP
|
||||
#define WEBSOCKET_CONTROL_MESSAGE_HPP
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../processors/processor.hpp"
|
||||
#include "../websocket_frame.hpp"
|
||||
#include "../utf8_validator/utf8_validator.hpp"
|
||||
#include "../processors/hybi_util.hpp"
|
||||
|
||||
using websocketpp::processor::hybi_util::circshift_prepared_key;
|
||||
|
||||
namespace websocketpp {
|
||||
namespace message {
|
||||
|
||||
class control {
|
||||
public:
|
||||
control() {
|
||||
m_payload.reserve(PAYLOAD_SIZE_INIT);
|
||||
}
|
||||
|
||||
frame::opcode::value get_opcode() const {
|
||||
return m_opcode;
|
||||
};
|
||||
|
||||
const std::string& get_payload() const {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
void process_payload(char *input,uint64_t size) {
|
||||
const size_t new_size = static_cast<size_t>(m_payload.size() + size);
|
||||
|
||||
if (new_size > PAYLOAD_SIZE_MAX) {
|
||||
throw processor::exception("Message payload was too large.",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
if (m_masked) {
|
||||
// this retrieves ceiling of size / word size
|
||||
size_t n = static_cast<size_t>((size + sizeof(size_t) - 1) / sizeof(size_t));
|
||||
|
||||
// reinterpret the input as an array of word sized integers
|
||||
size_t* data = reinterpret_cast<size_t*>(input);
|
||||
|
||||
// unmask working buffer
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
data[i] ^= m_prepared_key;
|
||||
}
|
||||
|
||||
// circshift working key
|
||||
m_prepared_key = circshift_prepared_key(m_prepared_key, size%4);
|
||||
}
|
||||
|
||||
// copy working buffer into
|
||||
m_payload.append(input, static_cast<size_t>(size));
|
||||
}
|
||||
|
||||
void complete() {
|
||||
if (m_opcode == frame::opcode::CLOSE) {
|
||||
if (m_payload.size() == 1) {
|
||||
throw processor::exception("Single byte close code",processor::error::PROTOCOL_VIOLATION);
|
||||
} else if (m_payload.size() >= 2) {
|
||||
close::status::value code = close::status::value(get_raw_close_code());
|
||||
|
||||
if (close::status::invalid(code)) {
|
||||
throw processor::exception("Close code is not allowed on the wire.",processor::error::PROTOCOL_VIOLATION);
|
||||
} else if (close::status::reserved(code)) {
|
||||
throw processor::exception("Close code is reserved.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
}
|
||||
if (m_payload.size() > 2) {
|
||||
if (!m_validator.decode(m_payload.begin()+2,m_payload.end())) {
|
||||
throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
if (!m_validator.complete()) {
|
||||
throw processor::exception("Invalid UTF8",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset(frame::opcode::value opcode, uint32_t masking_key) {
|
||||
m_opcode = opcode;
|
||||
set_masking_key(masking_key);
|
||||
m_payload.resize(0);
|
||||
m_validator.reset();
|
||||
}
|
||||
|
||||
close::status::value get_close_code() const {
|
||||
if (m_payload.size() == 0) {
|
||||
return close::status::NO_STATUS;
|
||||
} else {
|
||||
return close::status::value(get_raw_close_code());
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_close_reason() const {
|
||||
if (m_payload.size() > 2) {
|
||||
return m_payload.substr(2);
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
void set_masking_key(int32_t key) {
|
||||
m_masking_key.i = key;
|
||||
m_prepared_key = processor::hybi_util::prepare_masking_key(m_masking_key);
|
||||
m_masked = true;
|
||||
}
|
||||
private:
|
||||
uint16_t get_raw_close_code() const {
|
||||
if (m_payload.size() <= 1) {
|
||||
throw processor::exception("get_raw_close_code called with invalid size",processor::error::FATAL_ERROR);
|
||||
}
|
||||
|
||||
union {uint16_t i;char c[2];} val;
|
||||
|
||||
val.c[0] = m_payload[0];
|
||||
val.c[1] = m_payload[1];
|
||||
|
||||
return ntohs(val.i);
|
||||
}
|
||||
|
||||
static const uint64_t PAYLOAD_SIZE_INIT = 128; // 128B
|
||||
static const uint64_t PAYLOAD_SIZE_MAX = 128; // 128B
|
||||
|
||||
typedef websocketpp::processor::hybi_util::masking_key_type masking_key_type;
|
||||
|
||||
union masking_key {
|
||||
int32_t i;
|
||||
char c[4];
|
||||
};
|
||||
|
||||
// Message state
|
||||
frame::opcode::value m_opcode;
|
||||
|
||||
// UTF8 validation state
|
||||
utf8_validator::validator m_validator;
|
||||
|
||||
// Masking state
|
||||
masking_key_type m_masking_key;
|
||||
bool m_masked;
|
||||
size_t m_prepared_key;
|
||||
|
||||
// Message payload
|
||||
std::string m_payload;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<control> control_ptr;
|
||||
|
||||
} // namespace message
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_CONTROL_MESSAGE_HPP
|
||||
221
src/messages/data.cpp
Normal file
221
src/messages/data.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "data.hpp"
|
||||
|
||||
#include "../processors/processor.hpp"
|
||||
#include "../processors/hybi_header.hpp"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif // #ifdef max
|
||||
|
||||
using websocketpp::message::data;
|
||||
using websocketpp::processor::hybi_util::circshift_prepared_key;
|
||||
|
||||
data::data(data::pool_ptr p, size_t s) : m_prepared(false),m_index(s),m_ref_count(0),m_pool(p),m_live(false) {
|
||||
m_payload.reserve(PAYLOAD_SIZE_INIT);
|
||||
}
|
||||
|
||||
websocketpp::frame::opcode::value data::get_opcode() const {
|
||||
return m_opcode;
|
||||
}
|
||||
|
||||
const std::string& data::get_payload() const {
|
||||
return m_payload;
|
||||
}
|
||||
const std::string& data::get_header() const {
|
||||
return m_header;
|
||||
}
|
||||
|
||||
// input must be a buffer with size divisible by the machine word_size and at
|
||||
// least ceil(size/word_size)*word_size bytes long.
|
||||
void data::process_payload(char *input, size_t size) {
|
||||
//std::cout << "data message processing: " << size << " bytes" << std::endl;
|
||||
|
||||
const size_t new_size = m_payload.size() + size;
|
||||
|
||||
if (new_size > PAYLOAD_SIZE_MAX) {
|
||||
throw processor::exception("Message too big",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
if (new_size > m_payload.capacity()) {
|
||||
m_payload.reserve(std::max(new_size, 2*m_payload.capacity()));
|
||||
}
|
||||
|
||||
if (m_masked) {
|
||||
//std::cout << "message is masked" << std::endl;
|
||||
|
||||
//std::cout << "before: " << zsutil::to_hex(input, size) << std::endl;
|
||||
|
||||
// this retrieves ceiling of size / word size
|
||||
size_t n = (size + sizeof(size_t) - 1) / sizeof(size_t);
|
||||
|
||||
// reinterpret the input as an array of word sized integers
|
||||
size_t* data = reinterpret_cast<size_t*>(input);
|
||||
|
||||
// unmask working buffer
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
data[i] ^= m_prepared_key;
|
||||
}
|
||||
|
||||
//std::cout << "after: " << zsutil::to_hex(input, size) << std::endl;
|
||||
|
||||
// circshift working key
|
||||
//std::cout << "circshift by : " << size%4 << " bytes " << zsutil::to_hex(reinterpret_cast<char*>(&m_prepared_key),sizeof(size_t));
|
||||
m_prepared_key = circshift_prepared_key(m_prepared_key, size%4);
|
||||
//std::cout << " to " << zsutil::to_hex(reinterpret_cast<char*>(&m_prepared_key),sizeof(size_t)) << std::endl;
|
||||
}
|
||||
|
||||
if (m_opcode == frame::opcode::TEXT) {
|
||||
if (!m_validator.decode(input, input+size)) {
|
||||
throw processor::exception("Invalid UTF8 data",
|
||||
processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
// copy working buffer into
|
||||
//std::cout << "before: " << m_payload.size() << std::endl;
|
||||
|
||||
m_payload.append(input, size);
|
||||
|
||||
//std::cout << "after: " << m_payload.size() << std::endl;
|
||||
}
|
||||
|
||||
void data::reset(websocketpp::frame::opcode::value opcode) {
|
||||
m_opcode = opcode;
|
||||
m_masked = false;
|
||||
m_payload.clear();
|
||||
m_validator.reset();
|
||||
m_prepared = false;
|
||||
}
|
||||
|
||||
void data::complete() {
|
||||
if (m_opcode == frame::opcode::TEXT) {
|
||||
if (!m_validator.complete()) {
|
||||
throw processor::exception("Invalid UTF8 data",
|
||||
processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void data::validate_payload() {
|
||||
if (m_opcode == frame::opcode::TEXT) {
|
||||
if (!m_validator.decode(m_payload.begin(), m_payload.end())) {
|
||||
throw exception("Invalid UTF8 data",error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
|
||||
if (!m_validator.complete()) {
|
||||
throw exception("Invalid UTF8 data",error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void data::set_masking_key(int32_t key) {
|
||||
m_masking_key.i = key;
|
||||
m_prepared_key = processor::hybi_util::prepare_masking_key(m_masking_key);
|
||||
m_masked = true;
|
||||
}
|
||||
|
||||
void data::set_prepared(bool b) {
|
||||
m_prepared = b;
|
||||
}
|
||||
|
||||
bool data::get_prepared() const {
|
||||
return m_prepared;
|
||||
}
|
||||
|
||||
// This could be further optimized using methods that write directly into the
|
||||
// m_payload buffer
|
||||
void data::set_payload(const std::string& payload) {
|
||||
m_payload.reserve(payload.size());
|
||||
m_payload = payload;
|
||||
}
|
||||
void data::append_payload(const std::string& payload) {
|
||||
m_payload.reserve(m_payload.size()+payload.size());
|
||||
m_payload.append(payload);
|
||||
}
|
||||
void data::mask() {
|
||||
if (m_masked && m_payload.size() > 0) {
|
||||
// By default WebSocket++ performs block masking/unmasking in a mannor that makes
|
||||
// some assumptions about the nature of the machine and STL library used. In
|
||||
// particular the assumption is either a 32 or 64 bit word size and an STL with
|
||||
// std::string::data returning a contiguous char array.
|
||||
//
|
||||
// This method improves performance by 3-8x depending on the ratio of small to
|
||||
// large messages and the availability of a 64 bit processor.
|
||||
//
|
||||
// To disable this optimization (for use with alternative STL implementations or
|
||||
// processors) define WEBSOCKETPP_STRICT_MASKING when compiling the library. This
|
||||
// will force the library to perform masking in single byte chunks.
|
||||
//#define WEBSOCKETPP_STRICT_MASKING
|
||||
|
||||
#ifdef WEBSOCKETPP_STRICT_MASKING
|
||||
size_t len = m_payload.size();
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
m_payload[i] ^= m_masking_key.c[i%4];
|
||||
}
|
||||
#else
|
||||
// This should trigger a write to the string in case the STL
|
||||
// implimentation is copy-on-write and hasn't been written to yet.
|
||||
// Performing the masking will always require a copy of the string
|
||||
// in this case to hold the masked version.
|
||||
m_payload[0] = m_payload[0];
|
||||
|
||||
size_t size = m_payload.size()/sizeof(size_t);
|
||||
size_t key = m_masking_key.i;
|
||||
size_t wordSize = sizeof(size_t);
|
||||
if (wordSize == 8) {
|
||||
key <<= 32;
|
||||
key |= (static_cast<size_t>(m_masking_key.i) & 0x00000000FFFFFFFFLL);
|
||||
}
|
||||
size_t* data = reinterpret_cast<size_t*>(const_cast<char*>(m_payload.data()));
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
data[i] ^= key;
|
||||
}
|
||||
for (size_t i = size*sizeof(size_t); i < m_payload.size(); i++) {
|
||||
m_payload[i] ^= m_masking_key.c[i%4];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void data::set_header(const std::string& header) {
|
||||
m_header = header;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
void data::set_live() {
|
||||
m_live = true;
|
||||
}
|
||||
size_t data::get_index() const {
|
||||
return m_index;
|
||||
}
|
||||
274
src/messages/data.hpp
Normal file
274
src/messages/data.hpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_DATA_MESSAGE_HPP
|
||||
#define WEBSOCKET_DATA_MESSAGE_HPP
|
||||
|
||||
#include "../common.hpp"
|
||||
#include "../utf8_validator/utf8_validator.hpp"
|
||||
#include "../processors/hybi_util.hpp"
|
||||
|
||||
#include <boost/detail/atomic_count.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <boost/version.hpp>
|
||||
#if BOOST_VERSION >= 105300 // 1.53.00
|
||||
#include <boost/thread/lock_guard.hpp>
|
||||
#endif
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace message {
|
||||
|
||||
/// message::pool impliments a reference counted pool of elements.
|
||||
|
||||
// element_type interface:
|
||||
// constructor(ptr p, size_t index)
|
||||
// - shared pointer to the managing pool
|
||||
// - integer index
|
||||
|
||||
// get_index();
|
||||
// set_live()
|
||||
|
||||
template <class element_type>
|
||||
class pool : public boost::enable_shared_from_this< pool<element_type> >,
|
||||
boost::noncopyable {
|
||||
public:
|
||||
typedef pool<element_type> type;
|
||||
typedef boost::shared_ptr<type> ptr;
|
||||
typedef boost::weak_ptr<type> weak_ptr;
|
||||
typedef typename element_type::ptr element_ptr;
|
||||
typedef boost::function<void()> callback_type;
|
||||
|
||||
pool(size_t max_elements) : m_cur_elements(0),m_max_elements(max_elements) {}
|
||||
~pool() {}
|
||||
|
||||
// copy/assignment constructors require C++11
|
||||
// boost::noncopyable is being used in the meantime.
|
||||
// pool(pool const&) = delete;
|
||||
// pool& operator=(pool const&) = delete
|
||||
|
||||
/// Requests a pointer to the next free element in the resource pool.
|
||||
/* If there isn't a free element a new one is created. If the maximum number
|
||||
* of elements has been created then it returns an empty/null element
|
||||
* pointer.
|
||||
*/
|
||||
element_ptr get() {
|
||||
element_ptr p, q;
|
||||
{
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
/*std::cout << "message requested ("
|
||||
<< m_cur_elements-m_avaliable.size()
|
||||
<< "/"
|
||||
<< m_cur_elements
|
||||
<< ")"
|
||||
<< std::endl;*/
|
||||
|
||||
if (!m_avaliable.empty()) {
|
||||
p = m_avaliable.front(); // FIXME can call intrusive_ptr_add_ref (line 215) which can deadlock
|
||||
q = p;
|
||||
m_avaliable.pop(); // FIXME can call intrusive_ptr_release (line 217) which can deadlock
|
||||
m_used[p->get_index()] = p;
|
||||
} else {
|
||||
if (m_cur_elements == m_max_elements) {
|
||||
return element_ptr();
|
||||
}
|
||||
|
||||
p = element_ptr(new element_type(type::shared_from_this(),m_cur_elements));
|
||||
m_cur_elements++;
|
||||
m_used.push_back(p);
|
||||
|
||||
/*std::cout << "Allocated new data message. Count is now "
|
||||
<< m_cur_elements
|
||||
<< std::endl;*/
|
||||
}
|
||||
|
||||
p->set_live();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
void recycle(element_ptr p) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
|
||||
if (p->get_index()+1 > m_used.size() || m_used[p->get_index()] != p) {
|
||||
//std::cout << "error tried to recycle a pointer we don't control" << std::endl;
|
||||
// error tried to recycle a pointer we don't control
|
||||
return;
|
||||
}
|
||||
|
||||
m_avaliable.push(p);
|
||||
m_used[p->get_index()] = element_ptr();
|
||||
|
||||
/*std::cout << "message recycled ("
|
||||
<< m_cur_elements-m_avaliable.size()
|
||||
<< "/"
|
||||
<< m_cur_elements
|
||||
<< ")"
|
||||
<< std::endl;*/
|
||||
|
||||
if (m_callback && m_avaliable.size() == 1) {
|
||||
m_callback();
|
||||
}
|
||||
}
|
||||
|
||||
// set a function that will be called when new elements are avaliable.
|
||||
void set_callback(callback_type fn) {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_lock);
|
||||
m_callback = fn;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_cur_elements;
|
||||
size_t m_max_elements;
|
||||
|
||||
std::queue<element_ptr> m_avaliable;
|
||||
std::vector<element_ptr> m_used;
|
||||
|
||||
callback_type m_callback;
|
||||
|
||||
boost::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
class data {
|
||||
public:
|
||||
typedef boost::intrusive_ptr<data> ptr;
|
||||
typedef pool<data>::ptr pool_ptr;
|
||||
typedef pool<data>::weak_ptr pool_weak_ptr;
|
||||
|
||||
data(pool_ptr p, size_t s);
|
||||
|
||||
void reset(websocketpp::frame::opcode::value opcode);
|
||||
|
||||
frame::opcode::value get_opcode() const;
|
||||
const std::string& get_payload() const;
|
||||
const std::string& get_header() const;
|
||||
|
||||
// ##reading##
|
||||
// sets the masking key to be used to unmask as bytes are read.
|
||||
void set_masking_key(int32_t key);
|
||||
|
||||
// read at most size bytes from a payload stream and perform unmasking/utf8
|
||||
// validation. Returns number of bytes read.
|
||||
// throws a processor::exception if the message is too big, there is a fatal
|
||||
// istream read error, or invalid UTF8 data is read for a text message
|
||||
//uint64_t process_payload(std::istream& input,uint64_t size);
|
||||
void process_payload(char * input, size_t size);
|
||||
void complete();
|
||||
void validate_payload();
|
||||
|
||||
// ##writing##
|
||||
// sets the payload to payload. Performs max size and UTF8 validation
|
||||
// immediately and throws processor::exception if it fails
|
||||
void set_payload(const std::string& payload);
|
||||
void append_payload(const std::string& payload);
|
||||
|
||||
void set_header(const std::string& header);
|
||||
|
||||
// Performs masking and header generation if it has not been done already.
|
||||
void set_prepared(bool b);
|
||||
bool get_prepared() const;
|
||||
void mask();
|
||||
|
||||
int32_t get_masking_key() const {
|
||||
return m_masking_key.i;
|
||||
}
|
||||
|
||||
// pool management interface
|
||||
void set_live();
|
||||
size_t get_index() const;
|
||||
private:
|
||||
static const uint64_t PAYLOAD_SIZE_INIT = 1000; // 1KB
|
||||
static const uint64_t PAYLOAD_SIZE_MAX = 100000000;// 100MB
|
||||
|
||||
typedef websocketpp::processor::hybi_util::masking_key_type masking_key_type;
|
||||
|
||||
friend void intrusive_ptr_add_ref(const data * s) {
|
||||
boost::unique_lock<boost::recursive_mutex> lock(s->m_lock);
|
||||
++s->m_ref_count;
|
||||
}
|
||||
|
||||
friend void intrusive_ptr_release(const data * s) {
|
||||
boost::unique_lock<boost::recursive_mutex> lock(s->m_lock);
|
||||
|
||||
// TODO: thread safety
|
||||
long count = --s->m_ref_count;
|
||||
if (count == 1 && s->m_live) {
|
||||
// recycle if endpoint exists
|
||||
s->m_live = false;
|
||||
|
||||
pool_ptr pp = s->m_pool.lock();
|
||||
if (pp) {
|
||||
lock.unlock();
|
||||
pp->recycle(ptr(const_cast<data *>(s)));
|
||||
}
|
||||
|
||||
//s->m_pool->recycle(ptr(const_cast<data *>(s)));
|
||||
} else if (count == 0) {
|
||||
lock.unlock();
|
||||
boost::checked_delete(static_cast<data const *>(s));
|
||||
}
|
||||
}
|
||||
|
||||
// Message state
|
||||
frame::opcode::value m_opcode;
|
||||
|
||||
// UTF8 validation state
|
||||
utf8_validator::validator m_validator;
|
||||
|
||||
// Masking state
|
||||
masking_key_type m_masking_key;
|
||||
bool m_masked;
|
||||
size_t m_prepared_key;
|
||||
|
||||
std::string m_header;
|
||||
std::string m_payload;
|
||||
|
||||
bool m_prepared;
|
||||
|
||||
// reference counting
|
||||
size_t m_index;
|
||||
mutable boost::detail::atomic_count m_ref_count;
|
||||
mutable pool_weak_ptr m_pool;
|
||||
mutable bool m_live;
|
||||
mutable boost::recursive_mutex m_lock;
|
||||
};
|
||||
|
||||
typedef boost::intrusive_ptr<data> data_ptr;
|
||||
|
||||
} // namespace message
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_DATA_MESSAGE_HPP
|
||||
112
src/network_utilities.cpp
Normal file
112
src/network_utilities.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "network_utilities.hpp"
|
||||
|
||||
|
||||
|
||||
uint64_t zsutil::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;
|
||||
}
|
||||
|
||||
uint64_t zsutil::ntohll(uint64_t src) {
|
||||
return htonll(src);
|
||||
}
|
||||
|
||||
std::string zsutil::lookup_ws_close_status_string(uint16_t code) {
|
||||
switch (code) {
|
||||
case 1000:
|
||||
return "Normal closure";
|
||||
case 1001:
|
||||
return "Going away";
|
||||
case 1002:
|
||||
return "Protocol error";
|
||||
case 1003:
|
||||
return "Unacceptable data";
|
||||
case 1004:
|
||||
return "Reserved";
|
||||
case 1005:
|
||||
return "No status received";
|
||||
case 1006:
|
||||
return "Abnormal closure";
|
||||
case 1007:
|
||||
return "Invalid message data";
|
||||
case 1008:
|
||||
return "Policy Violation";
|
||||
case 1009:
|
||||
return "Message too large";
|
||||
case 1010:
|
||||
return "Missing required extensions";
|
||||
case 1011:
|
||||
return "Internal server error";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::string zsutil::to_hex(const std::string& 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;
|
||||
}
|
||||
|
||||
std::string zsutil::to_hex(const char* 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;
|
||||
}
|
||||
54
src/network_utilities.hpp
Normal file
54
src/network_utilities.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 NETWORK_UTILITIES_HPP
|
||||
#define NETWORK_UTILITIES_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
// http://www.viva64.com/en/k/0018/
|
||||
// TODO: impliment stuff from here:
|
||||
// http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c
|
||||
|
||||
namespace zsutil {
|
||||
|
||||
#define TYP_INIT 0
|
||||
#define TYP_SMLE 1
|
||||
#define TYP_BIGE 2
|
||||
|
||||
uint64_t htonll(uint64_t src);
|
||||
uint64_t ntohll(uint64_t src);
|
||||
|
||||
std::string lookup_ws_close_status_string(uint16_t code);
|
||||
|
||||
std::string to_hex(const std::string& input);
|
||||
std::string to_hex(const char* input,size_t length);
|
||||
|
||||
} // namespace zsutil
|
||||
|
||||
#endif // NETWORK_UTILITIES_HPP
|
||||
630
src/processors/hybi.hpp
Normal file
630
src/processors/hybi.hpp
Normal file
@@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
#include "hybi_header.hpp"
|
||||
|
||||
#include "../base64/base64.h"
|
||||
#include "../sha1/sha1.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#ifdef IGNORE
|
||||
#undef IGNORE
|
||||
#endif // #ifdef IGNORE
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif // #ifdef min
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace hybi_state {
|
||||
enum value {
|
||||
READ_HEADER = 0,
|
||||
READ_PAYLOAD = 1,
|
||||
READY = 2,
|
||||
IGNORE = 3
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
client case
|
||||
end user asks connection for next message
|
||||
- connection returns the next avaliable message or throws if none are ready
|
||||
- connection resets the message and fills in a new masking key
|
||||
end user calls set payload with const ref string to final payload
|
||||
- set opcode... argument to set payload?
|
||||
- set payload checks utf8 if copies from source, masks, and stores in payload.
|
||||
|
||||
|
||||
prepare (hybi):
|
||||
- writes header (writes opcode, payload length, masking key, fin bit, mask bit)
|
||||
|
||||
|
||||
server case
|
||||
end user asks connection for next message
|
||||
- connection returns the next avaliable message or throws if none are ready
|
||||
- connection resets the message and sets masking to off.
|
||||
end user calls set payload with const ref string to final payload
|
||||
- std::copy msg to payload
|
||||
|
||||
prepare
|
||||
- writes header (writes opcode, payload length, fin bit, mask bit)
|
||||
|
||||
|
||||
int reference_count
|
||||
std::list< std::pair< std::string,std::string > >
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*class hybi_message {
|
||||
public:
|
||||
hybi_message(frame::opcode::value opcode) : m_processed(false) {
|
||||
|
||||
}
|
||||
|
||||
void reset() {
|
||||
|
||||
}
|
||||
private:
|
||||
bool m_processed;
|
||||
std::string m_header;
|
||||
std::string m_payload;
|
||||
};*/
|
||||
|
||||
// connection must provide:
|
||||
// int32_t get_rng();
|
||||
// message::data_ptr get_data_message();
|
||||
// message::control_ptr get_control_message();
|
||||
// bool is_secure();
|
||||
|
||||
template <class connection_type>
|
||||
class hybi : public processor_base {
|
||||
public:
|
||||
hybi(connection_type &connection)
|
||||
: m_connection(connection),
|
||||
m_write_frame(connection)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void validate_handshake(const http::parser::request& request) const {
|
||||
std::stringstream err;
|
||||
std::string h;
|
||||
|
||||
if (request.method() != "GET") {
|
||||
err << "Websocket handshake has invalid method: "
|
||||
<< request.method();
|
||||
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
// TODO: allow versions greater than 1.1
|
||||
if (request.version() != "HTTP/1.1") {
|
||||
err << "Websocket handshake has invalid HTTP version: "
|
||||
<< request.method();
|
||||
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
// verify the presence of required headers
|
||||
if (request.header("Host") == "") {
|
||||
throw(http::exception("Required Host header is missing",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Upgrade");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Upgrade header is missing",http::status_code::BAD_REQUEST));
|
||||
} else if (!boost::ifind_first(h,"websocket")) {
|
||||
err << "Upgrade header \"" << h << "\", does not contain required token \"websocket\"";
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Connection");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Connection header is missing",http::status_code::BAD_REQUEST));
|
||||
} else if (!boost::ifind_first(h,"upgrade")) {
|
||||
err << "Connection header, \"" << h
|
||||
<< "\", does not contain required token \"upgrade\"";
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
if (request.header("Sec-WebSocket-Key") == "") {
|
||||
throw(http::exception("Required Sec-WebSocket-Key header is missing",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
h = request.header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
throw(http::exception("Required Sec-WebSocket-Version header is missing",http::status_code::BAD_REQUEST));
|
||||
} else {
|
||||
int version = atoi(h.c_str());
|
||||
|
||||
if (version != 7 && version != 8 && version != 13) {
|
||||
err << "This processor doesn't support WebSocket protocol version "
|
||||
<< version;
|
||||
throw(http::exception(err.str(),http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_origin(const http::parser::request& request) const {
|
||||
std::string h = request.header("Sec-WebSocket-Version");
|
||||
int version = atoi(h.c_str());
|
||||
|
||||
if (version == 13) {
|
||||
return request.header("Origin");
|
||||
} else if (version == 7 || version == 8) {
|
||||
return request.header("Sec-WebSocket-Origin");
|
||||
} else {
|
||||
throw(http::exception("Could not determine origin header. Check Sec-WebSocket-Version header",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const http::parser::request& request) const {
|
||||
std::string h = request.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 uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
|
||||
} else {
|
||||
return uri_ptr(new uri(m_connection.is_secure(),
|
||||
h.substr(0,last_colon),
|
||||
h.substr(last_colon+1),
|
||||
request.uri()));
|
||||
}
|
||||
|
||||
// TODO: check if get_uri is a full uri
|
||||
}
|
||||
|
||||
void handshake_response(const http::parser::request& request,
|
||||
http::parser::response& response)
|
||||
{
|
||||
std::string server_key = request.header("Sec-WebSocket-Key");
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (sha.Result(message_digest)){
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>
|
||||
(message_digest),20
|
||||
);
|
||||
|
||||
// set handshake accept headers
|
||||
response.replace_header("Sec-WebSocket-Accept",server_key);
|
||||
response.add_header("Upgrade","websocket");
|
||||
response.add_header("Connection","Upgrade");
|
||||
} else {
|
||||
//m_endpoint->elog().at(log::elevel::RERROR)
|
||||
//<< "Error computing handshake sha1 hash" << log::endl;
|
||||
// TODO: make sure this error path works
|
||||
response.set_status(http::status_code::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void consume(std::istream& s) {
|
||||
while (s.good() && m_state != hybi_state::READY) {
|
||||
try {
|
||||
switch (m_state) {
|
||||
case hybi_state::READ_HEADER:
|
||||
process_header(s);
|
||||
break;
|
||||
case hybi_state::READ_PAYLOAD:
|
||||
process_payload(s);
|
||||
break;
|
||||
case hybi_state::READY:
|
||||
// shouldn't be here..
|
||||
break;
|
||||
case hybi_state::IGNORE:
|
||||
s.ignore(m_payload_left);
|
||||
m_payload_left -= static_cast<size_t>(s.gcount());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
reset();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const processor::exception& e) {
|
||||
if (e.code() != processor::error::OUT_OF_MESSAGES) {
|
||||
// The out of messages exception acts as an inturrupt rather
|
||||
// than an error. In that case we don't want to reset
|
||||
// processor state. In all other cases we are aborting
|
||||
// processing of the message in flight and want to reset the
|
||||
// processor for a new message.
|
||||
if (m_header.ready()) {
|
||||
m_header.reset();
|
||||
ignore();
|
||||
}
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sends the processor an inturrupt signal instructing it to ignore the next
|
||||
// num bytes and then reset itself. This is used to flush a bad frame out of
|
||||
// the read buffer.
|
||||
void ignore() {
|
||||
m_state = hybi_state::IGNORE;
|
||||
}
|
||||
|
||||
void process_header(std::istream& s) {
|
||||
m_header.consume(s);
|
||||
|
||||
if (m_header.ready()) {
|
||||
// Get a free message from the read queue for the type of the
|
||||
// current message
|
||||
if (m_header.is_control()) {
|
||||
process_control_header();
|
||||
} else {
|
||||
process_data_header();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void process_control_header() {
|
||||
m_control_message = m_connection.get_control_message();
|
||||
|
||||
if (!m_control_message) {
|
||||
throw processor::exception("Out of control messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_control_message->reset(m_header.get_opcode(),m_header.get_masking_key());
|
||||
|
||||
m_payload_left = static_cast<size_t>(m_header.get_payload_size());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
} else {
|
||||
m_state = hybi_state::READ_PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void process_data_header() {
|
||||
if (!m_data_message) {
|
||||
// This is a new message. No continuation frames allowed.
|
||||
if (m_header.get_opcode() == frame::opcode::CONTINUATION) {
|
||||
throw processor::exception("Received continuation frame without an outstanding message.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_data_message = m_connection.get_data_message();
|
||||
|
||||
if (!m_data_message) {
|
||||
throw processor::exception("Out of data messages",
|
||||
processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_data_message->reset(m_header.get_opcode());
|
||||
} else {
|
||||
// A message has already been started. Continuation frames only!
|
||||
if (m_header.get_opcode() != frame::opcode::CONTINUATION) {
|
||||
throw processor::exception("Received new message before the completion of the existing one.",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
m_payload_left = static_cast<size_t>(m_header.get_payload_size());
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
} else {
|
||||
// each frame has a new masking key
|
||||
m_data_message->set_masking_key(m_header.get_masking_key());
|
||||
m_state = hybi_state::READ_PAYLOAD;
|
||||
}
|
||||
}
|
||||
|
||||
void process_payload(std::istream& input) {
|
||||
//std::cout << "payload left 1: " << m_payload_left << std::endl;
|
||||
size_t num;
|
||||
|
||||
// read bytes into processor buffer. Read the lesser of the buffer size
|
||||
// and the number of bytes left in the payload.
|
||||
|
||||
input.read(m_payload_buffer, std::min(m_payload_left, PAYLOAD_BUFFER_SIZE));
|
||||
num = static_cast<size_t>(input.gcount());
|
||||
|
||||
if (input.bad()) {
|
||||
throw processor::exception("istream readsome error",
|
||||
processor::error::FATAL_ERROR);
|
||||
}
|
||||
|
||||
if (num == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_payload_left -= num;
|
||||
|
||||
// tell the appropriate message to process the bytes.
|
||||
if (m_header.is_control()) {
|
||||
m_control_message->process_payload(m_payload_buffer,num);
|
||||
} else {
|
||||
//m_connection.alog().at(log::alevel::DEVEL) << "process_payload. Size: " << m_payload_left << log::endl;
|
||||
m_data_message->process_payload(m_payload_buffer,num);
|
||||
}
|
||||
|
||||
if (m_payload_left == 0) {
|
||||
process_frame();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void process_frame() {
|
||||
if (m_header.get_fin()) {
|
||||
if (m_header.is_control()) {
|
||||
m_control_message->complete();
|
||||
} else {
|
||||
m_data_message->complete();
|
||||
}
|
||||
m_state = hybi_state::READY;
|
||||
} else {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return m_state == hybi_state::READY;
|
||||
}
|
||||
|
||||
bool is_control() const {
|
||||
return m_header.is_control();
|
||||
}
|
||||
|
||||
// note this can only be called once
|
||||
message::data_ptr get_data_message() {
|
||||
message::data_ptr p = m_data_message;
|
||||
m_data_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
// note this can only be called once
|
||||
message::control_ptr get_control_message() {
|
||||
message::control_ptr p = m_control_message;
|
||||
m_control_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_state = hybi_state::READ_HEADER;
|
||||
m_header.reset();
|
||||
}
|
||||
|
||||
uint64_t get_bytes_needed() const {
|
||||
switch (m_state) {
|
||||
case hybi_state::READ_HEADER:
|
||||
return m_header.get_bytes_needed();
|
||||
case hybi_state::READ_PAYLOAD:
|
||||
case hybi_state::IGNORE:
|
||||
return m_payload_left;
|
||||
case hybi_state::READY:
|
||||
return 0;
|
||||
default:
|
||||
throw "shouldn't be here";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: replace all this to remove all lingering dependencies on
|
||||
// websocket_frame
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const utf8_string& payload) {
|
||||
/*if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}*/
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
|
||||
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(opcode);
|
||||
m_write_frame.set_masked(mask);
|
||||
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_payload(payload);
|
||||
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const binary_string& payload) {
|
||||
/*if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}*/
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(opcode);
|
||||
m_write_frame.set_masked(mask);
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_payload(payload);
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/*binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
bool mask,
|
||||
const std::string& reason) {
|
||||
binary_string_ptr response(new binary_string(0));
|
||||
|
||||
m_write_frame.reset();
|
||||
m_write_frame.set_opcode(frame::opcode::CLOSE);
|
||||
m_write_frame.set_masked(mask);
|
||||
m_write_frame.set_fin(true);
|
||||
m_write_frame.set_status(code,reason);
|
||||
|
||||
m_write_frame.process_payload();
|
||||
|
||||
// TODO
|
||||
response->resize(m_write_frame.get_header_len()+m_write_frame.get_payload().size());
|
||||
|
||||
// copy header
|
||||
std::copy(m_write_frame.get_header(),m_write_frame.get_header()+m_write_frame.get_header_len(),response->begin());
|
||||
|
||||
// copy payload
|
||||
std::copy(m_write_frame.get_payload().begin(),m_write_frame.get_payload().end(),response->begin()+m_write_frame.get_header_len());
|
||||
|
||||
return response;
|
||||
}*/
|
||||
|
||||
// new prepare frame stuff
|
||||
void prepare_frame(message::data_ptr msg) {
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->validate_payload();
|
||||
|
||||
bool masked = !m_connection.is_server();
|
||||
int32_t key = m_connection.rand();
|
||||
|
||||
m_write_header.reset();
|
||||
m_write_header.set_fin(true);
|
||||
m_write_header.set_opcode(msg->get_opcode());
|
||||
m_write_header.set_masked(masked,key);
|
||||
m_write_header.set_payload_size(msg->get_payload().size());
|
||||
m_write_header.complete();
|
||||
|
||||
msg->set_header(m_write_header.get_header_bytes());
|
||||
|
||||
if (masked) {
|
||||
msg->set_masking_key(key);
|
||||
msg->mask();
|
||||
}
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value code,
|
||||
const std::string& reason)
|
||||
{
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set close payload
|
||||
if (code != close::status::NO_STATUS) {
|
||||
const uint16_t payload = htons(static_cast<u_short>(code));
|
||||
|
||||
msg->set_payload(std::string(reinterpret_cast<const char*>(&payload), 2));
|
||||
msg->append_payload(reason);
|
||||
}
|
||||
|
||||
// prepare rest of frame
|
||||
prepare_frame(msg);
|
||||
}
|
||||
private:
|
||||
// must be divisible by 8 (some things are hardcoded for 4 and 8 byte word
|
||||
// sizes
|
||||
static const size_t PAYLOAD_BUFFER_SIZE = 512;
|
||||
|
||||
connection_type& m_connection;
|
||||
int m_state;
|
||||
|
||||
message::data_ptr m_data_message;
|
||||
message::control_ptr m_control_message;
|
||||
hybi_header m_header;
|
||||
hybi_header m_write_header;
|
||||
size_t m_payload_left;
|
||||
|
||||
char m_payload_buffer[PAYLOAD_BUFFER_SIZE];
|
||||
|
||||
frame::parser<connection_type> m_write_frame; // TODO: refactor this out
|
||||
};
|
||||
|
||||
template <class connection_type>
|
||||
const size_t hybi<connection_type>::PAYLOAD_BUFFER_SIZE;
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_HPP
|
||||
298
src/processors/hybi_header.cpp
Normal file
298
src/processors/hybi_header.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hybi_header.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using websocketpp::processor::hybi_header;
|
||||
|
||||
hybi_header::hybi_header() {
|
||||
reset();
|
||||
}
|
||||
void hybi_header::reset() {
|
||||
memset(m_header, 0x00, MAX_HEADER_LENGTH);
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
m_bytes_needed = BASIC_HEADER_LENGTH;
|
||||
}
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
void hybi_header::consume(std::istream& input) {
|
||||
switch (m_state) {
|
||||
case STATE_BASIC_HEADER:
|
||||
input.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_basic_header();
|
||||
|
||||
validate_basic_header();
|
||||
|
||||
if (m_bytes_needed > 0) {
|
||||
m_state = STATE_EXTENDED_HEADER;
|
||||
} else {
|
||||
process_extended_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_EXTENDED_HEADER:
|
||||
input.read(&m_header[get_header_len()-m_bytes_needed],
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= input.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_extended_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//std::cout << "header so far: " << zsutil::to_hex(std::string(m_header,MAX_HEADER_LENGTH)) << std::endl;
|
||||
}
|
||||
uint64_t hybi_header::get_bytes_needed() const {
|
||||
return m_bytes_needed;
|
||||
}
|
||||
bool hybi_header::ready() const {
|
||||
return m_state == STATE_READY;
|
||||
}
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
void hybi_header::set_fin(bool fin) {
|
||||
set_header_bit(BPB0_FIN,0,fin);
|
||||
}
|
||||
void hybi_header::set_rsv1(bool b) {
|
||||
set_header_bit(BPB0_RSV1,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv2(bool b) {
|
||||
set_header_bit(BPB0_RSV2,0,b);
|
||||
}
|
||||
void hybi_header::set_rsv3(bool b) {
|
||||
set_header_bit(BPB0_RSV3,0,b);
|
||||
}
|
||||
void hybi_header::set_opcode(websocketpp::frame::opcode::value op) {
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
m_header[0] |= op; // set op bits
|
||||
}
|
||||
void hybi_header::set_masked(bool masked,int32_t key) {
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
set_masking_key(key);
|
||||
} else {
|
||||
m_header[1] &= (0xFF ^ BPB1_MASK);
|
||||
clear_masking_key();
|
||||
}
|
||||
}
|
||||
void hybi_header::set_payload_size(uint64_t size) {
|
||||
if (size <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_header[1] |= size;
|
||||
m_payload_size = size;
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] |= BASIC_PAYLOAD_16BIT_CODE;
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] |= BASIC_PAYLOAD_16BIT_CODE;
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])) = htons(static_cast<uint16_t>(size));
|
||||
|
||||
/* uint16_t net_size = htons(static_cast<uint16_t>(size));
|
||||
//memcpy(&m_header[BASIC_HEADER_LENGTH], &net_size, sizeof(uint16_t));
|
||||
std::copy(
|
||||
reinterpret_cast<char*>(&net_size),
|
||||
reinterpret_cast<char*>(&net_size)+sizeof(uint16_t),
|
||||
&m_header[BASIC_HEADER_LENGTH]
|
||||
);*/
|
||||
} else if (size <= frame::limits::PAYLOAD_SIZE_JUMBO) {
|
||||
if (get_masked()) {
|
||||
// shift mask bytes to the correct position given the new size
|
||||
unsigned int mask_offset = get_header_len()-4;
|
||||
m_header[1] |= BASIC_PAYLOAD_64BIT_CODE;
|
||||
memcpy(&m_header[get_header_len()-4], &m_header[mask_offset], 4);
|
||||
} else {
|
||||
m_header[1] |= BASIC_PAYLOAD_64BIT_CODE;
|
||||
}
|
||||
m_payload_size = size;
|
||||
*(reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])) = zsutil::htonll(size);
|
||||
} else {
|
||||
throw processor::exception("set_payload_size called with value that was too large (>2^63)",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
}
|
||||
void hybi_header::complete() {
|
||||
validate_basic_header();
|
||||
m_state = STATE_READY;
|
||||
}
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
std::string hybi_header::get_header_bytes() const {
|
||||
return std::string(m_header,get_header_len());
|
||||
}
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
bool hybi_header::get_fin() const {
|
||||
return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
|
||||
}
|
||||
bool hybi_header::get_rsv1() const {
|
||||
return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
|
||||
}
|
||||
bool hybi_header::get_rsv2() const {
|
||||
return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
|
||||
}
|
||||
bool hybi_header::get_rsv3() const {
|
||||
return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
|
||||
}
|
||||
websocketpp::frame::opcode::value hybi_header::get_opcode() const {
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
bool hybi_header::get_masked() const {
|
||||
return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
|
||||
}
|
||||
int32_t hybi_header::get_masking_key() const {
|
||||
if (!get_masked()) {
|
||||
return 0;
|
||||
}
|
||||
return *reinterpret_cast<const int32_t*>(&m_header[get_header_len()-4]);
|
||||
}
|
||||
uint64_t hybi_header::get_payload_size() const {
|
||||
return m_payload_size;
|
||||
}
|
||||
|
||||
bool hybi_header::is_control() const {
|
||||
return (frame::opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
// private
|
||||
unsigned int hybi_header::get_header_len() const {
|
||||
unsigned int temp = 2;
|
||||
|
||||
if (get_masked()) {
|
||||
temp += 4;
|
||||
}
|
||||
|
||||
if (get_basic_size() == 126) {
|
||||
temp += 2;
|
||||
} else if (get_basic_size() == 127) {
|
||||
temp += 8;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
uint8_t hybi_header::get_basic_size() const {
|
||||
return m_header[1] & BPB1_PAYLOAD;
|
||||
}
|
||||
|
||||
void hybi_header::validate_basic_header() const {
|
||||
// check for control frame size
|
||||
if (is_control() && get_basic_size() > frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("Control Frame is too large",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved bits
|
||||
if (get_rsv1() || get_rsv2() || get_rsv3()) {
|
||||
throw processor::exception("Reserved bit used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved opcodes
|
||||
if (frame::opcode::reserved(get_opcode())) {
|
||||
throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for invalid opcodes
|
||||
if (frame::opcode::invalid(get_opcode())) {
|
||||
throw processor::exception("Invalid opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for fragmented control message
|
||||
if (is_control() && !get_fin()) {
|
||||
throw processor::exception("Fragmented control message",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::process_basic_header() {
|
||||
m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
|
||||
}
|
||||
void hybi_header::process_extended_header() {
|
||||
uint8_t s = get_basic_size();
|
||||
|
||||
if (s <= frame::limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_payload_size = s;
|
||||
} else if (s == BASIC_PAYLOAD_16BIT_CODE) {
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
m_payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << m_payload_size;
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else if (s == BASIC_PAYLOAD_64BIT_CODE) {
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
m_payload_size = zsutil::ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (m_payload_size <= frame::limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_header_bit(uint8_t bit,int byte,bool value) {
|
||||
if (value) {
|
||||
m_header[byte] |= bit;
|
||||
} else {
|
||||
m_header[byte] &= (0xFF ^ bit);
|
||||
}
|
||||
}
|
||||
|
||||
void hybi_header::set_masking_key(int32_t key) {
|
||||
*(reinterpret_cast<int32_t *>(&m_header[get_header_len()-4])) = key;
|
||||
}
|
||||
void hybi_header::clear_masking_key() {
|
||||
// this is a no-op as clearing the mask bit also changes the get_header_len
|
||||
// method to not include these byte ranges. Whenever the masking bit is re-
|
||||
// set a new key is generated anyways.
|
||||
}
|
||||
147
src/processors/hybi_header.hpp
Normal file
147
src/processors/hybi_header.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
/// Describes a processor for reading and writing WebSocket frame headers
|
||||
/**
|
||||
* The hybi_header class provides a processor capable of reading and writing
|
||||
* WebSocket frame headers. It has two writing modes and two reading modes.
|
||||
*
|
||||
* Writing method 1: call consume() until ready()
|
||||
* Writing method 2: call set_* methods followed by complete()
|
||||
*
|
||||
* Writing methods are valid only when ready() returns false. Use reset() to
|
||||
* reset the header for writing again. Mixing writing methods between calls to
|
||||
* reset() may behave unpredictably.
|
||||
*
|
||||
* Reading method 1: call get_header_bytes() to return a string of bytes
|
||||
* Reading method 2: call get_* methods to read individual values
|
||||
*
|
||||
* Reading methods are valid only when ready() is true.
|
||||
*
|
||||
* @par Thread Safety
|
||||
* @e Distinct @e objects: Safe.@n
|
||||
* @e Shared @e objects: Unsafe
|
||||
*/
|
||||
class hybi_header {
|
||||
public:
|
||||
/// Construct a header processor and initialize for writing
|
||||
hybi_header();
|
||||
/// Reset a header processor for writing
|
||||
void reset();
|
||||
|
||||
// Writing interface (parse a byte stream)
|
||||
// valid only if ready() returns false
|
||||
// Consume will throw a processor::exception in the case that the bytes it
|
||||
// read do not form a valid WebSocket frame header.
|
||||
void consume(std::istream& input);
|
||||
uint64_t get_bytes_needed() const;
|
||||
bool ready() const;
|
||||
|
||||
// Writing interface (set fields directly)
|
||||
// valid only if ready() returns false
|
||||
// set_* may allow invalid values. Call complete() once values are set to
|
||||
// check for header validity.
|
||||
void set_fin(bool fin);
|
||||
void set_rsv1(bool b);
|
||||
void set_rsv2(bool b);
|
||||
void set_rsv3(bool b);
|
||||
void set_opcode(websocketpp::frame::opcode::value op);
|
||||
void set_masked(bool masked,int32_t key);
|
||||
void set_payload_size(uint64_t size);
|
||||
// Complete will throw a processor::exception in the case that the
|
||||
// combination of values set do not form a valid WebSocket frame header.
|
||||
void complete();
|
||||
|
||||
// Reading interface (get string of bytes)
|
||||
// valid only if ready() returns true
|
||||
std::string get_header_bytes() const;
|
||||
|
||||
// Reading interface (get fields directly)
|
||||
// valid only if ready() returns true
|
||||
bool get_fin() const;
|
||||
bool get_rsv1() const;
|
||||
bool get_rsv2() const;
|
||||
bool get_rsv3() const;
|
||||
frame::opcode::value get_opcode() const;
|
||||
bool get_masked() const;
|
||||
// will return zero in the case where get_masked() is false. Note:
|
||||
// a masking key of zero is slightly different than no mask at all.
|
||||
int32_t get_masking_key() const;
|
||||
uint64_t get_payload_size() const;
|
||||
|
||||
bool is_control() const;
|
||||
private:
|
||||
// general helper functions
|
||||
unsigned int get_header_len() const;
|
||||
uint8_t get_basic_size() const;
|
||||
void validate_basic_header() const;
|
||||
|
||||
// helper functions for writing
|
||||
void process_basic_header();
|
||||
void process_extended_header();
|
||||
void set_header_bit(uint8_t bit,int byte,bool value);
|
||||
void set_masking_key(int32_t key);
|
||||
void clear_masking_key();
|
||||
|
||||
// basic payload byte flags
|
||||
static const uint8_t BPB0_OPCODE = 0x0F;
|
||||
static const uint8_t BPB0_RSV3 = 0x10;
|
||||
static const uint8_t BPB0_RSV2 = 0x20;
|
||||
static const uint8_t BPB0_RSV1 = 0x40;
|
||||
static const uint8_t BPB0_FIN = 0x80;
|
||||
static const uint8_t BPB1_PAYLOAD = 0x7F;
|
||||
static const uint8_t BPB1_MASK = 0x80;
|
||||
|
||||
static const uint8_t BASIC_PAYLOAD_16BIT_CODE = 0x7E; // 126
|
||||
static const uint8_t BASIC_PAYLOAD_64BIT_CODE = 0x7F; // 127
|
||||
|
||||
static const unsigned int BASIC_HEADER_LENGTH = 2;
|
||||
static const unsigned int MAX_HEADER_LENGTH = 14;
|
||||
|
||||
static const uint8_t STATE_BASIC_HEADER = 1;
|
||||
static const uint8_t STATE_EXTENDED_HEADER = 2;
|
||||
static const uint8_t STATE_READY = 3;
|
||||
static const uint8_t STATE_WRITE = 4;
|
||||
|
||||
uint8_t m_state;
|
||||
std::streamsize m_bytes_needed;
|
||||
uint64_t m_payload_size;
|
||||
char m_header[MAX_HEADER_LENGTH];
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_HEADER_HPP
|
||||
359
src/processors/hybi_legacy.hpp
Normal file
359
src/processors/hybi_legacy.hpp
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
|
||||
#include "processor.hpp"
|
||||
|
||||
#include "../md5/md5.hpp"
|
||||
#include "../network_utilities.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#ifdef min
|
||||
#undef min
|
||||
#endif // #ifdef min
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace hybi_legacy_state {
|
||||
enum value {
|
||||
INIT = 0,
|
||||
READ = 1,
|
||||
DONE = 2
|
||||
};
|
||||
}
|
||||
|
||||
template <class connection_type>
|
||||
class hybi_legacy : public processor_base {
|
||||
public:
|
||||
hybi_legacy(connection_type &connection)
|
||||
: m_connection(connection),
|
||||
m_state(hybi_legacy_state::INIT)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void validate_handshake(const http::parser::request& /*headers*/) const {
|
||||
|
||||
}
|
||||
|
||||
void handshake_response(const http::parser::request& request,http::parser::response& response) {
|
||||
char key_final[16];
|
||||
|
||||
// copy key1 into final key
|
||||
decode_client_key(request.header("Sec-WebSocket-Key1"), &key_final[0]);
|
||||
|
||||
// copy key2 into final key
|
||||
decode_client_key(request.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.
|
||||
const std::string& key3 = request.header("Sec-WebSocket-Key3");
|
||||
std::copy(key3.c_str(),
|
||||
key3.c_str()+std::min(static_cast<size_t>(8), key3.size()),
|
||||
&key_final[8]);
|
||||
|
||||
m_key3 = md5_hash_string(std::string(key_final,16));
|
||||
|
||||
response.add_header("Upgrade","websocket");
|
||||
response.add_header("Connection","Upgrade");
|
||||
|
||||
// TODO: require headers that need application specific information?
|
||||
|
||||
// Echo back client's origin unless our local application set a
|
||||
// more restrictive one.
|
||||
if (response.header("Sec-WebSocket-Origin") == "") {
|
||||
response.add_header("Sec-WebSocket-Origin",request.header("Origin"));
|
||||
}
|
||||
|
||||
// Echo back the client's request host unless our local application
|
||||
// set a different one.
|
||||
if (response.header("Sec-WebSocket-Location") == "") {
|
||||
// TODO: extract from host header rather than hard code
|
||||
uri_ptr uri = get_uri(request);
|
||||
response.add_header("Sec-WebSocket-Location",uri->str());
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_origin(const http::parser::request& request) const {
|
||||
return request.header("Origin");
|
||||
}
|
||||
|
||||
uri_ptr get_uri(const http::parser::request& request) const {
|
||||
std::string h = request.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 uri_ptr(new uri(m_connection.is_secure(),h,request.uri()));
|
||||
} else {
|
||||
return uri_ptr(new uri(m_connection.is_secure(),
|
||||
h.substr(0,last_colon),
|
||||
h.substr(last_colon+1),
|
||||
request.uri()));
|
||||
}
|
||||
|
||||
// TODO: check if get_uri is a full uri
|
||||
}
|
||||
|
||||
void consume(std::istream& s) {
|
||||
//unsigned char c;
|
||||
while (s.good() && m_state != hybi_legacy_state::DONE) {
|
||||
//c = s.get();
|
||||
//if (s.good()) {
|
||||
process(s);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return m_state == hybi_legacy_state::DONE;
|
||||
}
|
||||
|
||||
// legacy hybi has no control messages.
|
||||
bool is_control() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
message::data_ptr get_data_message() {
|
||||
message::data_ptr p = m_data_message;
|
||||
m_data_message.reset();
|
||||
return p;
|
||||
}
|
||||
|
||||
message::control_ptr get_control_message() {
|
||||
throw "Hybi legacy has no control messages.";
|
||||
}
|
||||
|
||||
void process(std::istream& input) {
|
||||
if (m_state == hybi_legacy_state::INIT) {
|
||||
// we are looking for a 0x00
|
||||
if (input.peek() == 0x00) {
|
||||
// start a message
|
||||
input.ignore();
|
||||
|
||||
m_state = hybi_legacy_state::READ;
|
||||
|
||||
m_data_message = m_connection.get_data_message();
|
||||
|
||||
if (!m_data_message) {
|
||||
throw processor::exception("Out of data messages",processor::error::OUT_OF_MESSAGES);
|
||||
}
|
||||
|
||||
m_data_message->reset(frame::opcode::TEXT);
|
||||
} else {
|
||||
input.ignore();
|
||||
// TODO: ignore or error
|
||||
//std::stringstream foo;
|
||||
|
||||
//foo << "invalid character read: |" << input.peek() << "|";
|
||||
|
||||
std::cout << "invalid character read: |" << input.peek() << "|" << std::endl;
|
||||
|
||||
//throw processor::exception(foo.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
} else if (m_state == hybi_legacy_state::READ) {
|
||||
if (input.peek() == 0xFF) {
|
||||
// end
|
||||
input.ignore();
|
||||
|
||||
m_state = hybi_legacy_state::DONE;
|
||||
} else {
|
||||
if (m_data_message) {
|
||||
size_t num;
|
||||
|
||||
input.get(m_payload_buffer, PAYLOAD_BUFFER_SIZE, '\xFF');
|
||||
|
||||
num = static_cast<size_t>(input.gcount());
|
||||
|
||||
if (input.bad()) {
|
||||
throw processor::exception("istream readsome error",
|
||||
processor::error::FATAL_ERROR);
|
||||
}
|
||||
|
||||
m_data_message->process_payload(m_payload_buffer,num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_state = hybi_legacy_state::INIT;
|
||||
m_data_message.reset();
|
||||
}
|
||||
|
||||
uint64_t get_bytes_needed() const {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string get_key3() const {
|
||||
return m_key3;
|
||||
}
|
||||
|
||||
// TODO: to factor away
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const binary_string& payload) {
|
||||
if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}
|
||||
|
||||
// TODO: mask = ignore?
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(payload.size()+2));
|
||||
|
||||
(*response)[0] = 0x00;
|
||||
std::copy(payload.begin(),payload.end(),response->begin()+1);
|
||||
(*response)[response->size()-1] = 0xFF;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
bool mask,
|
||||
const utf8_string& payload) {
|
||||
if (opcode != frame::opcode::TEXT) {
|
||||
// TODO: hybi_legacy doesn't allow non-text frames.
|
||||
throw;
|
||||
}
|
||||
|
||||
// TODO: mask = ignore?
|
||||
|
||||
// TODO: utf8 validation on payload.
|
||||
|
||||
binary_string_ptr response(new binary_string(payload.size()+2));
|
||||
|
||||
(*response)[0] = 0x00;
|
||||
std::copy(payload.begin(),payload.end(),response->begin()+1);
|
||||
(*response)[response->size()-1] = 0xFF;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
bool mask,
|
||||
const std::string& reason) {
|
||||
binary_string_ptr response(new binary_string(2));
|
||||
|
||||
(*response)[0] = 0xFF;
|
||||
(*response)[1] = 0x00;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void prepare_frame(message::data_ptr msg) {
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->set_header(std::string(1,0x00));
|
||||
|
||||
msg->append_payload(std::string(1,0xFF));
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value /*code*/,
|
||||
const std::string& /*reason*/)
|
||||
{
|
||||
assert(msg);
|
||||
if (msg->get_prepared()) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->set_header(std::string());
|
||||
|
||||
std::string val;
|
||||
val.append(1,0xFF);
|
||||
val.append(1,0x00);
|
||||
msg->set_payload(val);
|
||||
|
||||
msg->set_prepared(true);
|
||||
}
|
||||
|
||||
private:
|
||||
void decode_client_key(const std::string& key, char* result) {
|
||||
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 = atoi(digits.c_str());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// must be divisible by 8 (some things are hardcoded for 4 and 8 byte word
|
||||
// sizes
|
||||
static const size_t PAYLOAD_BUFFER_SIZE = 512;
|
||||
|
||||
connection_type& m_connection;
|
||||
hybi_legacy_state::value m_state;
|
||||
|
||||
message::data_ptr m_data_message;
|
||||
|
||||
std::string m_key3;
|
||||
|
||||
char m_payload_buffer[PAYLOAD_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
} // processor
|
||||
} // websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HYBI_LEGACY_HPP
|
||||
65
src/processors/hybi_util.cpp
Normal file
65
src/processors/hybi_util.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "hybi_util.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
namespace hybi_util {
|
||||
|
||||
size_t prepare_masking_key(const masking_key_type& key) {
|
||||
size_t prepared_key = key.i;
|
||||
size_t wordSize = sizeof(size_t);
|
||||
if (wordSize == 8) {
|
||||
prepared_key <<= 32;
|
||||
prepared_key |= (static_cast<size_t>(key.i) & 0x00000000FFFFFFFFLL);
|
||||
}
|
||||
return prepared_key;
|
||||
}
|
||||
|
||||
size_t circshift_prepared_key(size_t prepared_key, size_t offset) {
|
||||
size_t temp = prepared_key << (sizeof(size_t)-offset)*8;
|
||||
return (prepared_key >> offset*8) | temp;
|
||||
}
|
||||
|
||||
void word_mask_exact(char* data,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* word_data = reinterpret_cast<size_t*>(data);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
word_data[i] ^= prepared_key;
|
||||
}
|
||||
|
||||
for (size_t i = n*sizeof(size_t); i < length; i++) {
|
||||
data[i] ^= key.c[i%4];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace hybi_util
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
70
src/processors/hybi_util.hpp
Normal file
70
src/processors/hybi_util.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_HYBI_UTIL_HPP
|
||||
#define WEBSOCKET_HYBI_UTIL_HPP
|
||||
|
||||
#include "../common.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
namespace hybi_util {
|
||||
|
||||
// type used to store a masking key
|
||||
union masking_key_type {
|
||||
int32_t i;
|
||||
char c[4];
|
||||
};
|
||||
|
||||
// extract a masking key into a value the size of a machine word. Machine word
|
||||
// size must be 4 or 8
|
||||
size_t prepare_masking_key(const masking_key_type& key);
|
||||
|
||||
// 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 0, 1, 2, or 3
|
||||
size_t circshift_prepared_key(size_t prepared_key, size_t offset);
|
||||
|
||||
// basic byte by byte mask
|
||||
template <typename iter_type>
|
||||
void byte_mask(iter_type b, iter_type e, const masking_key_type& key, size_t key_offset = 0) {
|
||||
size_t key_index = key_offset;
|
||||
for (iter_type i = b; i != e; i++) {
|
||||
*i ^= key.c[key_index++];
|
||||
key_index %= 4;
|
||||
}
|
||||
}
|
||||
|
||||
// exactly masks the bytes from start to end using key `key`
|
||||
void word_mask_exact(char* data,size_t length,const masking_key_type& key);
|
||||
|
||||
} // namespace hybi_util
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_HYBI_UTIL_HPP
|
||||
148
src/processors/processor.hpp
Normal file
148
src/processors/processor.hpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_PROCESSOR_HPP
|
||||
#define WEBSOCKET_PROCESSOR_HPP
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
namespace error {
|
||||
enum value {
|
||||
FATAL_ERROR = 0, // force session end
|
||||
SOFT_ERROR = 1, // should log and ignore
|
||||
PROTOCOL_VIOLATION = 2, // must end session
|
||||
PAYLOAD_VIOLATION = 3, // should end session
|
||||
INTERNAL_ENDPOINT_ERROR = 4,// cleanly end session
|
||||
MESSAGE_TOO_BIG = 5, // ???
|
||||
OUT_OF_MESSAGES = 6 // read queue is empty, wait
|
||||
};
|
||||
}
|
||||
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
exception(const std::string& msg,
|
||||
error::value code = error::FATAL_ERROR)
|
||||
: m_msg(msg),m_code(code) {}
|
||||
~exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
|
||||
error::value code() const throw() {
|
||||
return m_code;
|
||||
}
|
||||
|
||||
std::string m_msg;
|
||||
error::value m_code;
|
||||
};
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#include "../http/parser.hpp"
|
||||
#include "../uri.hpp"
|
||||
#include "../websocket_frame.hpp" // TODO: clean up
|
||||
|
||||
#include "../messages/data.hpp"
|
||||
#include "../messages/control.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace processor {
|
||||
|
||||
class processor_base : boost::noncopyable {
|
||||
public:
|
||||
virtual ~processor_base() {}
|
||||
// validate client handshake
|
||||
// validate server handshake
|
||||
|
||||
// Given a list of HTTP headers determine if the values are sufficient
|
||||
// to start a websocket session. If so begin constructing a response, if not throw a handshake
|
||||
// exception.
|
||||
// validate handshake request
|
||||
virtual void validate_handshake(const http::parser::request& headers) const = 0;
|
||||
|
||||
virtual void handshake_response(const http::parser::request& request,http::parser::response& response) = 0;
|
||||
|
||||
// Extracts client origin from a handshake request
|
||||
virtual std::string get_origin(const http::parser::request& request) const = 0;
|
||||
// Extracts client uri from a handshake request
|
||||
virtual uri_ptr get_uri(const http::parser::request& request) const = 0;
|
||||
|
||||
// consume bytes, throw on exception
|
||||
virtual void consume(std::istream& s) = 0;
|
||||
|
||||
// is there a message ready to be dispatched?
|
||||
virtual bool ready() const = 0;
|
||||
virtual bool is_control() const = 0;
|
||||
virtual message::data_ptr get_data_message() = 0;
|
||||
virtual message::control_ptr get_control_message() = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual uint64_t get_bytes_needed() const = 0;
|
||||
|
||||
// Get information about the message that is ready
|
||||
//virtual frame::opcode::value get_opcode() const = 0;
|
||||
|
||||
//virtual utf8_string_ptr get_utf8_payload() const = 0;
|
||||
//virtual binary_string_ptr get_binary_payload() const = 0;
|
||||
//virtual close::status::value get_close_code() const = 0;
|
||||
//virtual utf8_string get_close_reason() const = 0;
|
||||
|
||||
// TODO: prepare a frame
|
||||
//virtual binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
// bool mask,
|
||||
// const utf8_string& payload) = 0;
|
||||
//virtual binary_string_ptr prepare_frame(frame::opcode::value opcode,
|
||||
// bool mask,
|
||||
// const binary_string& payload) = 0;
|
||||
//
|
||||
//virtual binary_string_ptr prepare_close_frame(close::status::value code,
|
||||
// bool mask,
|
||||
// const std::string& reason) = 0;
|
||||
|
||||
virtual void prepare_frame(message::data_ptr msg) = 0;
|
||||
virtual void prepare_close_frame(message::data_ptr msg,
|
||||
close::status::value code,
|
||||
const std::string& reason) = 0;
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<processor_base> ptr;
|
||||
|
||||
} // namespace processor
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKET_PROCESSOR_HPP
|
||||
28
src/rng/blank_rng.cpp
Normal file
28
src/rng/blank_rng.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 Makefile was derived from a similar one included in the libjson project
|
||||
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
*/
|
||||
46
src/rng/blank_rng.hpp
Normal file
46
src/rng/blank_rng.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 Makefile was derived from a similar one included in the libjson project
|
||||
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
*/
|
||||
|
||||
#ifndef BLANK_RNG_HPP
|
||||
#define BLANK_RNG_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class blank_rng {
|
||||
public:
|
||||
int32_t gen() {
|
||||
throw "Random Number generation not supported";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BLANK_RNG_HPP
|
||||
39
src/rng/boost_rng.cpp
Normal file
39
src/rng/boost_rng.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 Makefile was derived from a similar one included in the libjson project
|
||||
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
*/
|
||||
|
||||
#include "boost_rng.hpp"
|
||||
|
||||
using websocketpp::boost_rng;
|
||||
|
||||
boost_rng::boost_rng() : m_gen(m_rng,
|
||||
boost::random::uniform_int_distribution<>(INT32_MIN,INT32_MAX)) {}
|
||||
|
||||
int32_t boost_rng::gen() {
|
||||
return m_gen();
|
||||
}
|
||||
54
src/rng/boost_rng.hpp
Normal file
54
src/rng/boost_rng.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 Makefile was derived from a similar one included in the libjson project
|
||||
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
|
||||
*/
|
||||
|
||||
#ifndef BOOST_RNG_HPP
|
||||
#define BOOST_RNG_HPP
|
||||
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/random.hpp>
|
||||
#include <boost/random/random_device.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
class boost_rng {
|
||||
public:
|
||||
boost_rng();
|
||||
int32_t gen();
|
||||
private:
|
||||
boost::random::random_device m_rng;
|
||||
boost::random::variate_generator
|
||||
<
|
||||
boost::random::random_device&,
|
||||
boost::random::uniform_int_distribution<>
|
||||
> m_gen;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // BOOST_RNG_HPP
|
||||
714
src/roles/client.hpp
Normal file
714
src/roles/client.hpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_ROLE_CLIENT_HPP
|
||||
#define WEBSOCKETPP_ROLE_CLIENT_HPP
|
||||
|
||||
#include <limits>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/random.hpp>
|
||||
#include <boost/random/random_device.hpp>
|
||||
|
||||
#include "../endpoint.hpp"
|
||||
#include "../uri.hpp"
|
||||
#include "../shared_const_buffer.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable "warning C4355: 'this' : used in base member initializer list".
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace websocketpp {
|
||||
namespace role {
|
||||
|
||||
template <class endpoint>
|
||||
class client {
|
||||
public:
|
||||
// Connection specific details
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
typedef connection<connection_type> type;
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
// client connections are friends with their respective client endpoint
|
||||
friend class client<endpoint>;
|
||||
|
||||
// Valid always
|
||||
int get_version() const {
|
||||
return m_version;
|
||||
}
|
||||
|
||||
std::string get_origin() const {
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
// not sure when valid
|
||||
std::string get_request_header(const std::string& key) const {
|
||||
return m_request.header(key);
|
||||
}
|
||||
std::string get_response_header(const std::string& key) const {
|
||||
return m_response.header(key);
|
||||
}
|
||||
|
||||
// Valid before connect is called
|
||||
void add_request_header(const std::string& key, const std::string& value) {
|
||||
m_request.add_header(key,value);
|
||||
}
|
||||
void replace_request_header(const std::string& key, const std::string& value) {
|
||||
m_request.replace_header(key,value);
|
||||
}
|
||||
void remove_request_header(const std::string& key) {
|
||||
m_request.remove_header(key);
|
||||
}
|
||||
|
||||
void add_subprotocol(const std::string& value) {
|
||||
m_requested_subprotocols.push_back(value);
|
||||
}
|
||||
|
||||
void set_origin(const std::string& value) {
|
||||
m_origin = value;
|
||||
}
|
||||
|
||||
// Information about the requested URI
|
||||
// valid only after URIs are loaded
|
||||
// TODO: check m_uri for NULLness
|
||||
bool get_secure() const {
|
||||
return m_uri->get_secure();
|
||||
}
|
||||
std::string get_host() const {
|
||||
return m_uri->get_host();
|
||||
}
|
||||
std::string get_resource() const {
|
||||
return m_uri->get_resource();
|
||||
}
|
||||
uint16_t get_port() const {
|
||||
return m_uri->get_port();
|
||||
}
|
||||
std::string get_uri() const {
|
||||
return m_uri->str();
|
||||
}
|
||||
|
||||
int32_t rand() {
|
||||
return m_endpoint.rand();
|
||||
}
|
||||
|
||||
bool is_server() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// should this exist?
|
||||
boost::asio::io_service& get_io_service() {
|
||||
return m_endpoint.get_io_service();
|
||||
}
|
||||
protected:
|
||||
connection(endpoint& e)
|
||||
: m_endpoint(e),
|
||||
m_connection(static_cast< connection_type& >(*this)),
|
||||
// TODO: version shouldn't be hardcoded
|
||||
m_version(13) {}
|
||||
|
||||
void set_uri(uri_ptr u) {
|
||||
m_uri = u;
|
||||
}
|
||||
|
||||
void async_init() {
|
||||
m_connection.m_processor = processor::ptr(new processor::hybi<connection_type>(m_connection));
|
||||
|
||||
m_connection.get_handler()->on_handshake_init(m_connection.shared_from_this());
|
||||
|
||||
write_request();
|
||||
}
|
||||
|
||||
void write_request();
|
||||
void handle_write_request(const boost::system::error_code& error);
|
||||
void read_response();
|
||||
void handle_read_response(const boost::system::error_code& error,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
void log_open_result();
|
||||
private:
|
||||
endpoint& m_endpoint;
|
||||
connection_type& m_connection;
|
||||
|
||||
int m_version;
|
||||
uri_ptr m_uri;
|
||||
std::string m_origin;
|
||||
std::vector<std::string> m_requested_subprotocols;
|
||||
std::vector<std::string> m_requested_extensions;
|
||||
std::string m_subprotocol;
|
||||
std::vector<std::string> m_extensions;
|
||||
|
||||
std::string m_handshake_key;
|
||||
http::parser::request m_request;
|
||||
http::parser::response m_response;
|
||||
};
|
||||
|
||||
// types
|
||||
typedef client<endpoint> type;
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
typedef typename endpoint_traits<endpoint>::connection_type connection_type;
|
||||
typedef typename endpoint_traits<endpoint>::connection_ptr connection_ptr;
|
||||
typedef typename endpoint_traits<endpoint>::handler_ptr handler_ptr;
|
||||
|
||||
// handler interface callback class
|
||||
class handler_interface {
|
||||
public:
|
||||
virtual ~handler_interface() {}
|
||||
|
||||
// Required
|
||||
virtual void on_open(connection_ptr con) {}
|
||||
virtual void on_close(connection_ptr con) {}
|
||||
virtual void on_fail(connection_ptr con) {}
|
||||
|
||||
virtual void on_message(connection_ptr con,message::data_ptr) {}
|
||||
|
||||
// Optional
|
||||
virtual void on_handshake_init(connection_ptr con) {}
|
||||
virtual bool on_ping(connection_ptr con,std::string) {return true;}
|
||||
virtual void on_pong(connection_ptr con,std::string) {}
|
||||
virtual void on_pong_timeout(connection_ptr con,std::string) {}
|
||||
|
||||
};
|
||||
|
||||
client (boost::asio::io_service& m)
|
||||
: m_endpoint(static_cast< endpoint_type& >(*this)),
|
||||
m_io_service(m),
|
||||
m_gen(m_rng,
|
||||
boost::random::uniform_int_distribution<>(
|
||||
(std::numeric_limits<int32_t>::min)(),
|
||||
(std::numeric_limits<int32_t>::max)()
|
||||
)) {}
|
||||
|
||||
connection_ptr get_connection(const std::string& u);
|
||||
|
||||
connection_ptr connect(const std::string& u);
|
||||
connection_ptr connect(connection_ptr con);
|
||||
|
||||
void run(bool perpetual = false);
|
||||
void end_perpetual();
|
||||
void reset();
|
||||
protected:
|
||||
bool is_server() const {return false;}
|
||||
int32_t rand() {return m_gen();}
|
||||
private:
|
||||
void handle_connect(connection_ptr con, const boost::system::error_code& error);
|
||||
|
||||
endpoint_type& m_endpoint;
|
||||
boost::asio::io_service& m_io_service;
|
||||
|
||||
boost::random::random_device m_rng;
|
||||
boost::random::variate_generator<
|
||||
boost::random::random_device&,
|
||||
boost::random::uniform_int_distribution<>
|
||||
> m_gen;
|
||||
|
||||
boost::shared_ptr<boost::asio::io_service::work> m_idle_worker;
|
||||
};
|
||||
|
||||
// client implimentation
|
||||
|
||||
/// Start the client ASIO loop
|
||||
/**
|
||||
* Calls run on the endpoint's io_service. This method will block until the io_service
|
||||
* run method returns. This method may only be called when the endpoint is in the IDLE
|
||||
* state. Endpoints start in the idle state and can be returned to the IDLE state by
|
||||
* calling reset. `run` has a perpetual flag (default is false) that indicates whether
|
||||
* or not it should return after all connections have been made.
|
||||
*
|
||||
* <b>Important note:</b> Calling run with perpetual = false on a client endpoint will return
|
||||
* immediately unless you have already called connect() at least once. To get around
|
||||
* this either queue up all connections you want to make before calling run or call
|
||||
* run with perpetual in another thread.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from IDLE, an exception is thrown otherwise
|
||||
* Concurrency: callable from any thread
|
||||
*
|
||||
* @param perpetual whether or not to run the endpoint in perpetual mode
|
||||
* @exception websocketpp::exception with code error::INVALID_STATE if called from a state other than IDLE
|
||||
*/
|
||||
template <class endpoint>
|
||||
void client<endpoint>::run(bool perpetual) {
|
||||
{
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
if (m_endpoint.m_state != endpoint::IDLE) {
|
||||
throw exception("client::run called from invalid state",error::INVALID_STATE);
|
||||
}
|
||||
|
||||
if (perpetual) {
|
||||
m_idle_worker = boost::shared_ptr<boost::asio::io_service::work>(
|
||||
new boost::asio::io_service::work(m_io_service)
|
||||
);
|
||||
}
|
||||
|
||||
m_endpoint.m_state = endpoint::RUNNING;
|
||||
}
|
||||
|
||||
// TODO: preliminary support for multi-threaded clients. Finish external
|
||||
// interface once better tested
|
||||
size_t num_threads = 1;
|
||||
|
||||
if (num_threads == 1) {
|
||||
m_io_service.run();
|
||||
} else if (num_threads > 1 && num_threads <= MAX_THREAD_POOL_SIZE) {
|
||||
std::vector< boost::shared_ptr<boost::thread> > threads;
|
||||
|
||||
for (std::size_t i = 0; i < num_threads; ++i) {
|
||||
boost::shared_ptr<boost::thread> thread(
|
||||
new boost::thread(boost::bind(
|
||||
&boost::asio::io_service::run,
|
||||
&m_io_service
|
||||
))
|
||||
);
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < threads.size(); ++i) {
|
||||
threads[i]->join();
|
||||
}
|
||||
} else {
|
||||
throw exception("listen called with invalid num_threads value");
|
||||
}
|
||||
|
||||
m_endpoint.m_state = endpoint::STOPPED;
|
||||
}
|
||||
|
||||
/// End the idle work loop that keeps the io_service active
|
||||
/**
|
||||
* Calling end_perpetual on a client endpoint that was started in perpetual mode (via
|
||||
* run(true), will stop the idle work object that prevents the run method from
|
||||
* returning even when there is no work for it to do. Use if you want to gracefully
|
||||
* stop the endpoint. Use stop() to forcibly stop the endpoint.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from RUNNING, ignored otherwise
|
||||
* Concurrency: callable from any thread
|
||||
*/
|
||||
template <class endpoint>
|
||||
void client<endpoint>::end_perpetual() {
|
||||
if (m_idle_worker) {
|
||||
m_idle_worker.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset a stopped endpoint.
|
||||
/**
|
||||
* Resets an endpoint that was stopped by stop() or whose run() method exited due to
|
||||
* running out of work. reset() should not be called while the endpoint is running.
|
||||
* Use stop() and/or end_perpetual() first and then reset once one of those methods
|
||||
* has fully stopped the endpoint.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from STOPPED, an exception is thrown otherwise
|
||||
* Concurrency: callable from any thread
|
||||
*/
|
||||
template <class endpoint>
|
||||
void client<endpoint>::reset() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
if (m_endpoint.m_state != endpoint::STOPPED) {
|
||||
throw exception("client::reset called from invalid state",error::INVALID_STATE);
|
||||
}
|
||||
|
||||
m_io_service.reset();
|
||||
|
||||
m_endpoint.m_state = endpoint::IDLE;
|
||||
}
|
||||
|
||||
/// Returns a new connection
|
||||
/**
|
||||
* Creates and returns a pointer to a new connection to the given URI suitable for passing
|
||||
* to connect(). This method allows applying connection specific settings before
|
||||
* performing the connection.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from IDLE or RUNNING, an exception is thrown otherwise
|
||||
* Concurrency: callable from any thread
|
||||
*
|
||||
* @param u The URI that this connection will connect to.
|
||||
* @return The pointer to the new connection
|
||||
*/
|
||||
template <class endpoint>
|
||||
typename endpoint_traits<endpoint>::connection_ptr
|
||||
client<endpoint>::get_connection(const std::string& u) {
|
||||
try {
|
||||
uri_ptr location(new uri(u));
|
||||
|
||||
if (location->get_secure() && !m_endpoint.is_secure()) {
|
||||
throw websocketpp::exception("Endpoint doesn't support secure connections.",
|
||||
websocketpp::error::ENDPOINT_UNSECURE);
|
||||
}
|
||||
|
||||
connection_ptr con = m_endpoint.create_connection();
|
||||
|
||||
if (!con) {
|
||||
throw websocketpp::exception("get_connection called from invalid state",
|
||||
websocketpp::error::INVALID_STATE);
|
||||
}
|
||||
|
||||
con->set_uri(location);
|
||||
|
||||
return con;
|
||||
} catch (uri_exception& e) {
|
||||
throw websocketpp::exception(e.what(),websocketpp::error::INVALID_URI);
|
||||
}
|
||||
}
|
||||
|
||||
/// Begin the connect process for the given connection.
|
||||
/**
|
||||
* Initiates the async connect request for connection con.
|
||||
*
|
||||
* Visibility: public
|
||||
* State: Valid from IDLE or RUNNING, an exception is thrown otherwise
|
||||
* Concurrency: callable from any thread
|
||||
*
|
||||
* @param con A pointer to the connection to connect
|
||||
* @return The pointer to con
|
||||
*/
|
||||
template <class endpoint>
|
||||
typename endpoint_traits<endpoint>::connection_ptr
|
||||
client<endpoint>::connect(connection_ptr con) {
|
||||
tcp::resolver resolver(m_io_service);
|
||||
|
||||
std::stringstream p;
|
||||
p << con->get_port();
|
||||
|
||||
tcp::resolver::query query(con->get_host(),p.str());
|
||||
tcp::resolver::iterator iterator = resolver.resolve(query);
|
||||
|
||||
boost::asio::async_connect(
|
||||
con->get_raw_socket(),
|
||||
iterator,
|
||||
boost::bind(
|
||||
&endpoint_type::handle_connect,
|
||||
this, // shared from this?
|
||||
con,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
return con;
|
||||
}
|
||||
|
||||
/// Convenience method, equivalent to connect(get_connection(u))
|
||||
template <class endpoint>
|
||||
typename endpoint_traits<endpoint>::connection_ptr
|
||||
client<endpoint>::connect(const std::string& u) {
|
||||
return connect(get_connection(u));
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
void client<endpoint>::handle_connect(connection_ptr con,
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
if (!error) {
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT)
|
||||
<< "Successful connection" << log::endl;
|
||||
|
||||
con->start();
|
||||
} else {
|
||||
con->m_fail_code = fail::status::SYSTEM;
|
||||
con->m_fail_system = error;
|
||||
|
||||
if (error == boost::system::errc::connection_refused) {
|
||||
con->m_fail_reason = "Connection refused";
|
||||
} else if (error == boost::system::errc::operation_canceled) {
|
||||
con->m_fail_reason = "Operation canceled";
|
||||
} else if (error == boost::system::errc::connection_reset) {
|
||||
con->m_fail_reason = "Connection Reset";
|
||||
} else if (error == boost::system::errc::timed_out) {
|
||||
con->m_fail_reason = "Operation timed out";
|
||||
} else if (error == boost::system::errc::broken_pipe) {
|
||||
con->m_fail_reason = "Broken pipe";
|
||||
} else {
|
||||
con->m_fail_reason = "Unknown";
|
||||
}
|
||||
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "An error occurred while establishing a connection: "
|
||||
<< error << " (" << con->m_fail_reason << ")" << log::endl;
|
||||
|
||||
con->terminate(false);
|
||||
}
|
||||
}
|
||||
|
||||
// client connection implimentation
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void client<endpoint>::connection<connection_type>::write_request() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_connection.m_lock);
|
||||
// async write to handle_write
|
||||
|
||||
m_request.set_method("GET");
|
||||
m_request.set_uri(m_uri->get_resource());
|
||||
m_request.set_version("HTTP/1.1");
|
||||
|
||||
m_request.add_header("Upgrade","websocket");
|
||||
m_request.add_header("Connection","Upgrade");
|
||||
m_request.replace_header("Sec-WebSocket-Version","13");
|
||||
|
||||
m_request.replace_header("Host",m_uri->get_host_port());
|
||||
|
||||
if (m_origin != "") {
|
||||
m_request.replace_header("Origin",m_origin);
|
||||
}
|
||||
|
||||
if (m_requested_subprotocols.size() > 0) {
|
||||
std::string vals;
|
||||
std::string sep = "";
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
for (it = m_requested_subprotocols.begin(); it != m_requested_subprotocols.end(); ++it) {
|
||||
vals += sep + *it;
|
||||
sep = ",";
|
||||
}
|
||||
|
||||
m_request.replace_header("Sec-WebSocket-Protocol",vals);
|
||||
}
|
||||
|
||||
// Generate client key
|
||||
int32_t raw_key[4];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
raw_key[i] = this->rand();
|
||||
}
|
||||
|
||||
m_handshake_key = base64_encode(reinterpret_cast<unsigned char const*>(raw_key), 16);
|
||||
|
||||
m_request.replace_header("Sec-WebSocket-Key",m_handshake_key);
|
||||
|
||||
// Unless the user has overridden the user agent, send generic WS++
|
||||
if (m_request.header("User-Agent") == "") {
|
||||
m_request.replace_header("User-Agent",USER_AGENT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: generating this raw request involves way too much copying in cases
|
||||
// without string/vector move semantics.
|
||||
shared_const_buffer buffer(m_request.raw());
|
||||
|
||||
//std::string raw = m_request.raw();
|
||||
|
||||
//m_endpoint.m_alog->at(log::alevel::DEBUG_HANDSHAKE) << raw << log::endl;
|
||||
|
||||
boost::asio::async_write(
|
||||
m_connection.get_socket(),
|
||||
//boost::asio::buffer(raw),
|
||||
buffer,
|
||||
m_connection.get_strand().wrap(boost::bind(
|
||||
&type::handle_write_request,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void client<endpoint>::connection<connection_type>::handle_write_request(
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
if (error) {
|
||||
// TODO: detached state?
|
||||
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error writing WebSocket request. code: "
|
||||
<< error << log::endl;
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
read_response();
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void client<endpoint>::connection<connection_type>::read_response() {
|
||||
boost::asio::async_read_until(
|
||||
m_connection.get_socket(),
|
||||
m_connection.buffer(),
|
||||
"\r\n\r\n",
|
||||
m_connection.get_strand().wrap(boost::bind(
|
||||
&type::handle_read_response,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void client<endpoint>::connection<connection_type>::handle_read_response (
|
||||
const boost::system::error_code& error, std::size_t bytes_transferred)
|
||||
{
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_connection.m_lock);
|
||||
|
||||
// detached check?
|
||||
|
||||
if (error) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error reading HTTP request. code: " << error << log::endl;
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::istream request(&m_connection.buffer());
|
||||
|
||||
if (!m_response.parse_complete(request)) {
|
||||
// not a valid HTTP response
|
||||
// TODO: this should be a client error
|
||||
throw http::exception("Could not parse server response.",
|
||||
http::status_code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
m_endpoint.m_alog->at(log::alevel::DEBUG_HANDSHAKE) << m_response.raw()
|
||||
<< log::endl;
|
||||
|
||||
// error checking
|
||||
if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS) {
|
||||
throw http::exception("Server failed to upgrade connection.",
|
||||
m_response.get_status_code(),
|
||||
m_response.get_status_msg());
|
||||
}
|
||||
|
||||
std::string h = m_response.header("Upgrade");
|
||||
if (!boost::ifind_first(h,"websocket")) {
|
||||
throw http::exception("Token `websocket` missing from Upgrade header.",
|
||||
m_response.get_status_code(),
|
||||
m_response.get_status_msg());
|
||||
}
|
||||
|
||||
h = m_response.header("Connection");
|
||||
if (!boost::ifind_first(h,"upgrade")) {
|
||||
throw http::exception("Token `upgrade` missing from Connection header.",
|
||||
m_response.get_status_code(),
|
||||
m_response.get_status_msg());
|
||||
}
|
||||
|
||||
h = m_response.header("Sec-WebSocket-Accept");
|
||||
if (h == "") {
|
||||
throw http::exception("Required Sec-WebSocket-Accept header is missing.",
|
||||
m_response.get_status_code(),
|
||||
m_response.get_status_msg());
|
||||
} else {
|
||||
std::string server_key = m_handshake_key;
|
||||
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
|
||||
SHA1 sha;
|
||||
uint32_t message_digest[5];
|
||||
|
||||
sha.Reset();
|
||||
sha << server_key.c_str();
|
||||
|
||||
if (!sha.Result(message_digest)) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error computing handshake sha1 hash." << log::endl;
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
|
||||
// convert sha1 hash bytes to network byte order because this sha1
|
||||
// library works on ints rather than bytes
|
||||
for (int i = 0; i < 5; i++) {
|
||||
message_digest[i] = htonl(message_digest[i]);
|
||||
}
|
||||
|
||||
server_key = base64_encode(
|
||||
reinterpret_cast<const unsigned char*>(message_digest),20);
|
||||
if (server_key != h) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Server returned incorrect handshake key." << log::endl;
|
||||
// TODO: close behavior
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
log_open_result();
|
||||
|
||||
m_connection.m_state = session::state::OPEN;
|
||||
|
||||
m_connection.get_handler()->on_open(m_connection.shared_from_this());
|
||||
|
||||
get_io_service().post(
|
||||
m_connection.m_strand.wrap(boost::bind(
|
||||
&connection_type::handle_read_frame,
|
||||
m_connection.shared_from_this(),
|
||||
boost::system::error_code()
|
||||
))
|
||||
);
|
||||
|
||||
//m_connection.handle_read_frame(boost::system::error_code());
|
||||
} catch (const http::exception& e) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error processing server handshake. Server HTTP response: "
|
||||
<< e.m_error_msg << " (" << e.m_error_code
|
||||
<< ") Local error: " << e.what() << log::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// start session loop
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void client<endpoint>::connection<connection_type>::log_open_result() {
|
||||
std::stringstream version;
|
||||
version << "v" << m_version << " ";
|
||||
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT)
|
||||
<< (m_version == -1 ? "HTTP" : "WebSocket") << " Connection "
|
||||
<< m_connection.get_raw_socket().remote_endpoint() << " "
|
||||
<< (m_version == -1 ? "" : version.str())
|
||||
<< (get_request_header("Server") == "" ? "NULL" : get_request_header("Server"))
|
||||
<< " " << m_uri->get_resource() << " " << m_response.get_status_code()
|
||||
<< log::endl;
|
||||
}
|
||||
|
||||
} // namespace role
|
||||
} // namespace websocketpp
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // WEBSOCKETPP_ROLE_CLIENT_HPP
|
||||
957
src/roles/server.hpp
Normal file
957
src/roles/server.hpp
Normal file
@@ -0,0 +1,957 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_ROLE_SERVER_HPP
|
||||
#define WEBSOCKETPP_ROLE_SERVER_HPP
|
||||
|
||||
#include "../processors/hybi.hpp"
|
||||
#include "../processors/hybi_legacy.hpp"
|
||||
#include "../rng/blank_rng.hpp"
|
||||
|
||||
#include "../shared_const_buffer.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/thread/recursive_mutex.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable "warning C4355: 'this' : used in base member initializer list".
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
typedef boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type> bufIterator;
|
||||
|
||||
static std::pair<bufIterator, bool> match_header(boost::shared_ptr<std::string> string,
|
||||
bufIterator nBegin, bufIterator nEnd)
|
||||
{
|
||||
if (nBegin == nEnd)
|
||||
return std::make_pair(nEnd, false);
|
||||
|
||||
(*string) += std::string(nBegin, nEnd);
|
||||
std::string::const_iterator begin = string->begin();
|
||||
std::string::const_iterator end = string->end();
|
||||
|
||||
static const std::string eol_match = "\n";
|
||||
static const std::string header_match = "\r\n\r\n";
|
||||
static const std::string flash_match = "<policy-file-request/>";
|
||||
|
||||
// Do we have a complete HTTP request
|
||||
std::string::const_iterator it = std::search(begin, end, header_match.begin(), header_match.end());
|
||||
if (it != end)
|
||||
{
|
||||
int leftOver = (end - (it + header_match.size()));
|
||||
return std::make_pair(nEnd - leftOver, true);
|
||||
}
|
||||
|
||||
// If we don't have a flash policy request, we're done
|
||||
it = std::search(begin, end, flash_match.begin(), flash_match.end());
|
||||
if (it == end) // No match
|
||||
return std::make_pair(nEnd, false);
|
||||
|
||||
// If we have a line ending before the flash policy request, treat as http
|
||||
std::string::const_iterator it2 = std::search(begin, end, eol_match.begin(), eol_match.end());
|
||||
if ((it2 != end) && (it2 < it))
|
||||
return std::make_pair(nEnd, false);
|
||||
|
||||
// Treat as flash policy request
|
||||
int leftOver = (end - (it + flash_match.size()));
|
||||
return std::make_pair(nEnd - leftOver, true);
|
||||
}
|
||||
|
||||
// Forward declarations
|
||||
template <typename T> struct endpoint_traits;
|
||||
|
||||
namespace role {
|
||||
|
||||
template <class endpoint>
|
||||
class server {
|
||||
public:
|
||||
// Connection specific details
|
||||
template <typename connection_type>
|
||||
class connection : boost::noncopyable {
|
||||
public:
|
||||
typedef connection<connection_type> type;
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
// Valid always
|
||||
int get_version() const {
|
||||
return m_version;
|
||||
}
|
||||
std::string get_request_header(const std::string& key) const {
|
||||
return m_request.header(key);
|
||||
}
|
||||
std::string get_origin() const {
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
// Information about the requested URI
|
||||
// valid only after URIs are loaded
|
||||
// TODO: check m_uri for NULLness
|
||||
bool get_secure() const {
|
||||
return m_uri->get_secure();
|
||||
}
|
||||
std::string get_host() const {
|
||||
return m_uri->get_host();
|
||||
}
|
||||
std::string get_resource() const {
|
||||
return m_uri->get_resource();
|
||||
}
|
||||
uint16_t get_port() const {
|
||||
return m_uri->get_port();
|
||||
}
|
||||
|
||||
// Valid for CONNECTING state
|
||||
void add_response_header(const std::string& key, const std::string& value) {
|
||||
m_response.add_header(key,value);
|
||||
}
|
||||
void replace_response_header(const std::string& key, const std::string& value) {
|
||||
m_response.replace_header(key,value);
|
||||
}
|
||||
void remove_response_header(const std::string& key) {
|
||||
m_response.remove_header(key);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& get_subprotocols() const {
|
||||
return m_requested_subprotocols;
|
||||
}
|
||||
const std::vector<std::string>& get_extensions() const {
|
||||
return m_requested_extensions;
|
||||
}
|
||||
void select_subprotocol(const std::string& value);
|
||||
void select_extension(const std::string& value);
|
||||
|
||||
// Valid if get_version() returns -1 (ie this is an http connection)
|
||||
void set_body(const std::string& value);
|
||||
|
||||
int32_t rand() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool is_server() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
// should this exist?
|
||||
boost::asio::io_service& get_io_service() {
|
||||
return m_endpoint.get_io_service();
|
||||
}
|
||||
protected:
|
||||
connection(endpoint& e)
|
||||
: m_endpoint(e),
|
||||
m_connection(static_cast< connection_type& >(*this)),
|
||||
m_version(-1),
|
||||
m_uri() {}
|
||||
|
||||
// initializes the websocket connection
|
||||
void async_init();
|
||||
void handle_read_request(boost::shared_ptr<std::string>, const boost::system::error_code& error,
|
||||
std::size_t bytes_transferred);
|
||||
void handle_short_key3(const boost::system::error_code& error,
|
||||
std::size_t bytes_transferred);
|
||||
void write_response();
|
||||
void handle_write_response(const boost::system::error_code& error);
|
||||
|
||||
void log_open_result();
|
||||
private:
|
||||
endpoint& m_endpoint;
|
||||
connection_type& m_connection;
|
||||
|
||||
int m_version;
|
||||
uri_ptr m_uri;
|
||||
std::string m_origin;
|
||||
std::vector<std::string> m_requested_subprotocols;
|
||||
std::vector<std::string> m_requested_extensions;
|
||||
std::string m_subprotocol;
|
||||
std::vector<std::string> m_extensions;
|
||||
|
||||
http::parser::request m_request;
|
||||
http::parser::response m_response;
|
||||
blank_rng m_rng;
|
||||
};
|
||||
|
||||
// types
|
||||
typedef server<endpoint> type;
|
||||
typedef endpoint endpoint_type;
|
||||
|
||||
typedef typename endpoint_traits<endpoint>::connection_ptr connection_ptr;
|
||||
typedef typename endpoint_traits<endpoint>::handler_ptr handler_ptr;
|
||||
|
||||
// handler interface callback base class
|
||||
class handler_interface {
|
||||
public:
|
||||
virtual ~handler_interface() {}
|
||||
|
||||
virtual void on_handshake_init(connection_ptr con) {}
|
||||
|
||||
virtual void validate(connection_ptr con) {}
|
||||
virtual void on_open(connection_ptr con) {}
|
||||
virtual void on_close(connection_ptr con) {}
|
||||
virtual void on_fail(connection_ptr con) {}
|
||||
|
||||
virtual void on_message(connection_ptr con,message::data_ptr) {}
|
||||
|
||||
virtual bool on_ping(connection_ptr con,std::string) {return true;}
|
||||
virtual void on_pong(connection_ptr con,std::string) {}
|
||||
virtual void on_pong_timeout(connection_ptr con,std::string) {}
|
||||
virtual void http(connection_ptr con) {}
|
||||
|
||||
virtual void on_send_empty(connection_ptr con) {}
|
||||
};
|
||||
|
||||
server(boost::asio::io_service& m)
|
||||
: m_endpoint(static_cast< endpoint_type& >(*this)),
|
||||
m_io_service(m),
|
||||
// the only way to set an endpoint address family appears to be using
|
||||
// this constructor, which also requires a port. This port number can be
|
||||
// ignored, as it is always overwriten later by the listen() member func
|
||||
m_acceptor(m),
|
||||
m_state(IDLE),
|
||||
m_timer(m,boost::posix_time::seconds(0)) {}
|
||||
|
||||
void start_listen(uint16_t port, size_t num_threads = 1);
|
||||
void start_listen(const boost::asio::ip::tcp::endpoint& e, size_t num_threads = 1);
|
||||
// uses internal resolver
|
||||
void start_listen(const std::string &host, const std::string &service, size_t num_threads = 1);
|
||||
|
||||
template <typename InternetProtocol>
|
||||
void start_listen(const InternetProtocol &internet_protocol, uint16_t port, size_t num_threads = 1) {
|
||||
m_endpoint.m_alog->at(log::alevel::DEVEL)
|
||||
<< "role::server listening on port " << port << log::endl;
|
||||
boost::asio::ip::tcp::endpoint e(internet_protocol, port);
|
||||
start_listen(e,num_threads);
|
||||
}
|
||||
|
||||
void stop_listen(bool join);
|
||||
|
||||
// legacy interface
|
||||
void listen(uint16_t port, size_t num_threads = 1) {
|
||||
start_listen(port,num_threads>1 ? num_threads : 0);
|
||||
if(num_threads > 1) stop_listen(true);
|
||||
}
|
||||
void listen(const boost::asio::ip::tcp::endpoint& e, size_t num_threads = 1) {
|
||||
start_listen(e,num_threads>1 ? num_threads : 0);
|
||||
if(num_threads > 1) stop_listen(true);
|
||||
}
|
||||
void listen(const std::string &host, const std::string &service, size_t num_threads = 1) {
|
||||
start_listen(host,service,num_threads>1 ? num_threads : 0);
|
||||
if(num_threads > 1) stop_listen(true);
|
||||
}
|
||||
template <typename InternetProtocol>
|
||||
void listen(const InternetProtocol &internet_protocol, uint16_t port, size_t num_threads = 1) {
|
||||
start_listen(internet_protocol,port,num_threads>1 ? num_threads : 0);
|
||||
if(num_threads > 1) stop_listen(true);
|
||||
}
|
||||
|
||||
protected:
|
||||
bool is_server() {
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
enum state {
|
||||
IDLE = 0,
|
||||
LISTENING = 1,
|
||||
STOPPING = 2,
|
||||
STOPPED = 3
|
||||
};
|
||||
|
||||
// start_accept creates a new connection and begins an async_accept on it
|
||||
void start_accept();
|
||||
|
||||
// handle_accept will begin the connection's read/write loop and then reset
|
||||
// the server to accept a new connection. Errors returned by async_accept
|
||||
// are logged and ingored.
|
||||
void handle_accept(connection_ptr con,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
endpoint_type& m_endpoint;
|
||||
boost::asio::io_service& m_io_service;
|
||||
boost::asio::ip::tcp::acceptor m_acceptor;
|
||||
state m_state;
|
||||
|
||||
boost::asio::deadline_timer m_timer;
|
||||
|
||||
std::vector< boost::shared_ptr<boost::thread> > m_listening_threads;
|
||||
};
|
||||
|
||||
template <class endpoint>
|
||||
void server<endpoint>::start_listen(const boost::asio::ip::tcp::endpoint& e,size_t num_threads) {
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
if (m_state != IDLE) {
|
||||
throw exception("listen called from invalid state.");
|
||||
}
|
||||
|
||||
m_acceptor.open(e.protocol());
|
||||
m_acceptor.set_option(boost::asio::socket_base::reuse_address(true));
|
||||
m_acceptor.bind(e);
|
||||
m_acceptor.listen();
|
||||
|
||||
this->start_accept();
|
||||
}
|
||||
|
||||
if (num_threads > MAX_THREAD_POOL_SIZE) {
|
||||
throw exception("listen called with invalid num_threads value");
|
||||
}
|
||||
|
||||
m_state = LISTENING;
|
||||
|
||||
for (std::size_t i = 0; i < num_threads; ++i) {
|
||||
boost::shared_ptr<boost::thread> thread(
|
||||
new boost::thread(boost::bind(
|
||||
&endpoint_type::run_internal,
|
||||
&m_endpoint
|
||||
))
|
||||
);
|
||||
m_listening_threads.push_back(thread);
|
||||
}
|
||||
|
||||
if(num_threads == 0)
|
||||
{
|
||||
for (;;) {
|
||||
try {
|
||||
m_endpoint.run_internal();
|
||||
break;
|
||||
} catch (const std::exception & e) { // Don't let an exception trash the endpoint DJS
|
||||
m_endpoint.elog().at(log::elevel::RERROR) << "run_internal exception: " << e.what() << log::endl;
|
||||
}
|
||||
}
|
||||
m_state = IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
void server<endpoint>::stop_listen(bool join) {
|
||||
{
|
||||
boost::unique_lock<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
if (m_state != LISTENING) {
|
||||
throw exception("stop_listen called from invalid state");
|
||||
}
|
||||
|
||||
m_acceptor.close();
|
||||
}
|
||||
|
||||
if(join) {
|
||||
for (std::size_t i = 0; i < m_listening_threads.size(); ++i) {
|
||||
m_listening_threads[i]->join();
|
||||
}
|
||||
}
|
||||
|
||||
m_listening_threads.clear();
|
||||
|
||||
m_state = IDLE;
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
void server<endpoint>::start_listen(uint16_t port, size_t num_threads) {
|
||||
start_listen(boost::asio::ip::tcp::v6(), port, num_threads);
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
void server<endpoint>::start_listen(const std::string &host, const std::string &service, size_t num_threads) {
|
||||
boost::asio::ip::tcp::resolver resolver(m_io_service);
|
||||
boost::asio::ip::tcp::resolver::query query(host, service);
|
||||
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
if (endpoint_iterator == end) {
|
||||
throw std::invalid_argument("Can't resolve host/service to listen");
|
||||
}
|
||||
const boost::asio::ip::tcp::endpoint &ep = *endpoint_iterator;
|
||||
start_listen(ep,num_threads);
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
void server<endpoint>::start_accept() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
connection_ptr con = m_endpoint.create_connection();
|
||||
|
||||
if (con == connection_ptr()) {
|
||||
// the endpoint is no longer capable of accepting new connections.
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT)
|
||||
<< "Connection refused because endpoint is out of resources or closing."
|
||||
<< log::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_acceptor.async_accept(
|
||||
con->get_raw_socket(),
|
||||
boost::bind(
|
||||
&type::handle_accept,
|
||||
this,
|
||||
con,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// handle_accept will begin the connection's read/write loop and then reset
|
||||
// the server to accept a new connection. Errors returned by async_accept
|
||||
// are logged and ingored.
|
||||
template <class endpoint>
|
||||
void server<endpoint>::handle_accept(connection_ptr con,
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
bool delay = false;
|
||||
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_endpoint.m_lock);
|
||||
|
||||
try
|
||||
{
|
||||
if (error) {
|
||||
con->m_fail_code = fail::status::SYSTEM;
|
||||
con->m_fail_system = error;
|
||||
|
||||
if (error == boost::system::errc::too_many_files_open) {
|
||||
con->m_fail_reason = "too many files open";
|
||||
delay = true;
|
||||
|
||||
} else if (error == boost::asio::error::operation_aborted) {
|
||||
con->m_fail_reason = "io_service operation canceled";
|
||||
|
||||
// the operation was canceled. This was probably due to the
|
||||
// io_service being stopped.
|
||||
} else {
|
||||
con->m_fail_reason = "unknown";
|
||||
}
|
||||
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "async_accept returned error: " << error
|
||||
<< " (" << con->m_fail_reason << ")" << log::endl;
|
||||
|
||||
con->terminate(false);
|
||||
} else {
|
||||
con->start();
|
||||
}
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{ // We must call start_accept, even if we throw DJS
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "handle_accept caught exception: " << e.what() << log::endl;
|
||||
}
|
||||
|
||||
if (delay)
|
||||
{ // Don't spin if too many files are open DJS
|
||||
m_timer.expires_from_now(boost::posix_time::milliseconds(500));
|
||||
m_timer.async_wait(boost::bind(&type::start_accept,this));
|
||||
}
|
||||
else
|
||||
this->start_accept();
|
||||
}
|
||||
|
||||
// server<endpoint>::connection<connnection_type> Implimentation
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::select_subprotocol(
|
||||
const std::string& value)
|
||||
{
|
||||
// TODO: should this be locked?
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_requested_subprotocols.begin(),
|
||||
m_requested_subprotocols.end(),
|
||||
value);
|
||||
|
||||
if (value != "" && it == m_requested_subprotocols.end()) {
|
||||
throw std::invalid_argument("Attempted to choose a subprotocol not proposed by the client");
|
||||
}
|
||||
|
||||
m_subprotocol = value;
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::select_extension(
|
||||
const std::string& value)
|
||||
{
|
||||
// TODO: should this be locked?
|
||||
|
||||
if (value == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string>::iterator it;
|
||||
|
||||
it = std::find(m_requested_extensions.begin(),
|
||||
m_requested_extensions.end(),
|
||||
value);
|
||||
|
||||
if (it == m_requested_extensions.end()) {
|
||||
throw std::invalid_argument("Attempted to choose an extension not proposed by the client");
|
||||
}
|
||||
|
||||
m_extensions.push_back(value);
|
||||
}
|
||||
|
||||
// Valid if get_version() returns -1 (ie this is an http connection)
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::set_body(
|
||||
const std::string& value)
|
||||
{
|
||||
// TODO: should this be locked?
|
||||
|
||||
if (m_connection.m_version != -1) {
|
||||
// TODO: throw exception
|
||||
throw std::invalid_argument("set_body called from invalid state");
|
||||
}
|
||||
|
||||
m_response.set_body(value);
|
||||
}
|
||||
|
||||
/// initiates an async read for an HTTP header
|
||||
/**
|
||||
* Thread Safety: locks connection
|
||||
*/
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::async_init() {
|
||||
boost::lock_guard<boost::recursive_mutex> lock(m_connection.m_lock);
|
||||
|
||||
m_connection.get_handler()->on_handshake_init(m_connection.shared_from_this());
|
||||
|
||||
// TODO: make this value configurable
|
||||
m_connection.register_timeout(5000,fail::status::TIMEOUT_WS,
|
||||
"Timeout on WebSocket handshake");
|
||||
|
||||
boost::shared_ptr<std::string> stringPtr = boost::make_shared<std::string>();
|
||||
m_connection.get_socket().async_read_until(
|
||||
m_connection.buffer(),
|
||||
boost::bind(&match_header, stringPtr, _1, _2),
|
||||
m_connection.get_strand().wrap(boost::bind(
|
||||
&type::handle_read_request,
|
||||
m_connection.shared_from_this(),
|
||||
stringPtr,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
/// processes the response from an async read for an HTTP header
|
||||
/**
|
||||
* Thread Safety: async asio calls are not thread safe
|
||||
*/
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::handle_read_request(
|
||||
boost::shared_ptr<std::string> header,
|
||||
const boost::system::error_code& error, std::size_t /*bytes_transferred*/)
|
||||
{
|
||||
if (error) {
|
||||
// log error
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error reading HTTP request. code: " << error << log::endl;
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection.buffer().consume(header->size());
|
||||
|
||||
try {
|
||||
std::istringstream request(*header);
|
||||
|
||||
if (!m_request.parse_complete(request)) {
|
||||
// not a valid HTTP request/response
|
||||
if (m_request.method().find("<policy-file-request/>") != std::string::npos)
|
||||
{ // set m_version to -1, call async_write->handle_write_response
|
||||
std::string reply =
|
||||
"<?xml version=\"1.0\"?><cross-domain-policy>"
|
||||
"<allow-access-from domain=\"*\" to-ports=\"";
|
||||
reply += boost::lexical_cast<std::string>(m_connection.get_raw_socket().local_endpoint().port());
|
||||
reply += "\"/></cross-domain-policy>";
|
||||
reply.append("\0", 1);
|
||||
|
||||
m_version = -1;
|
||||
shared_const_buffer buffer(reply);
|
||||
m_connection.get_socket().async_write(
|
||||
shared_const_buffer(reply),
|
||||
boost::bind(
|
||||
&type::handle_write_response,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
throw http::exception("Received invalid HTTP Request",http::status_code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
// TODO: is there a way to short circuit this or something?
|
||||
// This is often useful but slow to generate.
|
||||
//m_endpoint.m_alog.at(log::alevel::DEBUG_HANDSHAKE) << m_request.raw() << log::endl;
|
||||
|
||||
std::string h = m_request.header("Upgrade");
|
||||
if (boost::ifind_first(h,"websocket")) {
|
||||
// Version is stored in the Sec-WebSocket-Version header for all
|
||||
// versions after draft Hybi 00/Hixie 76. The absense of a version
|
||||
// header is assumed to mean Hybi 00.
|
||||
h = m_request.header("Sec-WebSocket-Version");
|
||||
if (h == "") {
|
||||
m_version = 0;
|
||||
} else {
|
||||
m_version = atoi(h.c_str());
|
||||
if (m_version == 0) {
|
||||
throw(http::exception("Unable to determine connection version",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
}
|
||||
|
||||
// Choose an appropriate websocket processor based on the version
|
||||
if (m_version == 0) {
|
||||
m_connection.m_processor = processor::ptr(
|
||||
new processor::hybi_legacy<connection_type>(m_connection)
|
||||
);
|
||||
|
||||
// Hybi legacy requires some extra out of band bookkeeping that
|
||||
// future versions wont. We need to pull off an additional eight
|
||||
// bytes after the /r/n/r/n and store them somewhere that the
|
||||
// processor can find them.
|
||||
char foo[8];
|
||||
|
||||
request.read(foo,8);
|
||||
|
||||
if (request.gcount() != 8) {
|
||||
size_t left = 8 - static_cast<size_t>(request.gcount());
|
||||
// This likely occurs because the full key3 wasn't included
|
||||
// in the asio read. It is likely that the extra bytes are
|
||||
// actually on the wire and another asio read would get them
|
||||
// Fixing this will require a way of restarting the
|
||||
// handshake read and storing the existing bytes until that
|
||||
// comes back. Issue #101
|
||||
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Short Key 3: " << zsutil::to_hex(std::string(foo))
|
||||
<< " bytes missing: " << left
|
||||
<< " eofbit: " << (request.eof() ? "true" : "false")
|
||||
<< " failbit: " << (request.fail() ? "true" : "false")
|
||||
<< " badbit: " << (request.bad() ? "true" : "false")
|
||||
<< " goodbit: " << (request.good() ? "true" : "false")
|
||||
<< log::endl;
|
||||
|
||||
throw http::exception("Full Key3 not found in first chop",
|
||||
http::status_code::INTERNAL_SERVER_ERROR);
|
||||
/*m_request.add_header("Sec-WebSocket-Key3",std::string(foo));
|
||||
|
||||
|
||||
|
||||
boost::asio::async_read(
|
||||
m_connection.get_socket(),
|
||||
m_connection.buffer(),
|
||||
boost::asio::transfer_at_least(left),
|
||||
m_connection.get_strand().wrap(boost::bind(
|
||||
&type::handle_short_key3,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred
|
||||
))
|
||||
);
|
||||
return;*/
|
||||
}
|
||||
m_request.add_header("Sec-WebSocket-Key3",std::string(foo));
|
||||
} else if (m_version == 7 || m_version == 8 || m_version == 13) {
|
||||
m_connection.m_processor = processor::ptr(
|
||||
new processor::hybi<connection_type>(m_connection)
|
||||
);
|
||||
} else {
|
||||
// version does not match any processor we have avaliable. Send
|
||||
// an HTTP error and return the versions we do support in a the
|
||||
// appropriate response header.
|
||||
m_response.add_header("Sec-WebSocket-Version","13, 8, 7");
|
||||
|
||||
throw(http::exception("Unsupported WebSocket version",http::status_code::BAD_REQUEST));
|
||||
}
|
||||
|
||||
m_connection.m_processor->validate_handshake(m_request);
|
||||
|
||||
// Extract subprotocols
|
||||
std::string subprotocols = m_request.header("Sec-WebSocket-Protocol");
|
||||
if(subprotocols.length() > 0) {
|
||||
boost::char_separator<char> sep(",");
|
||||
boost::tokenizer< boost::char_separator<char> > tokens(subprotocols, sep);
|
||||
for(boost::tokenizer< boost::char_separator<char> >::iterator it = tokens.begin(); it != tokens.end(); ++it){
|
||||
std::string proto = *it;
|
||||
boost::trim(proto);
|
||||
if (proto.length() > 0){
|
||||
m_requested_subprotocols.push_back(proto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_origin = m_connection.m_processor->get_origin(m_request);
|
||||
m_uri = m_connection.m_processor->get_uri(m_request);
|
||||
|
||||
m_endpoint.get_handler()->validate(m_connection.shared_from_this());
|
||||
|
||||
m_response.set_status(http::status_code::SWITCHING_PROTOCOLS);
|
||||
} else {
|
||||
// should there be a more encapsulated http processor here?
|
||||
m_origin = m_request.header("Origin");
|
||||
|
||||
// Set URI
|
||||
std::string h = m_request.header("Host");
|
||||
|
||||
size_t last_colon = h.rfind(":");
|
||||
size_t last_sbrace = h.rfind("]");
|
||||
|
||||
if (last_colon == std::string::npos ||
|
||||
(last_sbrace != std::string::npos && last_sbrace > last_colon))
|
||||
{
|
||||
// TODO: this makes the assumption that WS and HTTP
|
||||
// default ports are the same.
|
||||
m_uri.reset(new uri(m_connection.is_secure(),h,m_request.uri()));
|
||||
} else {
|
||||
m_uri.reset(new uri(m_connection.is_secure(),
|
||||
h.substr(0,last_colon),
|
||||
h.substr(last_colon+1),
|
||||
m_request.uri()));
|
||||
}
|
||||
|
||||
// continue as HTTP?
|
||||
m_endpoint.get_handler()->http(m_connection.shared_from_this());
|
||||
|
||||
m_response.set_status(http::status_code::OK);
|
||||
}
|
||||
} catch (const http::exception& e) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR) << e.what() << log::endl;
|
||||
m_response.set_status(e.m_error_code,e.m_error_msg);
|
||||
m_response.set_body(e.m_body);
|
||||
} catch (const uri_exception& e) {
|
||||
// there was some error building the uri
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR) << e.what() << log::endl;
|
||||
m_response.set_status(http::status_code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
write_response();
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::handle_short_key3 (
|
||||
const boost::system::error_code& error, std::size_t /*bytes_transferred*/)
|
||||
{
|
||||
if (error) {
|
||||
// log error
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Error reading HTTP request. code: " << error << log::endl;
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::istream request(&m_connection.buffer());
|
||||
|
||||
std::string foo = m_request.header("Sec-WebSocket-Key3");
|
||||
|
||||
size_t left = 8-foo.size();
|
||||
|
||||
if (left == 0) {
|
||||
// ?????
|
||||
throw http::exception("handle_short_key3 called without short key",
|
||||
http::status_code::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
char foo2[9];
|
||||
|
||||
request.get(foo2,left);
|
||||
|
||||
if (request.gcount() != left) {
|
||||
// ?????
|
||||
throw http::exception("Full Key3 not found",
|
||||
http::status_code::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Recovered from Short Key 3: " << left << log::endl;
|
||||
|
||||
foo.append(std::string(foo2));
|
||||
|
||||
m_request.replace_header("Sec-WebSocket-Key3",foo2);
|
||||
|
||||
m_connection.m_processor->validate_handshake(m_request);
|
||||
m_origin = m_connection.m_processor->get_origin(m_request);
|
||||
m_uri = m_connection.m_processor->get_uri(m_request);
|
||||
|
||||
m_endpoint.get_handler()->validate(m_connection.shared_from_this());
|
||||
|
||||
m_response.set_status(http::status_code::SWITCHING_PROTOCOLS);
|
||||
} catch (const http::exception& e) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR) << e.what() << log::endl;
|
||||
m_response.set_status(e.m_error_code,e.m_error_msg);
|
||||
m_response.set_body(e.m_body);
|
||||
} catch (const uri_exception& e) {
|
||||
// there was some error building the uri
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR) << e.what() << log::endl;
|
||||
m_response.set_status(http::status_code::BAD_REQUEST);
|
||||
}
|
||||
|
||||
write_response();
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::write_response() {
|
||||
bool ws_response = true;
|
||||
|
||||
m_response.set_version("HTTP/1.1");
|
||||
|
||||
if (m_response.get_status_code() == http::status_code::SWITCHING_PROTOCOLS) {
|
||||
// websocket response
|
||||
m_connection.m_processor->handshake_response(m_request,m_response);
|
||||
|
||||
if (m_subprotocol != "") {
|
||||
m_response.replace_header("Sec-WebSocket-Protocol",m_subprotocol);
|
||||
}
|
||||
|
||||
// TODO: return negotiated extensions
|
||||
} else {
|
||||
// TODO: HTTP response
|
||||
ws_response = false;
|
||||
}
|
||||
|
||||
m_response.replace_header("Server",USER_AGENT);
|
||||
|
||||
std::string raw = m_response.raw();
|
||||
|
||||
// Hack for legacy HyBi
|
||||
if (ws_response && m_version == 0) {
|
||||
raw += boost::dynamic_pointer_cast<processor::hybi_legacy<connection_type> >(m_connection.m_processor)->get_key3();
|
||||
}
|
||||
|
||||
shared_const_buffer buffer(raw);
|
||||
|
||||
m_endpoint.m_alog->at(log::alevel::DEBUG_HANDSHAKE) << raw << log::endl;
|
||||
|
||||
m_connection.get_socket().async_write(
|
||||
//boost::asio::buffer(raw),
|
||||
buffer,
|
||||
boost::bind(
|
||||
&type::handle_write_response,
|
||||
m_connection.shared_from_this(),
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::handle_write_response(
|
||||
const boost::system::error_code& error)
|
||||
{
|
||||
if (error) {
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Network error writing handshake respons. code: " << error
|
||||
<< log::endl;
|
||||
|
||||
m_connection.terminate(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS) {
|
||||
if (m_version == -1) {
|
||||
// if this was not a websocket connection, we have written
|
||||
// the expected response and the connection can be closed.
|
||||
} else {
|
||||
// this was a websocket connection that ended in an error
|
||||
m_endpoint.m_elog->at(log::elevel::RERROR)
|
||||
<< "Handshake ended with HTTP error: "
|
||||
<< m_response.get_status_code() << " "
|
||||
<< m_response.get_status_msg() << log::endl;
|
||||
}
|
||||
m_connection.terminate(true);
|
||||
return;
|
||||
}
|
||||
|
||||
m_connection.cancel_timeout();
|
||||
|
||||
log_open_result();
|
||||
|
||||
m_connection.m_state = session::state::OPEN;
|
||||
|
||||
m_connection.get_handler()->on_open(m_connection.shared_from_this());
|
||||
|
||||
get_io_service().post(
|
||||
m_connection.m_strand.wrap(boost::bind(
|
||||
&connection_type::handle_read_frame,
|
||||
m_connection.shared_from_this(),
|
||||
boost::system::error_code()
|
||||
))
|
||||
);
|
||||
|
||||
//m_connection.handle_read_frame(boost::system::error_code());
|
||||
}
|
||||
|
||||
template <class endpoint>
|
||||
template <class connection_type>
|
||||
void server<endpoint>::connection<connection_type>::log_open_result() {
|
||||
std::stringstream version;
|
||||
version << "v" << m_version << " ";
|
||||
|
||||
std::string remote;
|
||||
boost::system::error_code ec;
|
||||
boost::asio::ip::tcp::endpoint ep = m_connection.get_raw_socket().remote_endpoint(ec);
|
||||
if (ec) {
|
||||
m_endpoint.m_elog->at(log::elevel::WARN)
|
||||
<< "Error getting remote endpoint. code: " << ec << log::endl;
|
||||
}
|
||||
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT)
|
||||
<< (m_version == -1 ? "HTTP" : "WebSocket") << " Connection ";
|
||||
|
||||
if (ec) {
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT) << "Unknown";
|
||||
} else {
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT) << ep;
|
||||
}
|
||||
|
||||
m_endpoint.m_alog->at(log::alevel::CONNECT) << " "
|
||||
<< (m_version == -1 ? "" : version.str())
|
||||
<< (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent"))
|
||||
<< " " << (m_uri ? m_uri->get_resource() : "uri is NULL") << " "
|
||||
<< m_response.get_status_code() << log::endl;
|
||||
}
|
||||
|
||||
} // namespace role
|
||||
} // namespace websocketpp
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // WEBSOCKETPP_ROLE_SERVER_HPP
|
||||
41
src/sha1/Makefile
Executable file
41
src/sha1/Makefile
Executable file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Makefile
|
||||
#
|
||||
# Copyright (C) 1998, 2009
|
||||
# Paul E. Jones <paulej@packetizer.com>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#############################################################################
|
||||
# $Id: Makefile 12 2009-06-22 19:34:25Z paulej $
|
||||
#############################################################################
|
||||
#
|
||||
# Description:
|
||||
# This is a makefile for UNIX to build the programs sha, shacmp, and
|
||||
# shatest
|
||||
#
|
||||
#
|
||||
|
||||
CC = g++
|
||||
|
||||
CFLAGS = -c -O2 -Wall -D_FILE_OFFSET_BITS=64
|
||||
|
||||
LIBS =
|
||||
|
||||
OBJS = sha1.o
|
||||
|
||||
all: sha shacmp shatest
|
||||
|
||||
sha: sha.o $(OBJS)
|
||||
$(CC) -o $@ sha.o $(OBJS) $(LIBS)
|
||||
|
||||
shacmp: shacmp.o $(OBJS)
|
||||
$(CC) -o $@ shacmp.o $(OBJS) $(LIBS)
|
||||
|
||||
shatest: shatest.o $(OBJS)
|
||||
$(CC) -o $@ shatest.o $(OBJS) $(LIBS)
|
||||
|
||||
%.o: %.cpp
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
$(RM) *.o sha shacmp shatest
|
||||
48
src/sha1/Makefile.nt
Executable file
48
src/sha1/Makefile.nt
Executable file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Makefile.nt
|
||||
#
|
||||
# Copyright (C) 1998, 2009
|
||||
# Paul E. Jones <paulej@packetizer.com>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
#############################################################################
|
||||
# $Id: Makefile.nt 13 2009-06-22 20:20:32Z paulej $
|
||||
#############################################################################
|
||||
#
|
||||
# Description:
|
||||
# This is a makefile for Win32 to build the programs sha, shacmp, and
|
||||
# shatest
|
||||
#
|
||||
# Portability Issues:
|
||||
# Designed to work with Visual C++
|
||||
#
|
||||
#
|
||||
|
||||
.silent:
|
||||
|
||||
!include <win32.mak>
|
||||
|
||||
RM = del /q
|
||||
|
||||
LIBS = $(conlibs) setargv.obj
|
||||
|
||||
CFLAGS = -D _CRT_SECURE_NO_WARNINGS /EHsc /O2 /W3
|
||||
|
||||
OBJS = sha1.obj
|
||||
|
||||
all: sha.exe shacmp.exe shatest.exe
|
||||
|
||||
sha.exe: sha.obj $(OBJS)
|
||||
$(link) $(conflags) -out:$@ sha.obj $(OBJS) $(LIBS)
|
||||
|
||||
shacmp.exe: shacmp.obj $(OBJS)
|
||||
$(link) $(conflags) -out:$@ shacmp.obj $(OBJS) $(LIBS)
|
||||
|
||||
shatest.exe: shatest.obj $(OBJS)
|
||||
$(link) $(conflags) -out:$@ shatest.obj $(OBJS) $(LIBS)
|
||||
|
||||
.cpp.obj:
|
||||
$(cc) $(CFLAGS) $(cflags) $(cvars) $<
|
||||
|
||||
clean:
|
||||
$(RM) *.obj sha.exe shacmp.exe shatest.exe
|
||||
14
src/sha1/license.txt
Executable file
14
src/sha1/license.txt
Executable file
@@ -0,0 +1,14 @@
|
||||
Copyright (C) 1998, 2009
|
||||
Paul E. Jones <paulej@packetizer.com>
|
||||
|
||||
Freeware Public License (FPL)
|
||||
|
||||
This software is licensed as "freeware." Permission to distribute
|
||||
this software in source and binary forms, including incorporation
|
||||
into other products, is hereby granted without a fee. THIS SOFTWARE
|
||||
IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHOR SHALL NOT BE HELD
|
||||
LIABLE FOR ANY DAMAGES RESULTING FROM THE USE OF THIS SOFTWARE, EITHER
|
||||
DIRECTLY OR INDIRECTLY, INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA
|
||||
OR DATA BEING RENDERED INACCURATE.
|
||||
176
src/sha1/sha.cpp
Executable file
176
src/sha1/sha.cpp
Executable file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* sha.cpp
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: sha.cpp 13 2009-06-22 20:20:32Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This utility will display the message digest (fingerprint) for
|
||||
* the specified file(s).
|
||||
*
|
||||
* Portability Issues:
|
||||
* None.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include "sha1.h"
|
||||
|
||||
/*
|
||||
* Function prototype
|
||||
*/
|
||||
void usage();
|
||||
|
||||
|
||||
/*
|
||||
* main
|
||||
*
|
||||
* Description:
|
||||
* This is the entry point for the program
|
||||
*
|
||||
* Parameters:
|
||||
* argc: [in]
|
||||
* This is the count of arguments in the argv array
|
||||
* argv: [in]
|
||||
* This is an array of filenames for which to compute message digests
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SHA1 sha; // SHA-1 class
|
||||
FILE *fp; // File pointer for reading files
|
||||
char c; // Character read from file
|
||||
unsigned message_digest[5]; // Message digest from "sha"
|
||||
int i; // Counter
|
||||
bool reading_stdin; // Are we reading standard in?
|
||||
bool read_stdin = false; // Have we read stdin?
|
||||
|
||||
/*
|
||||
* Check the program arguments and print usage information if -?
|
||||
* or --help is passed as the first argument.
|
||||
*/
|
||||
if (argc > 1 && (!strcmp(argv[1],"-?") || !strcmp(argv[1],"--help")))
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* For each filename passed in on the command line, calculate the
|
||||
* SHA-1 value and display it.
|
||||
*/
|
||||
for(i = 0; i < argc; i++)
|
||||
{
|
||||
/*
|
||||
* We start the counter at 0 to guarantee entry into the for loop.
|
||||
* So if 'i' is zero, we will increment it now. If there is no
|
||||
* argv[1], we will use STDIN below.
|
||||
*/
|
||||
if (i == 0)
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (argc == 1 || !strcmp(argv[i],"-"))
|
||||
{
|
||||
#ifdef WIN32
|
||||
_setmode(_fileno(stdin), _O_BINARY);
|
||||
#endif
|
||||
fp = stdin;
|
||||
reading_stdin = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(fp = fopen(argv[i],"rb")))
|
||||
{
|
||||
fprintf(stderr, "sha: unable to open file %s\n", argv[i]);
|
||||
return 2;
|
||||
}
|
||||
reading_stdin = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do not want to read STDIN multiple times
|
||||
*/
|
||||
if (reading_stdin)
|
||||
{
|
||||
if (read_stdin)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
read_stdin = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the SHA1 object and process input
|
||||
*/
|
||||
sha.Reset();
|
||||
|
||||
c = fgetc(fp);
|
||||
while(!feof(fp))
|
||||
{
|
||||
sha.Input(c);
|
||||
c = fgetc(fp);
|
||||
}
|
||||
|
||||
if (!reading_stdin)
|
||||
{
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (!sha.Result(message_digest))
|
||||
{
|
||||
fprintf(stderr,"sha: could not compute message digest for %s\n",
|
||||
reading_stdin?"STDIN":argv[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf( "%08X %08X %08X %08X %08X - %s\n",
|
||||
message_digest[0],
|
||||
message_digest[1],
|
||||
message_digest[2],
|
||||
message_digest[3],
|
||||
message_digest[4],
|
||||
reading_stdin?"STDIN":argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* usage
|
||||
*
|
||||
* Description:
|
||||
* This function will display program usage information to the user.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void usage()
|
||||
{
|
||||
printf("usage: sha <file> [<file> ...]\n");
|
||||
printf("\tThis program will display the message digest (fingerprint)\n");
|
||||
printf("\tfor files using the Secure Hashing Algorithm (SHA-1).\n");
|
||||
}
|
||||
591
src/sha1/sha1.cpp
Executable file
591
src/sha1/sha1.cpp
Executable file
@@ -0,0 +1,591 @@
|
||||
/*
|
||||
* sha1.cpp
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: sha1.cpp 12 2009-06-22 19:34:25Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This class implements the Secure Hashing Standard as defined
|
||||
* in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* The Secure Hashing Standard, which uses the Secure Hashing
|
||||
* Algorithm (SHA), produces a 160-bit message digest for a
|
||||
* given data stream. In theory, it is highly improbable that
|
||||
* two messages will produce the same message digest. Therefore,
|
||||
* this algorithm can serve as a means of providing a "fingerprint"
|
||||
* for a message.
|
||||
*
|
||||
* Portability Issues:
|
||||
* SHA-1 is defined in terms of 32-bit "words". This code was
|
||||
* written with the expectation that the processor has at least
|
||||
* a 32-bit machine word size. If the machine word size is larger,
|
||||
* the code should still function properly. One caveat to that
|
||||
* is that the input functions taking characters and character arrays
|
||||
* assume that only 8 bits of information are stored in each character.
|
||||
*
|
||||
* Caveats:
|
||||
* SHA-1 is designed to work with messages less than 2^64 bits long.
|
||||
* Although SHA-1 allows a message digest to be generated for
|
||||
* messages of any number of bits less than 2^64, this implementation
|
||||
* only works with messages with a length that is a multiple of 8
|
||||
* bits.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
using websocketpp::SHA1;
|
||||
|
||||
/*
|
||||
* SHA1
|
||||
*
|
||||
* Description:
|
||||
* This is the constructor for the sha1 class.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
SHA1::SHA1()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* ~SHA1
|
||||
*
|
||||
* Description:
|
||||
* This is the destructor for the sha1 class
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
SHA1::~SHA1()
|
||||
{
|
||||
// The destructor does nothing
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset
|
||||
*
|
||||
* Description:
|
||||
* This function will initialize the sha1 class member variables
|
||||
* in preparation for computing a new message digest.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::Reset()
|
||||
{
|
||||
Length_Low = 0;
|
||||
Length_High = 0;
|
||||
Message_Block_Index = 0;
|
||||
|
||||
H[0] = 0x67452301;
|
||||
H[1] = 0xEFCDAB89;
|
||||
H[2] = 0x98BADCFE;
|
||||
H[3] = 0x10325476;
|
||||
H[4] = 0xC3D2E1F0;
|
||||
|
||||
Computed = false;
|
||||
Corrupted = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Result
|
||||
*
|
||||
* Description:
|
||||
* This function will return the 160-bit message digest into the
|
||||
* array provided.
|
||||
*
|
||||
* Parameters:
|
||||
* message_digest_array: [out]
|
||||
* This is an array of five unsigned integers which will be filled
|
||||
* with the message digest that has been computed.
|
||||
*
|
||||
* Returns:
|
||||
* True if successful, false if it failed.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
bool SHA1::Result(unsigned *message_digest_array)
|
||||
{
|
||||
int i; // Counter
|
||||
|
||||
if (Corrupted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Computed)
|
||||
{
|
||||
PadMessage();
|
||||
Computed = true;
|
||||
}
|
||||
|
||||
for(i = 0; i < 5; i++)
|
||||
{
|
||||
message_digest_array[i] = H[i];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion of
|
||||
* the message.
|
||||
*
|
||||
* Parameters:
|
||||
* message_array: [in]
|
||||
* An array of characters representing the next portion of the
|
||||
* message.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::Input( const unsigned char *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
if (!length)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Computed || Corrupted)
|
||||
{
|
||||
Corrupted = true;
|
||||
return;
|
||||
}
|
||||
|
||||
while(length-- && !Corrupted)
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = (*message_array & 0xFF);
|
||||
|
||||
Length_Low += 8;
|
||||
Length_Low &= 0xFFFFFFFF; // Force it to 32 bits
|
||||
if (Length_Low == 0)
|
||||
{
|
||||
Length_High++;
|
||||
Length_High &= 0xFFFFFFFF; // Force it to 32 bits
|
||||
if (Length_High == 0)
|
||||
{
|
||||
Corrupted = true; // Message is too long
|
||||
}
|
||||
}
|
||||
|
||||
if (Message_Block_Index == 64)
|
||||
{
|
||||
ProcessMessageBlock();
|
||||
}
|
||||
|
||||
message_array++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts an array of octets as the next portion of
|
||||
* the message.
|
||||
*
|
||||
* Parameters:
|
||||
* message_array: [in]
|
||||
* An array of characters representing the next portion of the
|
||||
* message.
|
||||
* length: [in]
|
||||
* The length of the message_array
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::Input( const char *message_array,
|
||||
unsigned length)
|
||||
{
|
||||
Input((unsigned char *) message_array, length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts a single octets as the next message element.
|
||||
*
|
||||
* Parameters:
|
||||
* message_element: [in]
|
||||
* The next octet in the message.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::Input(unsigned char message_element)
|
||||
{
|
||||
Input(&message_element, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Input
|
||||
*
|
||||
* Description:
|
||||
* This function accepts a single octet as the next message element.
|
||||
*
|
||||
* Parameters:
|
||||
* message_element: [in]
|
||||
* The next octet in the message.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::Input(char message_element)
|
||||
{
|
||||
Input((unsigned char *) &message_element, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* operator<<
|
||||
*
|
||||
* Description:
|
||||
* This operator makes it convenient to provide character strings to
|
||||
* the SHA1 object for processing.
|
||||
*
|
||||
* Parameters:
|
||||
* message_array: [in]
|
||||
* The character array to take as input.
|
||||
*
|
||||
* Returns:
|
||||
* A reference to the SHA1 object.
|
||||
*
|
||||
* Comments:
|
||||
* Each character is assumed to hold 8 bits of information.
|
||||
*
|
||||
*/
|
||||
SHA1& SHA1::operator<<(const char *message_array)
|
||||
{
|
||||
const char *p = message_array;
|
||||
|
||||
while(*p)
|
||||
{
|
||||
Input(*p);
|
||||
p++;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* operator<<
|
||||
*
|
||||
* Description:
|
||||
* This operator makes it convenient to provide character strings to
|
||||
* the SHA1 object for processing.
|
||||
*
|
||||
* Parameters:
|
||||
* message_array: [in]
|
||||
* The character array to take as input.
|
||||
*
|
||||
* Returns:
|
||||
* A reference to the SHA1 object.
|
||||
*
|
||||
* Comments:
|
||||
* Each character is assumed to hold 8 bits of information.
|
||||
*
|
||||
*/
|
||||
SHA1& SHA1::operator<<(const unsigned char *message_array)
|
||||
{
|
||||
const unsigned char *p = message_array;
|
||||
|
||||
while(*p)
|
||||
{
|
||||
Input(*p);
|
||||
p++;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* operator<<
|
||||
*
|
||||
* Description:
|
||||
* This function provides the next octet in the message.
|
||||
*
|
||||
* Parameters:
|
||||
* message_element: [in]
|
||||
* The next octet in the message
|
||||
*
|
||||
* Returns:
|
||||
* A reference to the SHA1 object.
|
||||
*
|
||||
* Comments:
|
||||
* The character is assumed to hold 8 bits of information.
|
||||
*
|
||||
*/
|
||||
SHA1& SHA1::operator<<(const char message_element)
|
||||
{
|
||||
Input((unsigned char *) &message_element, 1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* operator<<
|
||||
*
|
||||
* Description:
|
||||
* This function provides the next octet in the message.
|
||||
*
|
||||
* Parameters:
|
||||
* message_element: [in]
|
||||
* The next octet in the message
|
||||
*
|
||||
* Returns:
|
||||
* A reference to the SHA1 object.
|
||||
*
|
||||
* Comments:
|
||||
* The character is assumed to hold 8 bits of information.
|
||||
*
|
||||
*/
|
||||
SHA1& SHA1::operator<<(const unsigned char message_element)
|
||||
{
|
||||
Input(&message_element, 1);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*
|
||||
* ProcessMessageBlock
|
||||
*
|
||||
* Description:
|
||||
* This function will process the next 512 bits of the message
|
||||
* stored in the Message_Block array.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
* Many of the variable names in this function, especially the single
|
||||
* character names, were used because those were the names used
|
||||
* in the publication.
|
||||
*
|
||||
*/
|
||||
void SHA1::ProcessMessageBlock()
|
||||
{
|
||||
const unsigned K[] = { // Constants defined for SHA-1
|
||||
0x5A827999,
|
||||
0x6ED9EBA1,
|
||||
0x8F1BBCDC,
|
||||
0xCA62C1D6
|
||||
};
|
||||
int t; // Loop counter
|
||||
unsigned temp; // Temporary word value
|
||||
unsigned W[80]; // Word sequence
|
||||
unsigned A, B, C, D, E; // Word buffers
|
||||
|
||||
/*
|
||||
* Initialize the first 16 words in the array W
|
||||
*/
|
||||
for(t = 0; t < 16; t++)
|
||||
{
|
||||
W[t] = ((unsigned) Message_Block[t * 4]) << 24;
|
||||
W[t] |= ((unsigned) Message_Block[t * 4 + 1]) << 16;
|
||||
W[t] |= ((unsigned) Message_Block[t * 4 + 2]) << 8;
|
||||
W[t] |= ((unsigned) Message_Block[t * 4 + 3]);
|
||||
}
|
||||
|
||||
for(t = 16; t < 80; t++)
|
||||
{
|
||||
W[t] = CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
|
||||
}
|
||||
|
||||
A = H[0];
|
||||
B = H[1];
|
||||
C = H[2];
|
||||
D = H[3];
|
||||
E = H[4];
|
||||
|
||||
for(t = 0; t < 20; t++)
|
||||
{
|
||||
temp = CircularShift(5,A) + ((B & C) | ((~B) & D)) + E + W[t] + K[0];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 20; t < 40; t++)
|
||||
{
|
||||
temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 40; t < 60; t++)
|
||||
{
|
||||
temp = CircularShift(5,A) +
|
||||
((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
for(t = 60; t < 80; t++)
|
||||
{
|
||||
temp = CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
|
||||
temp &= 0xFFFFFFFF;
|
||||
E = D;
|
||||
D = C;
|
||||
C = CircularShift(30,B);
|
||||
B = A;
|
||||
A = temp;
|
||||
}
|
||||
|
||||
H[0] = (H[0] + A) & 0xFFFFFFFF;
|
||||
H[1] = (H[1] + B) & 0xFFFFFFFF;
|
||||
H[2] = (H[2] + C) & 0xFFFFFFFF;
|
||||
H[3] = (H[3] + D) & 0xFFFFFFFF;
|
||||
H[4] = (H[4] + E) & 0xFFFFFFFF;
|
||||
|
||||
Message_Block_Index = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PadMessage
|
||||
*
|
||||
* Description:
|
||||
* According to the standard, the message must be padded to an even
|
||||
* 512 bits. The first padding bit must be a '1'. The last 64 bits
|
||||
* represent the length of the original message. All bits in between
|
||||
* should be 0. This function will pad the message according to those
|
||||
* rules by filling the message_block array accordingly. It will also
|
||||
* call ProcessMessageBlock() appropriately. When it returns, it
|
||||
* can be assumed that the message digest has been computed.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void SHA1::PadMessage()
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second block.
|
||||
*/
|
||||
if (Message_Block_Index > 55)
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = 0x80;
|
||||
while(Message_Block_Index < 64)
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = 0;
|
||||
}
|
||||
|
||||
ProcessMessageBlock();
|
||||
|
||||
while(Message_Block_Index < 56)
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = 0x80;
|
||||
while(Message_Block_Index < 56)
|
||||
{
|
||||
Message_Block[Message_Block_Index++] = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the message length as the last 8 octets
|
||||
*/
|
||||
Message_Block[56] = (Length_High >> 24) & 0xFF;
|
||||
Message_Block[57] = (Length_High >> 16) & 0xFF;
|
||||
Message_Block[58] = (Length_High >> 8) & 0xFF;
|
||||
Message_Block[59] = (Length_High) & 0xFF;
|
||||
Message_Block[60] = (Length_Low >> 24) & 0xFF;
|
||||
Message_Block[61] = (Length_Low >> 16) & 0xFF;
|
||||
Message_Block[62] = (Length_Low >> 8) & 0xFF;
|
||||
Message_Block[63] = (Length_Low) & 0xFF;
|
||||
|
||||
ProcessMessageBlock();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* CircularShift
|
||||
*
|
||||
* Description:
|
||||
* This member function will perform a circular shifting operation.
|
||||
*
|
||||
* Parameters:
|
||||
* bits: [in]
|
||||
* The number of bits to shift (1-31)
|
||||
* word: [in]
|
||||
* The value to shift (assumes a 32-bit integer)
|
||||
*
|
||||
* Returns:
|
||||
* The shifted value.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
unsigned SHA1::CircularShift(int bits, unsigned word)
|
||||
{
|
||||
return ((word << bits) & 0xFFFFFFFF) | ((word & 0xFFFFFFFF) >> (32-bits));
|
||||
}
|
||||
92
src/sha1/sha1.h
Executable file
92
src/sha1/sha1.h
Executable file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* sha1.h
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved.
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: sha1.h 12 2009-06-22 19:34:25Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This class implements the Secure Hashing Standard as defined
|
||||
* in FIPS PUB 180-1 published April 17, 1995.
|
||||
*
|
||||
* Many of the variable names in this class, especially the single
|
||||
* character names, were used because those were the names used
|
||||
* in the publication.
|
||||
*
|
||||
* Please read the file sha1.cpp for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SHA1_H_
|
||||
#define _SHA1_H_
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class SHA1
|
||||
{
|
||||
public:
|
||||
|
||||
SHA1();
|
||||
virtual ~SHA1();
|
||||
|
||||
/*
|
||||
* Re-initialize the class
|
||||
*/
|
||||
void Reset();
|
||||
|
||||
/*
|
||||
* Returns the message digest
|
||||
*/
|
||||
bool Result(unsigned *message_digest_array);
|
||||
|
||||
/*
|
||||
* Provide input to SHA1
|
||||
*/
|
||||
void Input( const unsigned char *message_array,
|
||||
unsigned length);
|
||||
void Input( const char *message_array,
|
||||
unsigned length);
|
||||
void Input(unsigned char message_element);
|
||||
void Input(char message_element);
|
||||
SHA1& operator<<(const char *message_array);
|
||||
SHA1& operator<<(const unsigned char *message_array);
|
||||
SHA1& operator<<(const char message_element);
|
||||
SHA1& operator<<(const unsigned char message_element);
|
||||
|
||||
private:
|
||||
|
||||
/*
|
||||
* Process the next 512 bits of the message
|
||||
*/
|
||||
void ProcessMessageBlock();
|
||||
|
||||
/*
|
||||
* Pads the current message block to 512 bits
|
||||
*/
|
||||
void PadMessage();
|
||||
|
||||
/*
|
||||
* Performs a circular left shift operation
|
||||
*/
|
||||
inline unsigned CircularShift(int bits, unsigned word);
|
||||
|
||||
unsigned H[5]; // Message digest buffers
|
||||
|
||||
unsigned Length_Low; // Message length in bits
|
||||
unsigned Length_High; // Message length in bits
|
||||
|
||||
unsigned char Message_Block[64]; // 512-bit message blocks
|
||||
int Message_Block_Index; // Index into message block array
|
||||
|
||||
bool Computed; // Is the digest computed?
|
||||
bool Corrupted; // Is the message digest corruped?
|
||||
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // _SHA1_H_
|
||||
169
src/sha1/shacmp.cpp
Executable file
169
src/sha1/shacmp.cpp
Executable file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* shacmp.cpp
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: shacmp.cpp 12 2009-06-22 19:34:25Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This utility will compare two files by producing a message digest
|
||||
* for each file using the Secure Hashing Algorithm and comparing
|
||||
* the message digests. This function will return 0 if they
|
||||
* compare or 1 if they do not or if there is an error.
|
||||
* Errors result in a return code higher than 1.
|
||||
*
|
||||
* Portability Issues:
|
||||
* none.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
/*
|
||||
* Return codes
|
||||
*/
|
||||
#define SHA1_COMPARE 0
|
||||
#define SHA1_NO_COMPARE 1
|
||||
#define SHA1_USAGE_ERROR 2
|
||||
#define SHA1_FILE_ERROR 3
|
||||
|
||||
/*
|
||||
* Function prototype
|
||||
*/
|
||||
void usage();
|
||||
|
||||
/*
|
||||
* main
|
||||
*
|
||||
* Description:
|
||||
* This is the entry point for the program
|
||||
*
|
||||
* Parameters:
|
||||
* argc: [in]
|
||||
* This is the count of arguments in the argv array
|
||||
* argv: [in]
|
||||
* This is an array of filenames for which to compute message digests
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
SHA1 sha; // SHA-1 class
|
||||
FILE *fp; // File pointer for reading files
|
||||
char c; // Character read from file
|
||||
unsigned message_digest[2][5]; // Message digest for files
|
||||
int i; // Counter
|
||||
bool message_match; // Message digest match flag
|
||||
int returncode;
|
||||
|
||||
/*
|
||||
* If we have two arguments, we will assume they are filenames. If
|
||||
* we do not have to arguments, call usage() and exit.
|
||||
*/
|
||||
if (argc != 3)
|
||||
{
|
||||
usage();
|
||||
return SHA1_USAGE_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the message digests for each file
|
||||
*/
|
||||
for(i = 1; i <= 2; i++)
|
||||
{
|
||||
sha.Reset();
|
||||
|
||||
if (!(fp = fopen(argv[i],"rb")))
|
||||
{
|
||||
fprintf(stderr, "sha: unable to open file %s\n", argv[i]);
|
||||
return SHA1_FILE_ERROR;
|
||||
}
|
||||
|
||||
c = fgetc(fp);
|
||||
while(!feof(fp))
|
||||
{
|
||||
sha.Input(c);
|
||||
c = fgetc(fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (!sha.Result(message_digest[i-1]))
|
||||
{
|
||||
fprintf(stderr,"shacmp: could not compute message digest for %s\n",
|
||||
argv[i]);
|
||||
return SHA1_FILE_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare the message digest values
|
||||
*/
|
||||
message_match = true;
|
||||
for(i = 0; i < 5; i++)
|
||||
{
|
||||
if (message_digest[0][i] != message_digest[1][i])
|
||||
{
|
||||
message_match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message_match)
|
||||
{
|
||||
printf("Fingerprints match:\n");
|
||||
returncode = SHA1_COMPARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Fingerprints do not match:\n");
|
||||
returncode = SHA1_NO_COMPARE;
|
||||
}
|
||||
|
||||
printf( "\t%08X %08X %08X %08X %08X\n",
|
||||
message_digest[0][0],
|
||||
message_digest[0][1],
|
||||
message_digest[0][2],
|
||||
message_digest[0][3],
|
||||
message_digest[0][4]);
|
||||
printf( "\t%08X %08X %08X %08X %08X\n",
|
||||
message_digest[1][0],
|
||||
message_digest[1][1],
|
||||
message_digest[1][2],
|
||||
message_digest[1][3],
|
||||
message_digest[1][4]);
|
||||
|
||||
return returncode;
|
||||
}
|
||||
|
||||
/*
|
||||
* usage
|
||||
*
|
||||
* Description:
|
||||
* This function will display program usage information to the user.
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void usage()
|
||||
{
|
||||
printf("usage: shacmp <file> <file>\n");
|
||||
printf("\tThis program will compare the message digests (fingerprints)\n");
|
||||
printf("\tfor two files using the Secure Hashing Algorithm (SHA-1).\n");
|
||||
}
|
||||
149
src/sha1/shatest.cpp
Executable file
149
src/sha1/shatest.cpp
Executable file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* shatest.cpp
|
||||
*
|
||||
* Copyright (C) 1998, 2009
|
||||
* Paul E. Jones <paulej@packetizer.com>
|
||||
* All Rights Reserved
|
||||
*
|
||||
*****************************************************************************
|
||||
* $Id: shatest.cpp 12 2009-06-22 19:34:25Z paulej $
|
||||
*****************************************************************************
|
||||
*
|
||||
* Description:
|
||||
* This file will exercise the SHA1 class and perform the three
|
||||
* tests documented in FIPS PUB 180-1.
|
||||
*
|
||||
* Portability Issues:
|
||||
* None.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "sha1.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* Define patterns for testing
|
||||
*/
|
||||
#define TESTA "abc"
|
||||
#define TESTB "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
|
||||
/*
|
||||
* Function prototype
|
||||
*/
|
||||
void DisplayMessageDigest(unsigned *message_digest);
|
||||
|
||||
/*
|
||||
* main
|
||||
*
|
||||
* Description:
|
||||
* This is the entry point for the program
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
int main()
|
||||
{
|
||||
SHA1 sha;
|
||||
unsigned message_digest[5];
|
||||
|
||||
/*
|
||||
* Perform test A
|
||||
*/
|
||||
cout << endl << "Test A: 'abc'" << endl;
|
||||
|
||||
sha.Reset();
|
||||
sha << TESTA;
|
||||
|
||||
if (!sha.Result(message_digest))
|
||||
{
|
||||
cerr << "ERROR-- could not compute message digest" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayMessageDigest(message_digest);
|
||||
cout << "Should match:" << endl;
|
||||
cout << '\t' << "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D" << endl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform test B
|
||||
*/
|
||||
cout << endl << "Test B: " << TESTB << endl;
|
||||
|
||||
sha.Reset();
|
||||
sha << TESTB;
|
||||
|
||||
if (!sha.Result(message_digest))
|
||||
{
|
||||
cerr << "ERROR-- could not compute message digest" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayMessageDigest(message_digest);
|
||||
cout << "Should match:" << endl;
|
||||
cout << '\t' << "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1" << endl;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform test C
|
||||
*/
|
||||
cout << endl << "Test C: One million 'a' characters" << endl;
|
||||
|
||||
sha.Reset();
|
||||
for(int i = 1; i <= 1000000; i++) sha.Input('a');
|
||||
|
||||
if (!sha.Result(message_digest))
|
||||
{
|
||||
cerr << "ERROR-- could not compute message digest" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayMessageDigest(message_digest);
|
||||
cout << "Should match:" << endl;
|
||||
cout << '\t' << "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F" << endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* DisplayMessageDigest
|
||||
*
|
||||
* Description:
|
||||
* Display Message Digest array
|
||||
*
|
||||
* Parameters:
|
||||
* None.
|
||||
*
|
||||
* Returns:
|
||||
* Nothing.
|
||||
*
|
||||
* Comments:
|
||||
*
|
||||
*/
|
||||
void DisplayMessageDigest(unsigned *message_digest)
|
||||
{
|
||||
ios::fmtflags flags;
|
||||
|
||||
cout << '\t';
|
||||
|
||||
flags = cout.setf(ios::hex|ios::uppercase,ios::basefield);
|
||||
cout.setf(ios::uppercase);
|
||||
|
||||
for(int i = 0; i < 5 ; i++)
|
||||
{
|
||||
cout << message_digest[i] << ' ';
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
|
||||
cout.setf(flags);
|
||||
}
|
||||
56
src/shared_const_buffer.hpp
Normal file
56
src/shared_const_buffer.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 SHARED_CONST_BUFFER_HPP
|
||||
#define SHARED_CONST_BUFFER_HPP
|
||||
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
class shared_const_buffer {
|
||||
public:
|
||||
explicit shared_const_buffer(const std::string &data) : m_data(new std::vector<char>(data.begin(), data.end())),
|
||||
m_buffer(boost::asio::buffer(*m_data)) {}
|
||||
public:
|
||||
typedef boost::asio::const_buffer value_type;
|
||||
typedef const boost::asio::const_buffer *const_iterator;
|
||||
const boost::asio::const_buffer *begin() const { return &m_buffer; }
|
||||
const boost::asio::const_buffer *end() const { return &m_buffer + 1; }
|
||||
private:
|
||||
boost::shared_ptr< std::vector<char> > m_data;
|
||||
boost::asio::const_buffer m_buffer;
|
||||
};
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // SHARED_CONST_BUFFER_HPP
|
||||
181
src/sockets/autotls.hpp
Normal file
181
src/sockets/autotls.hpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_SOCKET_AUTOTLS_HPP
|
||||
#define WEBSOCKETPP_SOCKET_AUTOTLS_HPP
|
||||
|
||||
#include "../common.hpp"
|
||||
#include "socket_base.hpp"
|
||||
#include "../../../ripple/AutoSocket.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace socket {
|
||||
|
||||
template <typename endpoint_type>
|
||||
class autotls {
|
||||
public:
|
||||
typedef autotls<endpoint_type> type;
|
||||
typedef AutoSocket autotls_socket;
|
||||
typedef boost::shared_ptr<autotls_socket> autotls_socket_ptr;
|
||||
|
||||
// should be private friended
|
||||
boost::asio::io_service& get_io_service() {
|
||||
return m_io_service;
|
||||
}
|
||||
|
||||
static void handle_shutdown(autotls_socket_ptr, const boost::system::error_code&) {
|
||||
}
|
||||
|
||||
void set_secure_only() {
|
||||
m_secure_only = true;
|
||||
}
|
||||
|
||||
void set_plain_only() {
|
||||
m_plain_only = true;
|
||||
}
|
||||
|
||||
// should be private friended?
|
||||
autotls_socket::handshake_type get_handshake_type() {
|
||||
if (static_cast< endpoint_type* >(this)->is_server()) {
|
||||
return boost::asio::ssl::stream_base::server;
|
||||
} else {
|
||||
return boost::asio::ssl::stream_base::client;
|
||||
}
|
||||
}
|
||||
|
||||
// TLS policy adds the on_tls_init method to the handler to allow the user
|
||||
// to set up their asio TLS context.
|
||||
class handler_interface {
|
||||
public:
|
||||
virtual ~handler_interface() {}
|
||||
|
||||
virtual void on_tcp_init() {};
|
||||
virtual boost::shared_ptr<boost::asio::ssl::context> on_tls_init() = 0;
|
||||
};
|
||||
|
||||
// Connection specific details
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
// should these two be public or protected. If protected, how?
|
||||
autotls_socket::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket_ptr->lowest_layer();
|
||||
}
|
||||
|
||||
autotls_socket& get_socket() {
|
||||
return *m_socket_ptr;
|
||||
}
|
||||
|
||||
bool is_secure() {
|
||||
return m_socket_ptr->isSecure();
|
||||
}
|
||||
protected:
|
||||
connection(autotls<endpoint_type>& e)
|
||||
: m_endpoint(e)
|
||||
, m_connection(static_cast< connection_type& >(*this)) {}
|
||||
|
||||
void init() {
|
||||
m_context_ptr = m_connection.get_handler()->on_tls_init();
|
||||
|
||||
if (!m_context_ptr) {
|
||||
throw "handler was unable to init autotls, connection error";
|
||||
}
|
||||
|
||||
m_socket_ptr =
|
||||
autotls_socket_ptr(new autotls_socket(m_endpoint.get_io_service(), *m_context_ptr,
|
||||
m_endpoint.m_secure_only, m_endpoint.m_plain_only));
|
||||
}
|
||||
|
||||
void async_init(boost::function<void(const boost::system::error_code&)> callback)
|
||||
{
|
||||
m_connection.get_handler()->on_tcp_init();
|
||||
|
||||
// wait for TLS handshake
|
||||
// TODO: configurable value
|
||||
m_connection.register_timeout(5000,
|
||||
fail::status::TIMEOUT_TLS,
|
||||
"Timeout on TLS handshake");
|
||||
|
||||
m_socket_ptr->async_handshake(
|
||||
m_endpoint.get_handshake_type(),
|
||||
boost::bind(
|
||||
&connection<connection_type>::handle_init,
|
||||
this,
|
||||
callback,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_init(socket_init_callback callback,const boost::system::error_code& error) {
|
||||
m_connection.cancel_timeout();
|
||||
callback(error);
|
||||
}
|
||||
|
||||
// note, this function for some reason shouldn't/doesn't need to be
|
||||
// called for plain HTTP connections. not sure why.
|
||||
bool shutdown() {
|
||||
boost::system::error_code ignored_ec;
|
||||
|
||||
m_socket_ptr->async_shutdown( // Don't block on connection shutdown DJS
|
||||
boost::bind(
|
||||
&autotls<endpoint_type>::handle_shutdown,
|
||||
m_socket_ptr,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
if (ignored_ec) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
boost::shared_ptr<boost::asio::ssl::context> m_context_ptr;
|
||||
autotls_socket_ptr m_socket_ptr;
|
||||
autotls<endpoint_type>& m_endpoint;
|
||||
connection_type& m_connection;
|
||||
};
|
||||
protected:
|
||||
autotls (boost::asio::io_service& m) : m_io_service(m), m_secure_only(false), m_plain_only(false) {}
|
||||
private:
|
||||
boost::asio::io_service& m_io_service;
|
||||
bool m_secure_only;
|
||||
bool m_plain_only;
|
||||
};
|
||||
|
||||
} // namespace socket
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_SOCKET_AUTOTLS_HPP
|
||||
131
src/sockets/plain.hpp
Normal file
131
src/sockets/plain.hpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#error Use Auto TLS only
|
||||
|
||||
#ifndef WEBSOCKETPP_SOCKET_PLAIN_HPP
|
||||
#define WEBSOCKETPP_SOCKET_PLAIN_HPP
|
||||
|
||||
#include "socket_base.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Disable "warning C4355: 'this' : used in base member initializer list".
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
namespace websocketpp {
|
||||
namespace socket {
|
||||
|
||||
template <typename endpoint_type>
|
||||
class plain : boost::noncopyable {
|
||||
public:
|
||||
boost::asio::io_service& get_io_service() {
|
||||
return m_io_service;
|
||||
}
|
||||
|
||||
bool is_secure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// hooks that this policy adds to handlers of connections that use it
|
||||
class handler_interface {
|
||||
public:
|
||||
virtual ~handler_interface() {}
|
||||
|
||||
virtual void on_tcp_init() {};
|
||||
};
|
||||
|
||||
// Connection specific details
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
// should these two be public or protected. If protected, how?
|
||||
boost::asio::ip::tcp::socket& get_raw_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& get_socket() {
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
bool is_secure() {
|
||||
return false;
|
||||
}
|
||||
protected:
|
||||
connection(plain<endpoint_type>& e)
|
||||
: m_socket(e.get_io_service())
|
||||
, m_connection(static_cast< connection_type& >(*this)) {}
|
||||
|
||||
void init() {
|
||||
|
||||
}
|
||||
|
||||
void async_init(socket_init_callback callback) {
|
||||
m_connection.get_handler()->on_tcp_init();
|
||||
|
||||
// TODO: make configuration option for NO_DELAY
|
||||
m_socket.set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
|
||||
// TODO: should this use post()?
|
||||
callback(boost::system::error_code());
|
||||
}
|
||||
|
||||
bool shutdown() {
|
||||
boost::system::error_code ignored_ec;
|
||||
m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both,ignored_ec);
|
||||
|
||||
if (ignored_ec) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
boost::asio::ip::tcp::socket m_socket;
|
||||
connection_type& m_connection;
|
||||
};
|
||||
protected:
|
||||
plain (boost::asio::io_service& m) : m_io_service(m) {}
|
||||
private:
|
||||
boost::asio::io_service& m_io_service;
|
||||
};
|
||||
|
||||
|
||||
} // namespace socket
|
||||
} // namespace websocketpp
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // WEBSOCKETPP_SOCKET_PLAIN_HPP
|
||||
60
src/sockets/socket_base.hpp
Normal file
60
src/sockets/socket_base.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_SOCKET_BASE_HPP
|
||||
#define WEBSOCKETPP_SOCKET_BASE_HPP
|
||||
|
||||
/*
|
||||
|
||||
websocketpp::socket API
|
||||
endpoint policy that provides:
|
||||
|
||||
|
||||
nested connection policy called `connection` that provides:
|
||||
- constructor that takes a reference to the endpoint_policy
|
||||
- async_init
|
||||
- async_read_some
|
||||
- async_write_some
|
||||
- get_raw_socket
|
||||
|
||||
*/
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace socket {
|
||||
|
||||
typedef boost::function<void(const boost::system::error_code&)> socket_init_callback;
|
||||
|
||||
|
||||
} // namespace socket
|
||||
} // namespace websocketpp
|
||||
|
||||
|
||||
|
||||
#endif // WEBSOCKETPP_SOCKET_BASE_HPP
|
||||
175
src/sockets/tls.hpp
Normal file
175
src/sockets/tls.hpp
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#error Use auto TLS only
|
||||
|
||||
#ifndef WEBSOCKETPP_SOCKET_TLS_HPP
|
||||
#define WEBSOCKETPP_SOCKET_TLS_HPP
|
||||
|
||||
#include "../common.hpp"
|
||||
#include "socket_base.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace socket {
|
||||
|
||||
template <typename endpoint_type>
|
||||
class tls {
|
||||
public:
|
||||
typedef tls<endpoint_type> type;
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> tls_socket;
|
||||
typedef boost::shared_ptr<tls_socket> tls_socket_ptr;
|
||||
|
||||
// should be private friended
|
||||
boost::asio::io_service& get_io_service() {
|
||||
return m_io_service;
|
||||
}
|
||||
|
||||
static void handle_shutdown(tls_socket_ptr, const boost::system::error_code&) {
|
||||
}
|
||||
|
||||
// should be private friended?
|
||||
tls_socket::handshake_type get_handshake_type() {
|
||||
if (static_cast< endpoint_type* >(this)->is_server()) {
|
||||
return boost::asio::ssl::stream_base::server;
|
||||
} else {
|
||||
return boost::asio::ssl::stream_base::client;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_secure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TLS policy adds the on_tls_init method to the handler to allow the user
|
||||
// to set up their asio TLS context.
|
||||
class handler_interface {
|
||||
public:
|
||||
virtual ~handler_interface() {}
|
||||
|
||||
virtual void on_tcp_init() {};
|
||||
virtual boost::shared_ptr<boost::asio::ssl::context> on_tls_init() = 0;
|
||||
};
|
||||
|
||||
// Connection specific details
|
||||
template <typename connection_type>
|
||||
class connection {
|
||||
public:
|
||||
// should these two be public or protected. If protected, how?
|
||||
tls_socket::lowest_layer_type& get_raw_socket() {
|
||||
return m_socket_ptr->lowest_layer();
|
||||
}
|
||||
|
||||
tls_socket& get_socket() {
|
||||
return *m_socket_ptr;
|
||||
}
|
||||
|
||||
bool is_secure() {
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
connection(tls<endpoint_type>& e)
|
||||
: m_endpoint(e)
|
||||
, m_connection(static_cast< connection_type& >(*this)) {}
|
||||
|
||||
void init() {
|
||||
m_context_ptr = m_connection.get_handler()->on_tls_init();
|
||||
|
||||
if (!m_context_ptr) {
|
||||
throw "handler was unable to init tls, connection error";
|
||||
}
|
||||
|
||||
m_socket_ptr = tls_socket_ptr(new tls_socket(m_endpoint.get_io_service(),*m_context_ptr));
|
||||
}
|
||||
|
||||
void async_init(boost::function<void(const boost::system::error_code&)> callback)
|
||||
{
|
||||
m_connection.get_handler()->on_tcp_init();
|
||||
|
||||
// wait for TLS handshake
|
||||
// TODO: configurable value
|
||||
m_connection.register_timeout(5000,
|
||||
fail::status::TIMEOUT_TLS,
|
||||
"Timeout on TLS handshake");
|
||||
|
||||
m_socket_ptr->async_handshake(
|
||||
m_endpoint.get_handshake_type(),
|
||||
boost::bind(
|
||||
&connection<connection_type>::handle_init,
|
||||
this,
|
||||
callback,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void handle_init(socket_init_callback callback,const boost::system::error_code& error) {
|
||||
m_connection.cancel_timeout();
|
||||
callback(error);
|
||||
}
|
||||
|
||||
// note, this function for some reason shouldn't/doesn't need to be
|
||||
// called for plain HTTP connections. not sure why.
|
||||
bool shutdown() {
|
||||
boost::system::error_code ignored_ec;
|
||||
|
||||
m_socket_ptr->async_shutdown( // Don't block on connection shutdown DJS
|
||||
boost::bind(
|
||||
&tls<endpoint_type>::handle_shutdown,
|
||||
m_socket_ptr,
|
||||
boost::asio::placeholders::error
|
||||
)
|
||||
);
|
||||
|
||||
if (ignored_ec) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private:
|
||||
boost::shared_ptr<boost::asio::ssl::context> m_context_ptr;
|
||||
tls_socket_ptr m_socket_ptr;
|
||||
tls<endpoint_type>& m_endpoint;
|
||||
connection_type& m_connection;
|
||||
};
|
||||
protected:
|
||||
tls (boost::asio::io_service& m) : m_io_service(m) {}
|
||||
private:
|
||||
boost::asio::io_service& m_io_service;
|
||||
tls_socket::handshake_type m_handshake_type;
|
||||
};
|
||||
|
||||
} // namespace socket
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_SOCKET_TLS_HPP
|
||||
58
src/ssl/client.pem
Normal file
58
src/ssl/client.pem
Normal file
@@ -0,0 +1,58 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,D6D356DA0705B377
|
||||
|
||||
cRb4uuukdht80UjwGfEA6ekOwv/RV9o3LVrWtNfRC2TI4JvQUrl+P3zz5p9BVIAA
|
||||
7alpdy241/xMzF1GB2XM1jY6Yy9691sNGmddh6bXNjgDXpEQKJz6w9Weqb64B4Y6
|
||||
fcN4zuyQeDZ3srlzmrGrLu6d8LXxJkqLAxOiOi/pFMId1j901T5gEJgOTpVq1Whv
|
||||
esYe7YYCTHo4T1TKmiz/rS3lwSv66j9fJ9OGBXvyuTwHuHPdaCnADUBPlyjWMiaM
|
||||
HBu7LCttVp9wn3+eI0Gjc92er8rGwcuA8kRoEC3c8CreczC78Zia1AG+5x17aBRe
|
||||
WniJ9SBK9ygtaeMM+VjB2NDwg0R+5zz2jXj2Zy31m3QeUXtQ1oHOMbjUa6PsKeYa
|
||||
iob8ppI1bxveMqP91ZuB7V9aFnNzYFcaDaHHs5NXtjsjHe/G/1vd2pNh57Bh2k2o
|
||||
BEZotnOuV4at2PiXXydMZNKTG3JaIaDWSyM6ybFxvvJmgbid9py3/nzqrPrYUVS0
|
||||
/roMUfhO07v7alCBaXe5mfP0mYwRitXAZ8Wug4UmaabZ9Kg2VnyQgnNIWSiiRE0V
|
||||
V/mzyDOLg2QKycnJitWi8wmv7x8/jr2o6xF3NchwHCZgpoE0EMYgDPjOKIgNy7pB
|
||||
CFFys0LxP4q2DDZys3Ic49zi4uPcQwcJMGGwfBAPwtWTqpSx02P0jehJjYTBhLsu
|
||||
prsMlX0Letmqeqh9nc6jFGZ29Aqcf7HBwykKeYzGulr6pp3t5fiYRwWTzvvd/9an
|
||||
8Lc3mueLpP7vpKqQgEHySb+jvJIRq875QfO5C+4zPpgHMigg8GU/0tEhuG5GPdM2
|
||||
6X6EnpRkJ4G4AdAIqe5cwrxCUB62/maSTnDvu5vPIjlX6B/jL9RuaVwmpPGz+DUB
|
||||
4MpiJxGi3Ftf1fgWU/1FJxB4ugQCwhLinfx9L5ReUNcWPEbQCuYByM3GU34D/op+
|
||||
n+L2mih1gTQdqUpy/Ro9ARCocQdV96jKxoj5LnUhOLWLz3IaKkoEfjvhBIl4aFaj
|
||||
cQ1n4MgwJrqsnGLC9opmDBa+OqIOYq/rOxd3J/WDbXCBQZFozfw6ABYhJuqc/RPD
|
||||
2EcGJsGDx7HgNYHUfPklx1EpBSHzC+O7DsXDuNzMN2+x1N1c/I1zg6VzJqpUuPxn
|
||||
o90RWhT2752HZZXExckkAs3hDSUS5YZxny28rLTDw86PdvVZ52kAoUiY2MOUrt3O
|
||||
dOoyXvK5FhrDasNv2wfmGNbuLUN38lgayMkoP3dg0dQ9SrmbGjQ88aIWI6D1ndTg
|
||||
3M3htdhQM4WxpwA/PvkSrf5LpPSe8bmQMHfvJ3vbjePNiQTYe8fa6NZO+eBYrM5n
|
||||
Gooz+XSHlDwy1Y6jSf250Pl/hgIkMQovpiDp8PBWoGCnc69706qqlXn56ae1XN5+
|
||||
y/4/+jsgRCndQqrGINE2QpVdrK5fZjmWYowRmMA1vOKSTDLGzRpnPK8Bwov5a1bQ
|
||||
fY+v9wpqxvVTjZmBB1fhZ7dlp9AQ6cSil3jtrP4jk9afI/Q30ggAy+/TzqA/Rtz0
|
||||
nOSjlkwF87lpEu4phkj/+SqV7IXxqd6OE7XNY8fFxDN1fxq8Irk6tmOm9lX0vKz1
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE0DCCA7igAwIBAgIJAPJpDwngMJONMA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xGDAW
|
||||
BgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0KysxFjAU
|
||||
BgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3RlckB6
|
||||
YXBob3lkLmNvbTAeFw0xMTExMTUyMTIxMDJaFw0xMjExMTQyMTIxMDJaMIGgMQsw
|
||||
CQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28x
|
||||
GDAWBgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0Kysx
|
||||
FjAUBgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3Rl
|
||||
ckB6YXBob3lkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANgl
|
||||
ZLPFaHf+vVrKB6R//8ylv6GJ7wdQ5PNCZpAHzd8BI/KPdHQShZZa7YmSbJ1oFzDC
|
||||
K5XGr5n0y7LCLyqHAJgEgwFjSxlx7IFjWR2MMVxMgPA+fRKGHJH4f2lLbAYea7Xi
|
||||
s+3VMANSO9epKnhJJdPkNFhsGHBbBulXX5SxRbdeHtNq1IpueQF96jfRRTiLye1v
|
||||
+gHaTSVSLxkDC95v+NJNehrVA0GomX1TnJ+X0tFIVFXSLkOXhBkff0aLy7/C+vb5
|
||||
CHGAI+fI/+mdc7sXtcmagbMQPMaL48ANK4n7qrnKfDJi/dWzLvdul/FeLsHd8xWw
|
||||
mTNGnVWcZeaU1NnxMZUCAwEAAaOCAQkwggEFMB0GA1UdDgQWBBQRiyJb1jsUEoRv
|
||||
33P+OdSJJeSVCTCB1QYDVR0jBIHNMIHKgBQRiyJb1jsUEoRv33P+OdSJJeSVCaGB
|
||||
pqSBozCBoDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQH
|
||||
EwdDaGljYWdvMRgwFgYDVQQKEw9aYXBob3lkIFN0dWRpb3MxFDASBgNVBAsTC1dl
|
||||
YlNvY2tldCsrMRYwFAYDVQQDEw1QZXRlciBUaG9yc29uMSQwIgYJKoZIhvcNAQkB
|
||||
FhV3ZWJtYXN0ZXJAemFwaG95ZC5jb22CCQDyaQ8J4DCTjTAMBgNVHRMEBTADAQH/
|
||||
MA0GCSqGSIb3DQEBBQUAA4IBAQAv2D8s4wFvhJCTnHk72uEt3MoNEkQBpM+nAbLC
|
||||
HXhwBuyYcfIO+Q1ft8oZ/p9EZjHnlQQZMnH2YsY/FxERgqGJuGCImf9w/f7zYRHQ
|
||||
pfeGXm50GRP0PGuE0xI3TJ+NjalOCkjCoTbtNKxLwxWciNBAGaaPempjgFaaX1T9
|
||||
OGfH+mXY7/cvR8yemDRrKkzPSjEbjEu5wQd7H/f7JBsYLpYQOPxxbPb7vqPQomqn
|
||||
Q+WAZXDOSEbxq7EruD9GKhuZ0b91vfFlG2oZP4rA2oa6pwVWqW7X50znTpASJEY9
|
||||
4xEMcOliTOY6+LMq9693SQZO1l+LuzPMBmJfsLBFPawdf8mT
|
||||
-----END CERTIFICATE-----
|
||||
12
src/ssl/dh512.pem
Normal file
12
src/ssl/dh512.pem
Normal file
@@ -0,0 +1,12 @@
|
||||
Diffie-Hellman-Parameters: (512 bit)
|
||||
prime:
|
||||
00:ba:24:79:08:45:94:52:3d:6c:be:3b:a9:a2:13:
|
||||
6e:e3:df:1f:ce:3a:35:08:be:60:89:2a:d2:fa:e4:
|
||||
10:c5:d1:ea:49:7e:12:64:1a:e9:bc:4e:6e:fa:6b:
|
||||
12:5a:88:28:d0:22:f7:52:fe:9a:a1:42:a5:cc:db:
|
||||
d9:96:a3:ed:6f
|
||||
generator: 5 (0x5)
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MEYCQQC6JHkIRZRSPWy+O6miE27j3x/OOjUIvmCJKtL65BDF0epJfhJkGum8Tm76
|
||||
axJaiCjQIvdS/pqhQqXM29mWo+1vAgEF
|
||||
-----END DH PARAMETERS-----
|
||||
40
src/ssl/server.cer
Normal file
40
src/ssl/server.cer
Normal file
@@ -0,0 +1,40 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQzCCAiugAwIBAgIBAjALBgkqhkiG9w0BAQswTDEYMBYGA1UEAwwPWmFwaG95
|
||||
ZCBTdHVkaW9zMQswCQYDVQQGEwJVUzEjMCEGCSqGSIb3DQEJARYUemFwaG95ZEB1
|
||||
Y2hpY2Fnby5lZHUwHhcNMTExMTE1MjE0NDQ2WhcNMTIxMTE0MjE0NDQ2WjBUMSAw
|
||||
HgYDVQQDDBdXZWJTb2NrZXQrKyBTZXJ2ZXIgVGVzdDELMAkGA1UEBhMCVVMxIzAh
|
||||
BgkqhkiG9w0BCQEWFHphcGhveWRAdWNoaWNhZ28uZWR1MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAz4iD0ZGDVk8qscrQgZ4BPkhKiaSUWTWTKYiquddT
|
||||
4J0C0RIy/RVFeNEZeUL4gzKoRJvG37GxvbMIOYriP9Xm3ydJSvLU7JTs+3yqYTen
|
||||
3U5xlKNwOjpc79Mp+O7ciJ36cZ8ATCoqQexf6B6IGCS7/Zo4VTj+YuKdaizZKY3X
|
||||
FDEp4UYYNGzWtTj5rQhE5SRK9O0V67fG8598GXsP1lcXlnR84UyP1vkdQVuJ0PPJ
|
||||
56okHoz9d52XiIliweQaC1F740zKtAdA2Ul8eSOfRneoMzzzN2LcqPMysTV2Mjut
|
||||
iecxeH+A/5aGhaDoaeZkzPkyVyYxZeGPszQ5Bqw3epY0jwIDAQABoyowKDAOBgNV
|
||||
HQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEL
|
||||
BQADggEBAFiRTh/TLAPGggHY/5SmXkbGaZ0htoRdAvM43J2MGayDmTlP3yZZY/rc
|
||||
vLtJrPnmZvVUsQhpnGMF3ZFBtrE0Tllq2DlqJeJOyoV+jQNzD1cRlJUnoFdM8WV1
|
||||
7HcVzJx7DoxjFPBn6AHjyCW6S3TKQNpsAd/kvV3lUsJyZf/7Z/un7Nc5FNWo5mkp
|
||||
NMYFIxTJGZNPGl0jVS59Wd7fuNVXqEBz22Bx6xFxKcJVEgRFy46arwUhjzh8mDBb
|
||||
bpt2AWq9i4kujlOYi585aUt8zqWS8gl7rG7D/EQOuiWEEmuzeuRkyGZf82HmXLjN
|
||||
lq7q/lcZji2slXMBRCu5mIG50YHKKmU=
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDQzCCAiugAwIBAgIBAjALBgkqhkiG9w0BAQswTDEYMBYGA1UEAwwPWmFwaG95
|
||||
ZCBTdHVkaW9zMQswCQYDVQQGEwJVUzEjMCEGCSqGSIb3DQEJARYUemFwaG95ZEB1
|
||||
Y2hpY2Fnby5lZHUwHhcNMTExMTE1MjE0NDQ2WhcNMTIxMTE0MjE0NDQ2WjBUMSAw
|
||||
HgYDVQQDDBdXZWJTb2NrZXQrKyBTZXJ2ZXIgVGVzdDELMAkGA1UEBhMCVVMxIzAh
|
||||
BgkqhkiG9w0BCQEWFHphcGhveWRAdWNoaWNhZ28uZWR1MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAz4iD0ZGDVk8qscrQgZ4BPkhKiaSUWTWTKYiquddT
|
||||
4J0C0RIy/RVFeNEZeUL4gzKoRJvG37GxvbMIOYriP9Xm3ydJSvLU7JTs+3yqYTen
|
||||
3U5xlKNwOjpc79Mp+O7ciJ36cZ8ATCoqQexf6B6IGCS7/Zo4VTj+YuKdaizZKY3X
|
||||
FDEp4UYYNGzWtTj5rQhE5SRK9O0V67fG8598GXsP1lcXlnR84UyP1vkdQVuJ0PPJ
|
||||
56okHoz9d52XiIliweQaC1F740zKtAdA2Ul8eSOfRneoMzzzN2LcqPMysTV2Mjut
|
||||
iecxeH+A/5aGhaDoaeZkzPkyVyYxZeGPszQ5Bqw3epY0jwIDAQABoyowKDAOBgNV
|
||||
HQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEL
|
||||
BQADggEBAFiRTh/TLAPGggHY/5SmXkbGaZ0htoRdAvM43J2MGayDmTlP3yZZY/rc
|
||||
vLtJrPnmZvVUsQhpnGMF3ZFBtrE0Tllq2DlqJeJOyoV+jQNzD1cRlJUnoFdM8WV1
|
||||
7HcVzJx7DoxjFPBn6AHjyCW6S3TKQNpsAd/kvV3lUsJyZf/7Z/un7Nc5FNWo5mkp
|
||||
NMYFIxTJGZNPGl0jVS59Wd7fuNVXqEBz22Bx6xFxKcJVEgRFy46arwUhjzh8mDBb
|
||||
bpt2AWq9i4kujlOYi585aUt8zqWS8gl7rG7D/EQOuiWEEmuzeuRkyGZf82HmXLjN
|
||||
lq7q/lcZji2slXMBRCu5mIG50YHKKmU=
|
||||
-----END CERTIFICATE-----
|
||||
58
src/ssl/server.pem
Normal file
58
src/ssl/server.pem
Normal file
@@ -0,0 +1,58 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,A0ED66EF872A48A9
|
||||
|
||||
gXuvKojXzApVhhPVNdRliiajbC4PtwQG5c8TA7JADLgwOR7o9t6KtXEr37bDRpvB
|
||||
9aO9P+SJaK5OOp3XKPGthOdqv+tvCRTlmzmC8GjPLBX389DWT2xoGu7JkGwDtdSm
|
||||
rnF49Rlp5bfjpACk5xKNiKeDo1CWfeEJzw9Kto0g+5eMaEdors64oPzjXs3geA2g
|
||||
TxCJSHv9qSX6++pCLKKCUTbyzidAxV/Zb0AAubt5V40QKqX4HhSwwstFnTaX3tlb
|
||||
3QOdY+y04VIkM6d7qN5W8M7NzRkMpZ1qBpQcUMpkhQcRzWP2wub5AAff9D2GntRd
|
||||
4Dz1vn3u41U3Okdr0CNj+iH7byCzuokoAhk6ZQEN6WB+GTpGgfBXdtUZrfpb0MKm
|
||||
UNYP5AF2AmUqJRXhViTDVtu/V2tHF3LGuNT+W2Dz+spFZEq0byEO0N858eR0dikc
|
||||
6jOASvNQbSwD0+mkgBC1gXKKU3ngj2gpJUwljeACdWFd8N2egrZfyI05CmX7vPNC
|
||||
NXbs7k2buWNdjP4/D8IM+HDVidWzQa/kG/qokXKqllem9Egg37lUucwnP3cX2/Hw
|
||||
U2mfaBWzeZtqc+GqRp08rYIql+Reai3sUYlQMnNk01prVY47UQb+dxuqjaxGV5Xx
|
||||
Xkx0s2mfQnNRjL4S7Hjhqelufi6GpkCQ2EGsPpA+6K1ztZ0ame9Q2BE1SXeM/6vU
|
||||
rxT5nRrCxueyXAyQSGcqMX9//gSeK8WWBqG/c1IAMVDa0NWrJeOJhSziE+ta3B0m
|
||||
bHAPBY6vh0iB3lLdRlbUOPbC6R1TpxMOs+6Vbs2+OTifFpvOVymEoZq/nroyg68P
|
||||
vn5uCKogwWA7o8EArf/UTlIwWJmH9bgILdZKld4wMel2HQg16RDzm+mEXAJi52a/
|
||||
FC+fgfphdxltmUJ+rqOyR4AHULjaTWUQqTIB6sdlzgmES1nXAiE71zX//KFqomar
|
||||
O60SPPk3C1bs0x5DsvmGJa8SIfDhyd+D7NPyqwEKqrZsaotYGklNkfqxa6pa8mrc
|
||||
ejxquW1PK4FvBk26+osu5a90Jih0PcQM7DUMMr2WHdTiMSXWAiK2ToYF8Itt25Qv
|
||||
Cd0CsSYw9CJkXNr1u1+mObheaY9QYOmztnSJLy4ZO2JsMhqNwuAueIcwmhXOREq7
|
||||
kzlnGMgJcuSeAS/OBNj8Zgx0c7QQ0kzc+YmnOCsqoMtPsu/CsXJ4iJiM3Tki/2jT
|
||||
bywrTiQwE6R3a/87GREOREX+WLicZBWX3k9/4tBL5XSe1p5wPpuIRQUDvAGNfNHP
|
||||
JN7kujDF4SehilF1qtvCygAwvxHFDj+EwhXKNDKJzoZZIM15rAk3k92n2j6nz1qH
|
||||
a3xOU05yydOlO6F6w51I1QoDddmkzCRNB0TeO3D6rekHsCK1aDWmC+qRcm2ZFtVz
|
||||
sY6fdZN2NEmMQokIh9Opi1f8CSYSizPESMzdu2SF0xVO9n/IGIkn1ksK04O2BZo0
|
||||
X3LBPHLfCRsQNY1eF17bj07fYU2oPZKs/XzJiwxkqK6LFvpeAVaYrtg9fqRO/UVe
|
||||
QhUIj3BL550ocEpa15xLehLrmwzYiW5zwGjSHQ4EgZluGLCwyKGTh4QswEJRA9Rt
|
||||
-----END RSA PRIVATE KEY-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE0DCCA7igAwIBAgIJAM5MuKJezXq0MA0GCSqGSIb3DQEBBQUAMIGgMQswCQYD
|
||||
VQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28xGDAW
|
||||
BgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0KysxFjAU
|
||||
BgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3RlckB6
|
||||
YXBob3lkLmNvbTAeFw0xMTExMTUyMTIwMDZaFw0xMjExMTQyMTIwMDZaMIGgMQsw
|
||||
CQYDVQQGEwJVUzERMA8GA1UECBMISWxsaW5vaXMxEDAOBgNVBAcTB0NoaWNhZ28x
|
||||
GDAWBgNVBAoTD1phcGhveWQgU3R1ZGlvczEUMBIGA1UECxMLV2ViU29ja2V0Kysx
|
||||
FjAUBgNVBAMTDVBldGVyIFRob3Jzb24xJDAiBgkqhkiG9w0BCQEWFXdlYm1hc3Rl
|
||||
ckB6YXBob3lkLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANR0
|
||||
tdwAnIB8I9qRZ7QbzEWY95RpM7GIn0u/9oH90PzdHiE0rXSkKT+yw3XUzH0iw5t0
|
||||
5dEwSC+srSP5Vm4cA6kXc94agVVaPW89tGcdP4fHptCruSrzQsDXELCPl5UUvMpA
|
||||
YUcGisdXYPN/EeOoqb9wKWxoW5mREsyyeWWS89fYN5qU/d0QpbSvEWghqLbL/ZS2
|
||||
hOlXT9LufOeA+vHiV1/T/h5xC7ecIH02YDQw1EnqxbPmkLPcWThztLS9FiufNDRM
|
||||
Rhcoaj2b9VDHvDwdbeA0T5v5qNdG34LaapYOelxzQMOtM0f9Dgqehodyxl2qm9mR
|
||||
lq432dlOEzDnVCPNHwECAwEAAaOCAQkwggEFMB0GA1UdDgQWBBTTPKfNMnKOykhv
|
||||
+vKS7vql5JsMyzCB1QYDVR0jBIHNMIHKgBTTPKfNMnKOykhv+vKS7vql5JsMy6GB
|
||||
pqSBozCBoDELMAkGA1UEBhMCVVMxETAPBgNVBAgTCElsbGlub2lzMRAwDgYDVQQH
|
||||
EwdDaGljYWdvMRgwFgYDVQQKEw9aYXBob3lkIFN0dWRpb3MxFDASBgNVBAsTC1dl
|
||||
YlNvY2tldCsrMRYwFAYDVQQDEw1QZXRlciBUaG9yc29uMSQwIgYJKoZIhvcNAQkB
|
||||
FhV3ZWJtYXN0ZXJAemFwaG95ZC5jb22CCQDOTLiiXs16tDAMBgNVHRMEBTADAQH/
|
||||
MA0GCSqGSIb3DQEBBQUAA4IBAQB+SH0s/hrv5VYqgX6SNLzxdSLvCVsUkCdTpxwY
|
||||
wOJ84XmYcXDMhKDtZqLtOtN6pfEwVusFlC9mkieuunztCnWNmsSG83RuljJPjFSi
|
||||
1d4Id4bKEQkQ4cfnjoHKivRrViWLnxuNnLzC6tpyGH/35kKWhhr6T58AXerFgVw3
|
||||
mHvLPTr1DuhdAZA0ZuvuseVAFFAjI3RetSySwHJE3ak8KswDVfLi6E3XxMVsIWTS
|
||||
/iFsC2WwoZQlljya2V/kRYIhu+uCdqJ01wunn2BvmURPSgr4GTBF0FQ9JGpNbXxM
|
||||
TAU7oQJgyFc5sCcuEgPTO0dWVQTvdZVgay4tkmduKDRkmJBF
|
||||
-----END CERTIFICATE-----
|
||||
320
src/uri.cpp
Normal file
320
src/uri.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "uri.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
using websocketpp::uri;
|
||||
|
||||
uri::uri(const std::string& uri) {
|
||||
// temporary testing non-regex implimentation.
|
||||
/*enum state {
|
||||
BEGIN = 0,
|
||||
HOST_BEGIN = 1,
|
||||
READ_IPV6_LITERAL = 2,
|
||||
READ_HOSTNAME = 3,
|
||||
BEGIN_PORT = 4,
|
||||
READ_PORT = 5,
|
||||
READ_RESOURCE = 6,
|
||||
};
|
||||
|
||||
state the_state = BEGIN;
|
||||
std::string temp;
|
||||
|
||||
for (std::string::const_iterator it = uri.begin(); it != uri.end(); it++) {
|
||||
switch (the_state) {
|
||||
case BEGIN:
|
||||
// we are looking for a ws:// or wss://
|
||||
if (temp.size() < 6) {
|
||||
temp.append(1,*it);
|
||||
} else {
|
||||
throw websocketpp::uri_exception("Scheme is too long");
|
||||
}
|
||||
|
||||
if (temp == "ws://") {
|
||||
m_secure = false;
|
||||
the_state = HOST_BEGIN;
|
||||
temp.clear();
|
||||
} else if (temp == "wss://") {
|
||||
m_secure = true;
|
||||
the_state = HOST_BEGIN;
|
||||
temp.clear();
|
||||
}
|
||||
break;
|
||||
case HOST_BEGIN:
|
||||
// if character is [ go into IPv6 literal state
|
||||
// otherwise go into read hostname state
|
||||
if (*it == '[') {
|
||||
the_state = READ_IPV6_LITERAL;
|
||||
} else {
|
||||
*it--;
|
||||
the_state = READ_HOSTNAME;
|
||||
}
|
||||
break;
|
||||
case READ_IPV6_LITERAL:
|
||||
// read 0-9a-fA-F:. until ] or 45 characters have been read
|
||||
if (*it == ']') {
|
||||
the_state = BEGIN_PORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_host.size() > 45) {
|
||||
throw websocketpp::uri_exception("IPv6 literal is too long");
|
||||
}
|
||||
|
||||
if (*it == '.' ||
|
||||
*it == ':' ||
|
||||
(*it >= '0' && *it <= '9') ||
|
||||
(*it >= 'a' && *it <= 'f') ||
|
||||
(*it >= 'A' && *it <= 'F'))
|
||||
{
|
||||
m_host.append(1,*it);
|
||||
}
|
||||
break;
|
||||
case READ_HOSTNAME:
|
||||
//
|
||||
if (*it == ':') {
|
||||
it--;
|
||||
the_state = BEGIN_PORT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*it == '/') {
|
||||
it--;
|
||||
the_state = BEGIN_PORT;
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: max url length?
|
||||
|
||||
// TODO: check valid characters?
|
||||
if (1) {
|
||||
m_host.append(1,*it);
|
||||
}
|
||||
break;
|
||||
case BEGIN_PORT:
|
||||
if (*it == ':') {
|
||||
the_state = READ_PORT;
|
||||
} else if (*it == '/') {
|
||||
m_port = get_port_from_string("");
|
||||
*it--;
|
||||
the_state = READ_RESOURCE;
|
||||
} else {
|
||||
throw websocketpp::uri_exception("Error parsing WebSocket URI");
|
||||
}
|
||||
break;
|
||||
case READ_PORT:
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
if (temp.size() < 5) {
|
||||
temp.append(1,*it);
|
||||
} else {
|
||||
throw websocketpp::uri_exception("Port is too long");
|
||||
}
|
||||
} else if (*it == '/') {
|
||||
m_port = get_port_from_string(temp);
|
||||
temp.clear();
|
||||
*it--;
|
||||
the_state = READ_RESOURCE;
|
||||
|
||||
} else {
|
||||
throw websocketpp::uri_exception("Error parsing WebSocket URI");
|
||||
}
|
||||
break;
|
||||
case READ_RESOURCE:
|
||||
// max length check?
|
||||
|
||||
if (*it == '#') {
|
||||
throw websocketpp::uri_exception("WebSocket URIs cannot have fragments");
|
||||
} else {
|
||||
m_resource.append(1,*it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (the_state) {
|
||||
case READ_PORT:
|
||||
m_port = get_port_from_string(temp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_resource == "") {
|
||||
m_resource = "/";
|
||||
}*/
|
||||
|
||||
|
||||
boost::cmatch matches;
|
||||
const boost::regex expression("(ws|wss)://([^/:\\[]+|\\[[0-9a-fA-F:.]+\\])(:\\d{1,5})?(/[^#]*)?");
|
||||
|
||||
// TODO: should this split resource into path/query?
|
||||
|
||||
if (boost::regex_match(uri.c_str(), matches, expression)) {
|
||||
m_secure = (matches[1] == "wss");
|
||||
m_host = matches[2];
|
||||
|
||||
// strip brackets from IPv6 literal URIs
|
||||
if (m_host[0] == '[') {
|
||||
m_host = m_host.substr(1,m_host.size()-2);
|
||||
}
|
||||
|
||||
std::string port(matches[3]);
|
||||
|
||||
if (port != "") {
|
||||
// strip off the :
|
||||
// this could probably be done with a better regex.
|
||||
port = port.substr(1);
|
||||
}
|
||||
|
||||
m_port = get_port_from_string(port);
|
||||
|
||||
m_resource = matches[4];
|
||||
|
||||
if (m_resource == "") {
|
||||
m_resource = "/";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw websocketpp::uri_exception("Error parsing WebSocket URI");
|
||||
|
||||
}
|
||||
|
||||
uri::uri(bool secure, const std::string& host, uint16_t port, const std::string& resource)
|
||||
: m_secure(secure),
|
||||
m_host(host),
|
||||
m_port(port),
|
||||
m_resource(resource == "" ? "/" : resource) {}
|
||||
|
||||
uri::uri(bool secure, const std::string& host, const std::string& resource)
|
||||
: m_secure(secure),
|
||||
m_host(host),
|
||||
m_port(m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT),
|
||||
m_resource(resource == "" ? "/" : resource) {}
|
||||
|
||||
uri::uri(bool secure,
|
||||
const std::string& host,
|
||||
const std::string& port,
|
||||
const std::string& resource)
|
||||
: m_secure(secure),
|
||||
m_host(host),
|
||||
m_port(get_port_from_string(port)),
|
||||
m_resource(resource == "" ? "/" : resource) {}
|
||||
|
||||
/* Slightly cleaner C++11 delegated constructor method
|
||||
|
||||
uri::uri(bool secure,
|
||||
const std::string& host,
|
||||
uint16_t port,
|
||||
const std::string& resource)
|
||||
: m_secure(secure),
|
||||
m_host(host),
|
||||
m_port(port),
|
||||
m_resource(resource == "" ? "/" : resource)
|
||||
{
|
||||
if (m_port > 65535) {
|
||||
throw websocketpp::uri_exception("Port must be less than 65535");
|
||||
}
|
||||
}
|
||||
|
||||
uri::uri(bool secure,
|
||||
const std::string& host,
|
||||
const std::string& port,
|
||||
const std::string& resource)
|
||||
: uri(secure, host, get_port_from_string(port), resource) {}
|
||||
|
||||
*/
|
||||
|
||||
bool uri::get_secure() const {
|
||||
return m_secure;
|
||||
}
|
||||
|
||||
std::string uri::get_host() const {
|
||||
return m_host;
|
||||
}
|
||||
|
||||
std::string uri::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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint16_t uri::get_port() const {
|
||||
return m_port;
|
||||
}
|
||||
|
||||
std::string uri::get_port_str() const {
|
||||
std::stringstream p;
|
||||
p << m_port;
|
||||
return p.str();
|
||||
}
|
||||
|
||||
std::string uri::get_resource() const {
|
||||
return m_resource;
|
||||
}
|
||||
|
||||
std::string uri::str() const {
|
||||
std::stringstream s;
|
||||
|
||||
s << "ws" << (m_secure ? "s" : "") << "://" << m_host;
|
||||
|
||||
if (m_port != (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT)) {
|
||||
s << ":" << m_port;
|
||||
}
|
||||
|
||||
s << m_resource;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
uint16_t uri::get_port_from_string(const std::string& port) const {
|
||||
if (port == "") {
|
||||
return (m_secure ? URI_DEFAULT_SECURE_PORT : URI_DEFAULT_PORT);
|
||||
}
|
||||
|
||||
unsigned int t_port = atoi(port.c_str());
|
||||
|
||||
if (t_port > 65535) {
|
||||
throw websocketpp::uri_exception("Port must be less than 65535");
|
||||
}
|
||||
|
||||
if (t_port == 0) {
|
||||
throw websocketpp::uri_exception("Error parsing port string: "+port);
|
||||
}
|
||||
|
||||
return static_cast<uint16_t>(t_port);
|
||||
}
|
||||
101
src/uri.hpp
Normal file
101
src/uri.hpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 "common.hpp"
|
||||
|
||||
#include <exception>
|
||||
|
||||
namespace websocketpp {
|
||||
|
||||
// WebSocket URI only (not http/etc)
|
||||
|
||||
class uri_exception : public std::exception {
|
||||
public:
|
||||
uri_exception(const std::string& msg) : m_msg(msg) {}
|
||||
~uri_exception() throw() {}
|
||||
|
||||
virtual const char* what() const throw() {
|
||||
return m_msg.c_str();
|
||||
}
|
||||
private:
|
||||
std::string m_msg;
|
||||
};
|
||||
|
||||
// TODO: figure out why this fixes horrible linking errors.
|
||||
static const uint16_t URI_DEFAULT_PORT = 80;
|
||||
static const uint16_t URI_DEFAULT_SECURE_PORT = 443;
|
||||
|
||||
class uri {
|
||||
public:
|
||||
explicit uri(const std::string& uri);
|
||||
uri(bool secure, const std::string& host, uint16_t port, const std::string& resource);
|
||||
uri(bool secure, const std::string& host, const std::string& resource);
|
||||
uri(bool secure, const std::string& host, const std::string& port, const std::string& resource);
|
||||
|
||||
bool get_secure() const;
|
||||
std::string get_host() const;
|
||||
std::string get_host_port() const;
|
||||
uint16_t get_port() const;
|
||||
std::string get_port_str() const;
|
||||
std::string get_resource() const;
|
||||
std::string str() const;
|
||||
|
||||
// get query?
|
||||
// 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(const std::string& port) const;
|
||||
|
||||
bool m_secure;
|
||||
std::string m_host;
|
||||
uint16_t m_port;
|
||||
std::string m_resource;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<uri> uri_ptr;
|
||||
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_URI_HPP
|
||||
90
src/utf8_validator/utf8_validator.hpp
Normal file
90
src/utf8_validator/utf8_validator.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
||||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
||||
|
||||
#ifndef UTF8_VALIDATOR_HPP
|
||||
#define UTF8_VALIDATOR_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace utf8_validator {
|
||||
|
||||
static const unsigned int UTF8_ACCEPT = 0;
|
||||
static const unsigned int UTF8_REJECT = 1;
|
||||
|
||||
static const uint8_t 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
|
||||
};
|
||||
|
||||
uint32_t inline
|
||||
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;
|
||||
}
|
||||
|
||||
class validator {
|
||||
public:
|
||||
validator() : m_state(UTF8_ACCEPT),m_codepoint(0) {}
|
||||
|
||||
bool consume (uint32_t byte) {
|
||||
if (utf8_validator::decode(&m_state,&m_codepoint,static_cast<uint8_t>(byte)) == UTF8_REJECT) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename iterator_type>
|
||||
bool decode (iterator_type b, iterator_type e) {
|
||||
for (iterator_type i = b; i != e; i++) {
|
||||
if (utf8_validator::decode(&m_state,&m_codepoint,*i) == UTF8_REJECT) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool complete() {
|
||||
return m_state == UTF8_ACCEPT;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_state = UTF8_ACCEPT;
|
||||
m_codepoint = 0;
|
||||
}
|
||||
private:
|
||||
uint32_t m_state;
|
||||
uint32_t m_codepoint;
|
||||
};
|
||||
|
||||
// convenience function that creates a validator, validates a complete string
|
||||
// and returns the result.
|
||||
// TODO: should this be inline?
|
||||
inline bool validate(const std::string& s) {
|
||||
validator v;
|
||||
if (!v.decode(s.begin(),s.end())) {
|
||||
return false;
|
||||
}
|
||||
return v.complete();
|
||||
}
|
||||
|
||||
} // namespace utf8_validator
|
||||
|
||||
#endif // UTF8_VALIDATOR_HPP
|
||||
639
src/websocket_frame.hpp
Normal file
639
src/websocket_frame.hpp
Normal file
@@ -0,0 +1,639 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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 WEBSOCKET_FRAME_HPP
|
||||
#define WEBSOCKET_FRAME_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
|
||||
|
||||
#include "network_utilities.hpp"
|
||||
#include "processors/processor.hpp"
|
||||
#include "utf8_validator/utf8_validator.hpp"
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
|
||||
#if defined(WIN32)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
namespace websocketpp {
|
||||
namespace frame {
|
||||
|
||||
/* policies to abstract out
|
||||
|
||||
- random number generation
|
||||
- utf8 validation
|
||||
|
||||
rng
|
||||
int32_t gen()
|
||||
|
||||
|
||||
class boost_random {
|
||||
public:
|
||||
boost_random()
|
||||
int32_t gen();
|
||||
private:
|
||||
boost::random::random_device m_rng;
|
||||
boost::random::variate_generator<boost::random::random_device&,boost::random::uniform_int_distribution<> > m_gen;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
template <class rng_policy>
|
||||
class parser : boost::noncopyable {
|
||||
public:
|
||||
// basic payload byte flags
|
||||
static const uint8_t BPB0_OPCODE = 0x0F;
|
||||
static const uint8_t BPB0_RSV3 = 0x10;
|
||||
static const uint8_t BPB0_RSV2 = 0x20;
|
||||
static const uint8_t BPB0_RSV1 = 0x40;
|
||||
static const uint8_t BPB0_FIN = 0x80;
|
||||
static const uint8_t BPB1_PAYLOAD = 0x7F;
|
||||
static const uint8_t BPB1_MASK = 0x80;
|
||||
|
||||
static const uint8_t BASIC_PAYLOAD_16BIT_CODE = 0x7E; // 126
|
||||
static const uint8_t BASIC_PAYLOAD_64BIT_CODE = 0x7F; // 127
|
||||
|
||||
static const unsigned int BASIC_HEADER_LENGTH = 2;
|
||||
static const unsigned int MAX_HEADER_LENGTH = 14;
|
||||
static const uint8_t extended_header_length = 12;
|
||||
static const uint64_t max_payload_size = 100000000; // 100MB
|
||||
|
||||
// create an empty frame for writing into
|
||||
parser(rng_policy& rng) : m_rng(rng) {
|
||||
reset();
|
||||
}
|
||||
|
||||
bool ready() const {
|
||||
return (m_state == STATE_READY);
|
||||
}
|
||||
uint64_t get_bytes_needed() const {
|
||||
return m_bytes_needed;
|
||||
}
|
||||
void reset() {
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
m_bytes_needed = BASIC_HEADER_LENGTH;
|
||||
m_degraded = false;
|
||||
m_payload.clear();
|
||||
std::fill(m_header,m_header+MAX_HEADER_LENGTH,0);
|
||||
}
|
||||
|
||||
// Method invariant: One of the following must always be true even in the case
|
||||
// of exceptions.
|
||||
// - m_bytes_needed > 0
|
||||
// - m-state = STATE_READY
|
||||
void consume(std::istream &s) {
|
||||
try {
|
||||
switch (m_state) {
|
||||
case STATE_BASIC_HEADER:
|
||||
s.read(&m_header[BASIC_HEADER_LENGTH-m_bytes_needed],m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= s.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_basic_header();
|
||||
|
||||
validate_basic_header();
|
||||
|
||||
if (m_bytes_needed > 0) {
|
||||
m_state = STATE_EXTENDED_HEADER;
|
||||
} else {
|
||||
process_extended_header();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
m_state = STATE_READY;
|
||||
process_payload();
|
||||
|
||||
} else {
|
||||
m_state = STATE_PAYLOAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_EXTENDED_HEADER:
|
||||
s.read(&m_header[get_header_len()-m_bytes_needed],m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= s.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
process_extended_header();
|
||||
if (m_bytes_needed == 0) {
|
||||
m_state = STATE_READY;
|
||||
process_payload();
|
||||
} else {
|
||||
m_state = STATE_PAYLOAD;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_PAYLOAD:
|
||||
s.read(reinterpret_cast<char *>(&m_payload[m_payload.size()-m_bytes_needed]),
|
||||
m_bytes_needed);
|
||||
|
||||
m_bytes_needed -= s.gcount();
|
||||
|
||||
if (m_bytes_needed == 0) {
|
||||
m_state = STATE_READY;
|
||||
process_payload();
|
||||
}
|
||||
break;
|
||||
case STATE_RECOVERY:
|
||||
// Recovery state discards all bytes that are not the first byte
|
||||
// of a close frame.
|
||||
do {
|
||||
s.read(reinterpret_cast<char *>(&m_header[0]),1);
|
||||
|
||||
//std::cout << std::hex << int(static_cast<unsigned char>(m_header[0])) << " ";
|
||||
|
||||
if (int(static_cast<unsigned char>(m_header[0])) == 0x88) {
|
||||
//(BPB0_FIN && CONNECTION_CLOSE)
|
||||
m_bytes_needed--;
|
||||
m_state = STATE_BASIC_HEADER;
|
||||
break;
|
||||
}
|
||||
} while (s.gcount() > 0);
|
||||
|
||||
//std::cout << std::endl;
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*if (s.gcount() == 0) {
|
||||
throw frame_error("consume read zero bytes",FERR_FATAL_SESSION_ERROR);
|
||||
}*/
|
||||
} catch (const processor::exception& e) {
|
||||
// After this point all non-close frames must be considered garbage,
|
||||
// including the current one. Reset it and put the reading frame into
|
||||
// a recovery state.
|
||||
if (m_degraded == true) {
|
||||
throw processor::exception("An error occurred while trying to gracefully recover from a less serious frame error.",processor::error::FATAL_ERROR);
|
||||
} else {
|
||||
reset();
|
||||
m_state = STATE_RECOVERY;
|
||||
m_degraded = true;
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get pointers to underlying buffers
|
||||
char* get_header() {
|
||||
return m_header;
|
||||
}
|
||||
char* get_extended_header() {
|
||||
return m_header+BASIC_HEADER_LENGTH;
|
||||
}
|
||||
unsigned int get_header_len() const {
|
||||
unsigned int temp = 2;
|
||||
|
||||
if (get_masked()) {
|
||||
temp += 4;
|
||||
}
|
||||
|
||||
if (get_basic_size() == 126) {
|
||||
temp += 2;
|
||||
} else if (get_basic_size() == 127) {
|
||||
temp += 8;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
char* get_masking_key() {
|
||||
/*if (m_state != STATE_READY) {
|
||||
std::cout << "throw" << std::endl;
|
||||
throw processor::exception("attempted to get masking_key before reading full header");
|
||||
}*/
|
||||
return &m_header[get_header_len()-4];
|
||||
}
|
||||
|
||||
// get and set header bits
|
||||
bool get_fin() const {
|
||||
return ((m_header[0] & BPB0_FIN) == BPB0_FIN);
|
||||
}
|
||||
void set_fin(bool fin) {
|
||||
if (fin) {
|
||||
m_header[0] |= BPB0_FIN;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_FIN);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_rsv1() const {
|
||||
return ((m_header[0] & BPB0_RSV1) == BPB0_RSV1);
|
||||
}
|
||||
void set_rsv1(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV1;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV1);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_rsv2() const {
|
||||
return ((m_header[0] & BPB0_RSV2) == BPB0_RSV2);
|
||||
}
|
||||
void set_rsv2(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV2;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV2);
|
||||
}
|
||||
}
|
||||
|
||||
bool get_rsv3() const {
|
||||
return ((m_header[0] & BPB0_RSV3) == BPB0_RSV3);
|
||||
}
|
||||
void set_rsv3(bool b) {
|
||||
if (b) {
|
||||
m_header[0] |= BPB0_RSV3;
|
||||
} else {
|
||||
m_header[0] &= (0xFF ^ BPB0_RSV3);
|
||||
}
|
||||
}
|
||||
|
||||
opcode::value get_opcode() const {
|
||||
return frame::opcode::value(m_header[0] & BPB0_OPCODE);
|
||||
}
|
||||
void set_opcode(opcode::value op) {
|
||||
if (opcode::reserved(op)) {
|
||||
throw processor::exception("reserved opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (opcode::invalid(op)) {
|
||||
throw processor::exception("invalid opcode",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
m_header[0] &= (0xFF ^ BPB0_OPCODE); // clear op bits
|
||||
m_header[0] |= op; // set op bits
|
||||
}
|
||||
|
||||
bool get_masked() const {
|
||||
return ((m_header[1] & BPB1_MASK) == BPB1_MASK);
|
||||
}
|
||||
void set_masked(bool masked) {
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
generate_masking_key();
|
||||
} else {
|
||||
m_header[1] &= (0xFF ^ BPB1_MASK);
|
||||
clear_masking_key();
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t get_basic_size() const {
|
||||
return m_header[1] & BPB1_PAYLOAD;
|
||||
}
|
||||
size_t get_payload_size() const {
|
||||
if (m_state != STATE_READY && m_state != STATE_PAYLOAD) {
|
||||
// TODO: how to handle errors like this?
|
||||
throw "attempted to get payload size before reading full header";
|
||||
}
|
||||
|
||||
return m_payload.size();
|
||||
}
|
||||
|
||||
close::status::value get_close_status() const {
|
||||
if (get_payload_size() == 0) {
|
||||
return close::status::NO_STATUS;
|
||||
} else if (get_payload_size() >= 2) {
|
||||
char val[2] = { m_payload[0], m_payload[1] };
|
||||
uint16_t code;
|
||||
|
||||
std::copy(val,val+sizeof(code),&code);
|
||||
code = ntohs(code);
|
||||
|
||||
return close::status::value(code);
|
||||
} else {
|
||||
return close::status::PROTOCOL_ERROR;
|
||||
}
|
||||
}
|
||||
std::string get_close_msg() const {
|
||||
if (get_payload_size() > 2) {
|
||||
uint32_t state = utf8_validator::UTF8_ACCEPT;
|
||||
uint32_t codep = 0;
|
||||
validate_utf8(&state,&codep,2);
|
||||
if (state != utf8_validator::UTF8_ACCEPT) {
|
||||
throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
return std::string(m_payload.begin()+2,m_payload.end());
|
||||
} else {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<unsigned char> &get_payload() {
|
||||
return m_payload;
|
||||
}
|
||||
|
||||
void set_payload(const std::vector<unsigned char>& source) {
|
||||
set_payload_helper(source.size());
|
||||
|
||||
std::copy(source.begin(),source.end(),m_payload.begin());
|
||||
}
|
||||
void set_payload(const std::string& source) {
|
||||
set_payload_helper(source.size());
|
||||
|
||||
std::copy(source.begin(),source.end(),m_payload.begin());
|
||||
}
|
||||
void set_payload_helper(size_t s) {
|
||||
if (s > max_payload_size) {
|
||||
throw processor::exception("requested payload is over implementation defined limit",processor::error::MESSAGE_TOO_BIG);
|
||||
}
|
||||
|
||||
// limits imposed by the websocket spec
|
||||
if (is_control() && s > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("control frames can't have large payloads",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
bool masked = get_masked();
|
||||
|
||||
if (s <= limits::PAYLOAD_SIZE_BASIC) {
|
||||
m_header[1] = s;
|
||||
} else if (s <= limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
m_header[1] = BASIC_PAYLOAD_16BIT_CODE;
|
||||
|
||||
// this reinterprets the second pair of bytes in m_header as a
|
||||
// 16 bit int and writes the payload size there as an integer
|
||||
// in network byte order
|
||||
*reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH]) = htons(s);
|
||||
} else if (s <= limits::PAYLOAD_SIZE_JUMBO) {
|
||||
m_header[1] = BASIC_PAYLOAD_64BIT_CODE;
|
||||
*reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH]) = zsutil::htonll(s);
|
||||
} else {
|
||||
throw processor::exception("payload size limit is 63 bits",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
}
|
||||
|
||||
m_payload.resize(s);
|
||||
}
|
||||
|
||||
void set_status(close::status::value status,const std::string message = "") {
|
||||
// check for valid statuses
|
||||
if (close::status::invalid(status)) {
|
||||
std::stringstream err;
|
||||
err << "Status code " << status << " is invalid";
|
||||
throw processor::exception(err.str());
|
||||
}
|
||||
|
||||
if (close::status::reserved(status)) {
|
||||
std::stringstream err;
|
||||
err << "Status code " << status << " is reserved";
|
||||
throw processor::exception(err.str());
|
||||
}
|
||||
|
||||
m_payload.resize(2+message.size());
|
||||
|
||||
char val[2];
|
||||
|
||||
*reinterpret_cast<uint16_t*>(&val[0]) = htons(status);
|
||||
|
||||
bool masked = get_masked();
|
||||
|
||||
m_header[1] = message.size()+2;
|
||||
|
||||
if (masked) {
|
||||
m_header[1] |= BPB1_MASK;
|
||||
}
|
||||
|
||||
m_payload[0] = val[0];
|
||||
m_payload[1] = val[1];
|
||||
|
||||
std::copy(message.begin(),message.end(),m_payload.begin()+2);
|
||||
}
|
||||
|
||||
bool is_control() const {
|
||||
return (opcode::is_control(get_opcode()));
|
||||
}
|
||||
|
||||
std::string print_frame() const {
|
||||
std::stringstream f;
|
||||
|
||||
unsigned int len = get_header_len();
|
||||
|
||||
f << "frame: ";
|
||||
// print header
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
f << std::hex << (unsigned short)m_header[i] << " ";
|
||||
}
|
||||
// print message
|
||||
if (m_payload.size() > 50) {
|
||||
f << "[payload of " << m_payload.size() << " bytes]";
|
||||
} else {
|
||||
std::vector<unsigned char>::const_iterator it;
|
||||
for (it = m_payload.begin(); it != m_payload.end(); it++) {
|
||||
f << *it;
|
||||
}
|
||||
}
|
||||
return f.str();
|
||||
}
|
||||
|
||||
// reads basic header, sets and returns m_header_bits_needed
|
||||
void process_basic_header() {
|
||||
m_bytes_needed = get_header_len() - BASIC_HEADER_LENGTH;
|
||||
}
|
||||
void process_extended_header() {
|
||||
uint8_t s = get_basic_size();
|
||||
uint64_t payload_size;
|
||||
int mask_index = BASIC_HEADER_LENGTH;
|
||||
|
||||
if (s <= limits::PAYLOAD_SIZE_BASIC) {
|
||||
payload_size = s;
|
||||
} else if (s == BASIC_PAYLOAD_16BIT_CODE) {
|
||||
// reinterpret the second two bytes as a 16 bit integer in network
|
||||
// byte order. Convert to host byte order and store locally.
|
||||
payload_size = ntohs(*(
|
||||
reinterpret_cast<uint16_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (payload_size < s) {
|
||||
std::stringstream err;
|
||||
err << "payload length not minimally encoded. Using 16 bit form for payload size: " << payload_size;
|
||||
m_bytes_needed = payload_size;
|
||||
throw processor::exception(err.str(),processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
mask_index += 2;
|
||||
} else if (s == BASIC_PAYLOAD_64BIT_CODE) {
|
||||
// reinterpret the second eight bytes as a 64 bit integer in
|
||||
// network byte order. Convert to host byte order and store.
|
||||
payload_size = zsutil::ntohll(*(
|
||||
reinterpret_cast<uint64_t*>(&m_header[BASIC_HEADER_LENGTH])
|
||||
));
|
||||
|
||||
if (payload_size <= limits::PAYLOAD_SIZE_EXTENDED) {
|
||||
m_bytes_needed = payload_size;
|
||||
throw processor::exception("payload length not minimally encoded",
|
||||
processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
mask_index += 8;
|
||||
} else {
|
||||
// TODO: shouldn't be here how to handle?
|
||||
throw processor::exception("invalid get_basic_size in process_extended_header");
|
||||
}
|
||||
|
||||
if (get_masked() == 0) {
|
||||
clear_masking_key();
|
||||
} else {
|
||||
// TODO: this should be removed entirely once it is confirmed to not
|
||||
// be used by anything.
|
||||
// std::copy(m_header[mask_index],m_header[mask_index+4],m_masking_key);
|
||||
/*m_masking_key[0] = m_header[mask_index+0];
|
||||
m_masking_key[1] = m_header[mask_index+1];
|
||||
m_masking_key[2] = m_header[mask_index+2];
|
||||
m_masking_key[3] = m_header[mask_index+3];*/
|
||||
}
|
||||
|
||||
if (payload_size > max_payload_size) {
|
||||
// TODO: frame/message size limits
|
||||
// TODO: find a way to throw a server error without coupling frame
|
||||
// with server
|
||||
// throw websocketpp::server_error("got frame with payload greater than maximum frame buffer size.");
|
||||
throw "Got frame with payload greater than maximum frame buffer size.";
|
||||
}
|
||||
m_payload.resize(payload_size);
|
||||
m_bytes_needed = payload_size;
|
||||
}
|
||||
|
||||
void process_payload() {
|
||||
if (get_masked()) {
|
||||
char *masking_key = get_masking_key();
|
||||
|
||||
for (uint64_t i = 0; i < m_payload.size(); i++) {
|
||||
m_payload[i] = (m_payload[i] ^ masking_key[i%4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// experiment with more efficient masking code.
|
||||
void process_payload2() {
|
||||
// unmask payload one byte at a time
|
||||
|
||||
//uint64_t key = (*((uint32_t*)m_masking_key;)) << 32;
|
||||
//key += *((uint32_t*)m_masking_key);
|
||||
|
||||
// might need to switch byte order
|
||||
/*uint32_t key = *((uint32_t*)m_masking_key);
|
||||
|
||||
// 4
|
||||
|
||||
uint64_t i = 0;
|
||||
uint64_t s = (m_payload.size() / 4);
|
||||
|
||||
std::cout << "s: " << s << std::endl;
|
||||
|
||||
// chunks of 4
|
||||
for (i = 0; i < s; i+=4) {
|
||||
((uint32_t*)(&m_payload[0]))[i] = (((uint32_t*)(&m_payload[0]))[i] ^ key);
|
||||
}
|
||||
|
||||
// finish the last few
|
||||
for (i = s; i < m_payload.size(); i++) {
|
||||
m_payload[i] = (m_payload[i] ^ m_masking_key[i%4]);
|
||||
}*/
|
||||
}
|
||||
|
||||
void validate_utf8(uint32_t* state,uint32_t* codep,size_t offset = 0) const {
|
||||
for (size_t i = offset; i < m_payload.size(); i++) {
|
||||
using utf8_validator::decode;
|
||||
|
||||
if (decode(state,codep,m_payload[i]) == utf8_validator::UTF8_REJECT) {
|
||||
throw processor::exception("Invalid UTF-8 Data",processor::error::PAYLOAD_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
void validate_basic_header() const {
|
||||
// check for control frame size
|
||||
if (is_control() && get_basic_size() > limits::PAYLOAD_SIZE_BASIC) {
|
||||
throw processor::exception("Control Frame is too large",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved bits
|
||||
if (get_rsv1() || get_rsv2() || get_rsv3()) {
|
||||
throw processor::exception("Reserved bit used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for reserved opcodes
|
||||
if (opcode::reserved(get_opcode())) {
|
||||
throw processor::exception("Reserved opcode used",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
|
||||
// check for fragmented control message
|
||||
if (is_control() && !get_fin()) {
|
||||
throw processor::exception("Fragmented control message",processor::error::PROTOCOL_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_masking_key() {
|
||||
*(reinterpret_cast<int32_t *>(&m_header[get_header_len()-4])) = m_rng.rand();
|
||||
}
|
||||
void clear_masking_key() {
|
||||
// this is a no-op as clearing the mask bit also changes the get_header_len
|
||||
// method to not include these byte ranges. Whenever the masking bit is re-
|
||||
// set a new key is generated anyways.
|
||||
}
|
||||
|
||||
private:
|
||||
static const uint8_t STATE_BASIC_HEADER = 1;
|
||||
static const uint8_t STATE_EXTENDED_HEADER = 2;
|
||||
static const uint8_t STATE_PAYLOAD = 3;
|
||||
static const uint8_t STATE_READY = 4;
|
||||
static const uint8_t STATE_RECOVERY = 5;
|
||||
|
||||
uint8_t m_state;
|
||||
uint64_t m_bytes_needed;
|
||||
bool m_degraded;
|
||||
|
||||
char m_header[MAX_HEADER_LENGTH];
|
||||
std::vector<unsigned char> m_payload;
|
||||
|
||||
rng_policy& m_rng;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WEBSOCKET_FRAME_HPP
|
||||
63
src/websocketpp.hpp
Normal file
63
src/websocketpp.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2011, 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_HPP
|
||||
#define WEBSOCKETPP_HPP
|
||||
|
||||
#include "common.hpp"
|
||||
#include "endpoint.hpp"
|
||||
|
||||
namespace websocketpp {
|
||||
#ifdef WEBSOCKETPP_ROLE_SERVER_HPP
|
||||
#ifdef WEBSOCKETPP_SOCKET_PLAIN_HPP
|
||||
typedef websocketpp::endpoint<websocketpp::role::server,
|
||||
websocketpp::socket::plain> server;
|
||||
#endif
|
||||
#ifdef WEBSOCKETPP_SOCKET_TLS_HPP
|
||||
typedef websocketpp::endpoint<websocketpp::role::server,
|
||||
websocketpp::socket::tls> server_tls;
|
||||
#endif
|
||||
#ifdef WEBSOCKETPP_SOCKET_AUTOTLS_HPP
|
||||
typedef websocketpp::endpoint<websocketpp::role::server,
|
||||
websocketpp::socket::autotls> server_autotls;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WEBSOCKETPP_ROLE_CLIENT_HPP
|
||||
#ifdef WEBSOCKETPP_SOCKET_PLAIN_HPP
|
||||
typedef websocketpp::endpoint<websocketpp::role::client,
|
||||
websocketpp::socket::plain> client;
|
||||
#endif
|
||||
#ifdef WEBSOCKETPP_SOCKET_TLS_HPP
|
||||
typedef websocketpp::endpoint<websocketpp::role::client,
|
||||
websocketpp::socket::tls> client_tls;
|
||||
#endif
|
||||
#endif
|
||||
} // namespace websocketpp
|
||||
|
||||
#endif // WEBSOCKETPP_HPP
|
||||
Reference in New Issue
Block a user