Squashed 'src/cpp/websocketpp/' content from commit 1ec36a4

git-subtree-dir: src/cpp/websocketpp
git-subtree-split: 1ec36a47468a23f01754fa3a086874e13a4d52d9
This commit is contained in:
Vinnie Falco
2013-05-03 16:47:44 -07:00
commit b1e689fed3
255 changed files with 50215 additions and 0 deletions

20
src/SConscript Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

606
src/endpoint.hpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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.
}

View 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

View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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);
}

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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