Updating websocketpp again to release 0.5.0 (because Windows build fails currently).

This commit is contained in:
lotodore committed 2015-01-24 12:30:04 +01:00
1 parent cc5998e863
commit a2109df5e9
63 files changed
+2072 -664

No files matched your search

+19
View File
@@ -150,6 +150,21 @@ namespace status {
* illegal on the wire.
*/
static value const tls_handshake = 1015;
/// A generic subprotocol error
/**
* Indicates that a subprotocol error occurred. Typically this involves
* receiving a message that is not formatted as a valid message for the
* subprotocol in use.
*/
static value const subprotocol_error = 3000;
/// A invalid subprotocol data
/**
* Indicates that data was received that violated the specification of the
* subprotocol in use.
*/
static value const invalid_subprotocol_data = 3001;
/// First value in range reserved for future protocol use
static value const rsv_start = 1016;
@@ -235,6 +250,10 @@ namespace status {
return "Internal endpoint error";
case tls_handshake:
return "TLS handshake failure";
case subprotocol_error:
return "Generic subprotocol error";
case invalid_subprotocol_data:
return "Invalid subprotocol data";
default:
return "Unknown";
}
@@ -51,6 +51,11 @@
// C++11 compiler via the __cplusplus macro or the user/build system
// supplies one of the two preprocessor defines below:
// This is defined to allow other WebSocket++ common headers to enable
// C++11 features when they are detected by this file rather than
// duplicating the above logic in every common header.
#define _WEBSOCKETPP_CPP11_INTERNAL_
// _WEBSOCKETPP_CPP11_STRICT_
//
// This define reports to WebSocket++ that 100% of the language and library
@@ -28,12 +28,27 @@
#ifndef WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
#define WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 functional header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 functional header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#include <functional>
#else
@@ -52,14 +67,14 @@ namespace lib {
using std::bind;
using std::ref;
namespace placeholders = std::placeholders;
// There are some cases where a C++11 compiler balks at using std::ref
// but a C++03 compiler using boost function requires boost::ref. As such
// lib::ref is not useful in these cases. Instead this macro allows the use
// of boost::ref in the case of a boost compile or no reference wrapper at
// all in the case of a C++11 compile
#define _WEBSOCKETPP_REF(x) x
template <typename T>
void clear_function(T & x) {
x = nullptr;
@@ -72,11 +87,12 @@ namespace lib {
/// \todo this feels hacky, is there a better way?
using ::_1;
using ::_2;
using ::_3;
}
// See above definition for more details on what this is and why it exists
#define _WEBSOCKETPP_REF(x) boost::ref(x)
template <typename T>
void clear_function(T & x) {
x.clear();
+16 -1
View File
@@ -28,12 +28,27 @@
#ifndef WEBSOCKETPP_COMMON_MEMORY_HPP
#define WEBSOCKETPP_COMMON_MEMORY_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 memory header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 functional header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
#include <memory>
#else
+1 -1
View File
@@ -42,7 +42,7 @@ namespace net {
inline bool is_little_endian() {
short int val = 0x1;
char *ptr = (char*)&val;
char *ptr = reinterpret_cast<char *>(&val);
return (ptr[0] == 1);
}
@@ -38,4 +38,9 @@
#define NOMINMAX
#endif
// Bump up the variadic parameter max for Visual Studio 2012
#if defined(_MSC_VER) && _MSC_VER == 1700
#define _VARIADIC_MAX 8
#endif
#endif // WEBSOCKETPP_COMMON_PLATFORMS_HPP
+17 -1
View File
@@ -28,12 +28,28 @@
#ifndef WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
#define WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 random header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_
#ifndef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 random header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#include <random>
#else
@@ -28,12 +28,28 @@
#ifndef WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
#define WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 system_error header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 system_error header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#include <system_error>
#else
+13 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,9 +28,18 @@
#ifndef WEBSOCKETPP_COMMON_THREAD_HPP
#define WEBSOCKETPP_COMMON_THREAD_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
#ifndef _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_THREAD_
#include <websocketpp/common/cpp11.hpp>
// If we autodetect C++11 and haven't been explicitly instructed to not use
// C++11 threads, then set the defines that instructs the rest of this header
// to use C++11 <thread> and <mutex>
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
// MinGW by default does not support C++11 thread/mutex so even if the
// internal check for C++11 passes, ignore it if we are on MinGW
#if (!defined(__MINGW32__) && !defined(__MINGW64__))
#ifndef _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_THREAD_
#endif
#endif
#endif
+4 -2
View File
@@ -28,7 +28,7 @@
#ifndef WEBSOCKETPP_COMMON_TIME_HPP
#define WEBSOCKETPP_COMMON_TIME_HPP
#include <time.h>
#include <ctime>
namespace websocketpp {
namespace lib {
@@ -40,7 +40,9 @@ namespace lib {
/// Thread safe cross platform localtime
inline std::tm localtime(std::time_t const & time) {
std::tm tm_snapshot;
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
#if (defined(__MINGW32__) || defined(__MINGW64__))
memcpy(&tm_snapshot, ::localtime(&time), sizeof(std::tm));
#elif (defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
localtime_s(&tm_snapshot, &time);
#else
localtime_r(&time, &tm_snapshot); // POSIX
+12
View File
@@ -226,6 +226,18 @@ struct core {
* @since 0.3.0
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
@@ -236,6 +236,18 @@ struct core_client {
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
@@ -228,6 +228,18 @@ struct debug_core {
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
@@ -112,10 +112,8 @@ struct minimal_server {
endpoint_msg_manager_type;
/// Logging policies
typedef websocketpp::log::stub<concurrency_type,
websocketpp::log::elevel> elog_type;
typedef websocketpp::log::stub<concurrency_type,
websocketpp::log::alevel> alog_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
/// RNG policies
typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
@@ -256,6 +254,18 @@ struct minimal_server {
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
+75 -60
View File
@@ -29,18 +29,18 @@
#define WEBSOCKETPP_CONNECTION_HPP
#include <websocketpp/close.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/frame.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <algorithm>
#include <iostream>
#include <queue>
#include <sstream>
#include <string>
@@ -119,7 +119,7 @@ typedef lib::function<void(connection_hdl,std::string)> pong_timeout_handler;
/**
* The validate handler is called after a WebSocket handshake has been received
* and processed but before it has been accepted. This gives the application a
* chance to impliment connection details specific policies for accepting
* chance to implement connection details specific policies for accepting
* connections and the ability to negotiate extensions and subprotocols.
*
* The validate handler return value indicates whether or not the connection
@@ -311,6 +311,7 @@ public:
, m_rng(rng)
, m_local_close_code(close::status::abnormal_close)
, m_remote_close_code(close::status::abnormal_close)
, m_is_http(false)
, m_was_clean(false)
{
m_alog.write(log::alevel::devel,"connection constructor");
@@ -533,8 +534,8 @@ public:
/// Get maximum message size
/**
* Get maximum message size. Maximum message size determines the point at which the
* connection will fail a connection with the message_too_big protocol error.
* Get maximum message size. Maximum message size determines the point at
* which the connection will fail with the message_too_big protocol error.
*
* The default is set by the endpoint that creates the connection.
*
@@ -546,9 +547,9 @@ public:
/// Set maximum message size
/**
* Set maximum message size. Maximum message size determines the point at which the
* connection will fail a connection with the message_too_big protocol error. This
* value may be changed during the connection.
* Set maximum message size. Maximum message size determines the point at
* which the connection will fail with the message_too_big protocol error.
* This value may be changed during the connection.
*
* The default is set by the endpoint that creates the connection.
*
@@ -562,6 +563,38 @@ public:
m_processor->set_max_message_size(new_value);
}
}
/// Get maximum HTTP message body size
/**
* Get maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the endpoint that creates the connection.
*
* @since 0.5.0
*
* @return The maximum HTTP message body size
*/
size_t get_max_http_body_size() const {
return m_request.get_max_body_size();
}
/// Set maximum HTTP message body size
/**
* Set maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the endpoint that creates the connection.
*
* @since 0.5.0
*
* @param new_value The value to set as the maximum message size.
*/
void set_max_http_body_size(size_t new_value) {
m_request.set_max_body_size(new_value);
}
//////////////////////////////////
// Uncategorized public methods //
@@ -899,7 +932,18 @@ public:
* @param key Name of the header to get
* @return The value of the header
*/
std::string const & get_request_header(std::string const & key);
std::string const & get_request_header(std::string const & key) const;
/// Retrieve a request body
/**
* Retrieve the value of the request body. This value is typically used with
* PUT and POST requests to upload files or other data. Only HTTP
* connections will ever have bodies. WebSocket connection's will always
* have blank bodies.
*
* @return The value of the request body.
*/
std::string const & get_request_body() const;
/// Retrieve a response header
/**
@@ -908,7 +952,7 @@ public:
* @param key Name of the header to get
* @return The value of the header
*/
std::string const & get_response_header(std::string const & key);
std::string const & get_response_header(std::string const & key) const;
/// Set response status code and message
/**
@@ -1168,7 +1212,7 @@ public:
void read_frame();
/// Get array of WebSocket protocol versions that this connection supports.
const std::vector<int>& get_supported_versions() const;
std::vector<int> const & get_supported_versions() const;
/// Sets the handler for a terminating connection. Should only be used
/// internally by the endpoint class.
@@ -1202,60 +1246,21 @@ protected:
void handle_transport_init(lib::error_code const & ec);
/// Set m_processor based on information in m_request. Set m_response
/// status and return false on error.
bool initialize_processor();
/// status and return an error code indicating status.
lib::error_code initialize_processor();
/// Perform WebSocket handshake validation of m_request using m_processor.
/// set m_response and return false on error.
bool process_handshake_request();
/// Atomically change the internal connection state.
/**
* @param req The required starting state. If the internal state does not
* match req an exception is thrown.
*
* @param dest The state to change to.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_change(istate_type req, istate_type dest,
std::string msg);
/// Atomically change the internal and external connection state.
/**
* @param ireq The required starting internal state. If the internal state
* does not match ireq an exception is thrown.
*
* @param idest The internal state to change to.
*
* @param ereq The required starting external state. If the external state
* does not match ereq an exception is thrown.
*
* @param edest The external state to change to.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_change(istate_type ireq, istate_type idest,
session::state::value ereq, session::state::value edest,
std::string msg);
/// Atomically read and compared the internal state.
/**
* @param req The state to test against. If the internal state does not
* match req an exception is thrown.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_check(istate_type req, std::string msg);
/// set m_response and return an error code indicating status.
lib::error_code process_handshake_request();
private:
/// Completes m_response, serializes it, and sends it out on the wire.
void send_http_response();
void send_http_response(lib::error_code const & ec);
/// Sends an opening WebSocket connect request
void send_http_request();
/// Alternate path for send_http_response in error conditions
void send_http_response_error();
void send_http_response_error(lib::error_code const & ec);
/// Process control message
/**
@@ -1352,6 +1357,12 @@ private:
* Includes: error code and message for why it was failed
*/
void log_fail_result();
/// Prints information about HTTP connections
/**
* Includes: TODO
*/
void log_http_result();
/// Prints information about an arbitrary error code on the specified channel
template <typename error_type>
@@ -1495,6 +1506,10 @@ private:
/// Detailed internal error code
lib::error_code m_ec;
/// A flag that gets set once it is determined that the connection is an
/// HTTP connection and not a WebSocket one.
bool m_is_http;
bool m_was_clean;
+59 -22
View File
@@ -29,10 +29,11 @@
#define WEBSOCKETPP_ENDPOINT_HPP
#include <websocketpp/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/version.hpp>
#include <set>
#include <string>
namespace websocketpp {
@@ -84,8 +85,6 @@ public:
// TODO: organize these
typedef typename connection_type::termination_handler termination_handler;
typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
explicit endpoint(bool p_is_server)
: m_alog(config::alog_level, log::channel_type_hint::access)
, m_elog(config::elog_level, log::channel_type_hint::error)
@@ -94,6 +93,7 @@ public:
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
, m_pong_timeout_dur(config::timeout_pong)
, m_max_message_size(config::max_message_size)
, m_max_http_body_size(config::max_http_body_size)
, m_is_server(p_is_server)
{
m_alog.set_channels(config::alog_level);
@@ -350,9 +350,10 @@ public:
/// Get default maximum message size
/**
* Get the default maximum message size that will be used for new connections created
* by this endpoint. The maximum message size determines the point at which the
* connection will fail a connection with the message_too_big protocol error.
* Get the default maximum message size that will be used for new
* connections created by this endpoint. The maximum message size determines
* the point at which the connection will fail a connection with the
* message_too_big protocol error.
*
* The default is set by the max_message_size value from the template config
*
@@ -364,9 +365,10 @@ public:
/// Set default maximum message size
/**
* Set the default maximum message size that will be used for new connections created
* by this endpoint. Maximum message size determines the point at which the connection
* will fail a connection with the message_too_big protocol error.
* Set the default maximum message size that will be used for new
* connections created by this endpoint. Maximum message size determines the
* point at which the connection will fail a connection with the
* message_too_big protocol error.
*
* The default is set by the max_message_size value from the template config
*
@@ -378,6 +380,40 @@ public:
m_max_message_size = new_value;
}
/// Get maximum HTTP message body size
/**
* Get maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the max_http_body_size value from the template
* config
*
* @since 0.5.0
*
* @return The maximum HTTP message body size
*/
size_t get_max_http_body_size() const {
return m_max_http_body_size;
}
/// Set maximum HTTP message body size
/**
* Set maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the max_http_body_size value from the template
* config
*
* @since 0.5.0
*
* @param new_value The value to set as the maximum message size.
*/
void get_max_http_body_size(size_t new_value) {
m_max_http_body_size = new_value;
}
/*************************************/
/* Connection pass through functions */
/*************************************/
@@ -395,21 +431,21 @@ public:
/// Pause reading of new data (exception free)
/**
* Signals to the connection to halt reading of new data. While reading is paused,
* the connection will stop reading from its associated socket. In turn this will
* result in TCP based flow control kicking in and slowing data flow from the remote
* endpoint.
* Signals to the connection to halt reading of new data. While reading is
* paused, the connection will stop reading from its associated socket. In
* turn this will result in TCP based flow control kicking in and slowing
* data flow from the remote endpoint.
*
* This is useful for applications that push new requests to a queue to be processed
* by another thread and need a way to signal when their request queue is full without
* blocking the network processing thread.
* This is useful for applications that push new requests to a queue to be
* processed by another thread and need a way to signal when their request
* queue is full without blocking the network processing thread.
*
* Use `resume_reading()` to resume.
*
* If supported by the transport this is done asynchronously. As such reading may not
* stop until the current read operation completes. Typically you can expect to
* receive no more bytes after initiating a read pause than the size of the read
* buffer.
* If supported by the transport this is done asynchronously. As such
* reading may not stop until the current read operation completes.
* Typically you can expect to receive no more bytes after initiating a read
* pause than the size of the read buffer.
*
* If reading is paused for this connection already nothing is changed.
*/
@@ -420,8 +456,8 @@ public:
/// Resume reading of new data (exception free)
/**
* Signals to the connection to resume reading of new data after it was paused by
* `pause_reading()`.
* Signals to the connection to resume reading of new data after it was
* paused by `pause_reading()`.
*
* If reading is not paused for this connection already nothing is changed.
*/
@@ -565,6 +601,7 @@ private:
long m_close_handshake_timeout_dur;
long m_pong_timeout_dur;
size_t m_max_message_size;
size_t m_max_http_body_size;
rng_type m_rng;
+31 -7
View File
@@ -28,7 +28,9 @@
#ifndef WEBSOCKETPP_ERROR_HPP
#define WEBSOCKETPP_ERROR_HPP
#include <exception>
#include <string>
#include <utility>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
@@ -122,7 +124,23 @@ enum value {
async_accept_not_listening,
/// The requested operation was canceled
operation_canceled
operation_canceled,
/// Connection rejected
rejected,
/// Upgrade Required. This happens if an HTTP request is made to a
/// WebSocket++ server that doesn't implement an http handler
upgrade_required,
/// Invalid WebSocket protocol version
invalid_version,
/// Unsupported WebSocket protocol version
unsupported_version,
/// HTTP parse error
http_parse_error
}; // enum value
@@ -188,6 +206,16 @@ public:
return "Async Accept not listening";
case error::operation_canceled:
return "Operation canceled";
case error::rejected:
return "Connection rejected";
case error::upgrade_required:
return "Upgrade required";
case error::invalid_version:
return "Invalid version";
case error::unsupported_version:
return "Unsupported version";
case error::http_parse_error:
return "HTTP parse error";
default:
return "Unknown";
}
@@ -218,7 +246,7 @@ namespace websocketpp {
class exception : public std::exception {
public:
exception(std::string const & msg, lib::error_code ec = make_error_code(error::general))
: m_msg(msg), m_code(ec)
: m_msg(msg.empty() ? ec.message() : msg), m_code(ec)
{}
explicit exception(lib::error_code ec)
@@ -228,11 +256,7 @@ public:
~exception() throw() {}
virtual char const * what() const throw() {
if (m_msg.empty()) {
return m_code.message().c_str();
} else {
return m_msg.c_str();
}
return m_msg.c_str();
}
lib::error_code code() const throw() {
@@ -1,71 +0,0 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_ERROR_MESSAGE_HPP
#define WEBSOCKETPP_ERROR_MESSAGE_HPP
namespace websocketpp {
/**
* The transport::security::* classes are a set of security/socket related
* policies and support code for the ASIO transport types.
*/
class error_msg {
public:
const std::string& get_msg() const {
return m_error_msg;
}
void set_msg(const std::string& msg) {
m_error_msg = msg;
}
void append_msg(const std::string& msg) {
m_error_msg.append(msg);
}
template <typename T>
void set_msg(const T& thing) {
std::stringsteam val;
val << thing;
this->set_msg(val.str());
}
template <typename T>
void append_msg(const T& thing) {
std::stringsteam val;
val << thing;
this->append_msg(val.str());
}
private:
// error resources
std::string m_error_msg;
};
} // namespace websocketpp
#endif // WEBSOCKETPP_ERROR_MESSAGE_HPP
@@ -46,13 +46,13 @@
namespace websocketpp {
namespace extensions {
/// Implimentation of the draft permessage-deflate WebSocket extension
/// Implementation of the draft permessage-deflate WebSocket extension
/**
* ### permessage-deflate interface
*
* **is_implemented**\n
* `bool is_implemented()`\n
* Returns whether or not the object impliments the extension or not
* Returns whether or not the object implements the extension or not
*
* **is_enabled**\n
* `bool is_enabled()`\n
@@ -287,11 +287,11 @@ public:
return lib::error_code();
}
/// Test if this object impliments the permessage-deflate specification
/// Test if this object implements the permessage-deflate specification
/**
* Because this object does impliment it, it will always return true.
* Because this object does implieent it, it will always return true.
*
* @return Whether or not this object impliments permessage-deflate
* @return Whether or not this object implements permessage-deflate
*/
bool is_implemented() const {
return true;
+1
View File
@@ -29,6 +29,7 @@
#define WEBSOCKETPP_FRAME_HPP
#include <algorithm>
#include <string>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/network.hpp>
@@ -28,9 +28,11 @@
#ifndef HTTP_CONSTANTS_HPP
#define HTTP_CONSTANTS_HPP
#include <exception>
#include <map>
#include <string>
#include <vector>
#include <utility>
namespace websocketpp {
/// HTTP handling support
@@ -61,6 +63,9 @@ namespace http {
/// Maximum size in bytes before rejecting an HTTP header as too big.
size_t const max_header_size = 16000;
/// Default Maximum size in bytes for HTTP message bodies.
size_t const max_body_size = 32000000;
/// Number of bytes to use for temporary istream read buffers
size_t const istream_buffer = 512;
+42 -17
View File
@@ -29,6 +29,8 @@
#define HTTP_PARSER_IMPL_HPP
#include <algorithm>
#include <cstdlib>
#include <istream>
#include <sstream>
#include <string>
@@ -93,6 +95,9 @@ inline void parser::set_body(std::string const & value) {
return;
}
// TODO: should this method respect the max size? If so how should errors
// be indicated?
std::stringstream len;
len << value.size();
replace_header("Content-Length", len.str());
@@ -111,26 +116,46 @@ inline bool parser::parse_parameter_list(std::string const & in,
return (it == in.begin());
}
inline bool parser::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(header_separator,0);
if (end != std::string::npos) {
append_header(header.substr(0,end),header.substr(end+2));
inline bool parser::prepare_body() {
if (get_header("Content-Length") != "") {
std::string const & cl_header = get_header("Content-Length");
char * end;
// TODO: not 100% sure what the compatibility of this method is. Also,
// I believe this will only work up to 32bit sizes. Is there a need for
// > 4GiB HTTP payloads?
m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10);
if (m_body_bytes_needed > m_body_bytes_max) {
throw exception("HTTP message body too large",
status_code::request_entity_too_large);
}
m_body_encoding = body_encoding::plain;
return true;
} else if (get_header("Transfer-Encoding") == "chunked") {
// TODO
//m_body_encoding = body_encoding::chunked;
return false;
} else {
return false;
}
}
return true;
inline size_t parser::process_body(char const * buf, size_t len) {
if (m_body_encoding == body_encoding::plain) {
size_t processed = (std::min)(m_body_bytes_needed,len);
m_body.append(buf,processed);
m_body_bytes_needed -= processed;
return processed;
} else if (m_body_encoding == body_encoding::chunked) {
// TODO:
throw exception("Unexpected body encoding",
status_code::internal_server_error);
} else {
throw exception("Unexpected body encoding",
status_code::internal_server_error);
}
}
inline void parser::process_header(std::string::iterator begin,
+42 -42
View File
@@ -30,6 +30,7 @@
#include <algorithm>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
@@ -37,35 +38,18 @@ namespace websocketpp {
namespace http {
namespace parser {
inline bool request::parse_complete(std::istream& s) {
std::string req;
// get status line
std::getline(s, req);
if (req[req.size()-1] == '\r') {
req.erase(req.end()-1);
std::stringstream ss(req);
std::string val;
ss >> val;
set_method(val);
ss >> val;
set_uri(val);
ss >> val;
set_version(val);
} else {
return false;
}
return parse_headers(s);
}
inline size_t request::consume(const char *buf, size_t len) {
inline size_t request::consume(char const * buf, size_t len) {
size_t bytes_processed;
if (m_ready) {return 0;}
if (m_body_bytes_needed > 0) {
bytes_processed = process_body(buf,len);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
}
if (m_buf->size() + len > max_header_size) {
// exceeded max header size
@@ -107,9 +91,8 @@ inline size_t request::consume(const char *buf, size_t len) {
if (m_method.empty() || get_header("Host") == "") {
throw exception("Incomplete Request",status_code::bad_request);
}
m_ready = true;
size_t bytes_processed = (
bytes_processed = (
len - static_cast<std::string::size_type>(m_buf->end()-end)
+ sizeof(header_delimiter) - 1
);
@@ -117,8 +100,22 @@ inline size_t request::consume(const char *buf, size_t len) {
// frees memory used temporarily during request parsing
m_buf.reset();
// return number of bytes processed (starting bytes - bytes left)
return bytes_processed;
// if this was not an upgrade request and has a content length
// continue capturing content-length bytes and expose them as a
// request body.
if (prepare_body()) {
bytes_processed += process_body(buf+bytes_processed,len-bytes_processed);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
} else {
m_ready = true;
// return number of bytes processed (starting bytes - bytes left)
return bytes_processed;
}
} else {
if (m_method.empty()) {
this->process(begin,end);
@@ -131,7 +128,7 @@ inline size_t request::consume(const char *buf, size_t len) {
}
}
inline std::string request::raw() {
inline std::string request::raw() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
@@ -141,7 +138,17 @@ inline std::string request::raw() {
return ret.str();
}
inline void request::set_method(const std::string& method) {
inline std::string request::raw_head() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n";
return ret.str();
}
inline void request::set_method(std::string const & method) {
if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) {
throw exception("Invalid method token.",status_code::bad_request);
}
@@ -149,14 +156,7 @@ inline void request::set_method(const std::string& method) {
m_method = method;
}
/// Set HTTP body
/**
* Sets the body of the HTTP object and fills in the appropriate content length
* header
*
* @param value The value to set the body to.
*/
inline void request::set_uri(const std::string& uri) {
inline void request::set_uri(std::string const & uri) {
// TODO: validation?
m_uri = uri;
}
@@ -29,7 +29,9 @@
#define HTTP_PARSER_RESPONSE_IMPL_HPP
#include <algorithm>
#include <istream>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
@@ -37,7 +39,7 @@ namespace websocketpp {
namespace http {
namespace parser {
inline size_t response::consume(const char *buf, size_t len) {
inline size_t response::consume(char const * buf, size_t len) {
if (m_state == DONE) {return 0;}
if (m_state == BODY) {
@@ -170,34 +172,6 @@ inline size_t response::consume(std::istream & s) {
return total;
}
inline bool response::parse_complete(std::istream& s) {
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
std::string line;
// get status line
std::getline(s, line);
if (line[line.size()-1] == '\r') {
line.erase(line.end()-1);
std::stringstream ss(line);
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);
}
inline std::string response::raw() const {
// TODO: validation. Make sure all required fields have been set?
@@ -217,7 +191,7 @@ inline void response::set_status(status_code::value code) {
m_status_msg = get_string(code);
}
inline void response::set_status(status_code::value code, const std::string&
inline void response::set_status(status_code::value code, std::string const &
msg)
{
// TODO: validation?
@@ -255,7 +229,7 @@ inline void response::process(std::string::iterator begin,
set_status(status_code::value(code),std::string(cursor_end+1,end));
}
inline size_t response::process_body(const char *buf, size_t len) {
inline size_t response::process_body(char const * buf, size_t len) {
// If no content length was set then we read forever and never set m_ready
if (m_read == 0) {
//m_body.append(buf,len);
+88 -14
View File
@@ -29,8 +29,9 @@
#define HTTP_PARSER_HPP
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <websocketpp/utilities.hpp>
#include <websocketpp/http/constants.hpp>
@@ -48,6 +49,14 @@ namespace state {
};
}
namespace body_encoding {
enum value {
unknown,
plain,
chunked
};
}
typedef std::map<std::string, std::string, utility::ci_less > header_list;
/// Read and return the next token in the stream
@@ -384,6 +393,11 @@ inline std::string strip_lws(std::string const & input) {
*/
class parser {
public:
parser()
: m_body_bytes_needed(0)
, m_body_bytes_max(max_body_size)
, m_body_encoding(body_encoding::unknown) {}
/// Get the HTTP version string
/**
* @return The version string for this parser
@@ -467,12 +481,11 @@ public:
*/
void remove_header(std::string const & key);
/// Set HTTP body
/// Get HTTP body
/**
* Sets the body of the HTTP object and fills in the appropriate content
* length header.
* Gets the body of the HTTP object
*
* @param [in] value The value to set the body to.
* @return The body of the HTTP message.
*/
std::string const & get_body() const {
return m_body;
@@ -489,6 +502,32 @@ public:
*/
void set_body(std::string const & value);
/// Get body size limit
/**
* Retrieves the maximum number of bytes to parse & buffer before canceling
* a request.
*
* @since 0.5.0
*
* @return The maximum length of a message body.
*/
size_t get_max_body_size() const {
return m_body_bytes_max;
}
/// Set body size limit
/**
* Set the maximum number of bytes to parse and buffer before canceling a
* request.
*
* @since 0.5.0
*
* @param value The size to set the max body length to.
*/
void set_max_body_size(size_t value) {
m_body_bytes_max = value;
}
/// Extract an HTTP parameter list from a string.
/**
* @param [in] in The input string.
@@ -498,14 +537,6 @@ public:
bool parse_parameter_list(std::string const & in, parameter_list & out)
const;
protected:
/// Parse headers from an istream
/**
* @deprecated Use process_header instead.
*
* @param [in] s The istream to extract headers from.
*/
bool parse_headers(std::istream & s);
/// Process a header line
/**
* @todo Update this method to be exception free.
@@ -515,6 +546,45 @@ protected:
*/
void process_header(std::string::iterator begin, std::string::iterator end);
/// Prepare the parser to begin parsing body data
/**
* Inspects headers to determine if the message has a body that needs to be
* read. If so, sets up the necessary state, otherwise returns false. If
* this method returns true and loading the message body is desired call
* `process_body` until it returns zero bytes or an error.
*
* Must not be called until after all headers have been processed.
*
* @since 0.5.0
*
* @return True if more bytes are needed to load the body, false otherwise.
*/
bool prepare_body();
/// Process body data
/**
* Parses body data.
*
* @since 0.5.0
*
* @param [in] begin An iterator to the beginning of the sequence.
* @param [in] end An iterator to the end of the sequence.
* @return The number of bytes processed
*/
size_t process_body(char const * buf, size_t len);
/// Check if the parser is done parsing the body
/**
* Behavior before a call to `prepare_body` is undefined.
*
* @since 0.5.0
*
* @return True if the message body has been completed loaded.
*/
bool body_ready() const {
return (m_body_bytes_needed == 0);
}
/// Generate and return the HTTP headers as a string
/**
* Each headers will be followed by the \r\n sequence including the last one.
@@ -526,7 +596,11 @@ protected:
std::string m_version;
header_list m_headers;
std::string m_body;
std::string m_body;
size_t m_body_bytes_needed;
size_t m_body_bytes_max;
body_encoding::value m_body_encoding;
};
} // namespace parser
+12 -10
View File
@@ -28,6 +28,8 @@
#ifndef HTTP_PARSER_REQUEST_HPP
#define HTTP_PARSER_REQUEST_HPP
#include <string>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/http/parser.hpp>
@@ -54,9 +56,6 @@ public:
: m_buf(lib::make_shared<std::string>())
, m_ready(false) {}
/// DEPRECATED parse a complete header (\r\n\r\n MUST be in the istream)
bool parse_complete(std::istream& s);
/// Process bytes in the input buffer
/**
* Process up to len bytes from input buffer buf. Returns the number of
@@ -77,29 +76,32 @@ public:
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(const char *buf, size_t len);
size_t consume(char const * buf, size_t len);
/// Returns whether or not the request is ready for reading.
bool ready() const {
return m_ready;
}
/// Returns the full raw request
std::string raw();
/// Returns the full raw request (including the body)
std::string raw() const;
/// Returns the raw request headers only (similar to an HTTP HEAD request)
std::string raw_head() const;
/// Set the HTTP method. Must be a valid HTTP token
void set_method(const std::string& method);
void set_method(std::string const & method);
/// Return the request method
const std::string& get_method() const {
std::string const & get_method() const {
return m_method;
}
/// Set the HTTP uri. Must be a valid HTTP uri
void set_uri(const std::string& uri);
void set_uri(std::string const & uri);
/// Return the requested URI
const std::string& get_uri() const {
std::string const & get_uri() const {
return m_uri;
}
+26 -6
View File
@@ -28,6 +28,9 @@
#ifndef HTTP_PARSER_RESPONSE_HPP
#define HTTP_PARSER_RESPONSE_HPP
#include <iostream>
#include <string>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
@@ -82,8 +85,28 @@ public:
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(const char *buf, size_t len);
size_t consume(char const * buf, size_t len);
/// Process bytes in the input buffer (istream version)
/**
* Process bytes from istream s. Returns the number of bytes processed.
* Bytes left unprocessed means bytes left over after the final header
* delimiters.
*
* Consume is a streaming processor. It may be called multiple times on one
* response and the full headers need not be available before processing can
* begin. If the end of the response was reached during this call to consume
* the ready flag will be set. Further calls to consume once ready will be
* ignored.
*
* Consume will throw an http::exception in the case of an error. Typical
* error reasons include malformed responses, incomplete responses, and max
* header size being reached.
*
* @param buf Pointer to byte buffer
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(std::istream & s);
/// Returns true if the response is ready.
@@ -99,9 +122,6 @@ public:
return (m_state == BODY || m_state == DONE);
}
/// DEPRECATED parse a complete response from a pre-delimited istream
bool parse_complete(std::istream& s);
/// Returns the full raw response
std::string raw() const;
@@ -126,7 +146,7 @@ public:
* @param code Code to set
* @param msg Message to set
*/
void set_status(status_code::value code, const std::string& msg);
void set_status(status_code::value code, std::string const & msg);
/// Return the response status code
status_code::value get_status_code() const {
@@ -142,7 +162,7 @@ private:
void process(std::string::iterator begin, std::string::iterator end);
/// Helper function for processing body bytes
size_t process_body(const char *buf, size_t len);
size_t process_body(char const * buf, size_t len);
enum state {
RESPONSE_LINE = 0,
+374 -225
View File
@@ -28,16 +28,23 @@
#ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP
#define WEBSOCKETPP_CONNECTION_IMPL_HPP
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/processors/hybi00.hpp>
#include <websocketpp/processors/hybi07.hpp>
#include <websocketpp/processors/hybi08.hpp>
#include <websocketpp/processors/hybi13.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/common/system_error.hpp>
#include <algorithm>
#include <exception>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace websocketpp {
namespace istate = session::internal_state;
@@ -55,7 +62,7 @@ void connection<config>::set_termination_handler(
}
template <typename config>
const std::string& connection<config>::get_origin() const {
std::string const & connection<config>::get_origin() const {
//scoped_lock_type lock(m_connection_state_lock);
return m_processor->get_origin(m_request);
}
@@ -73,7 +80,7 @@ session::state::value connection<config>::get_state() const {
}
template <typename config>
lib::error_code connection<config>::send(const std::string& payload,
lib::error_code connection<config>::send(std::string const & payload,
frame::opcode::value op)
{
message_ptr msg = m_msg_manager->get_message(op,payload.size());
@@ -83,7 +90,7 @@ lib::error_code connection<config>::send(const std::string& payload,
}
template <typename config>
lib::error_code connection<config>::send(const void* payload, size_t len,
lib::error_code connection<config>::send(void const * payload, size_t len,
frame::opcode::value op)
{
message_ptr msg = m_msg_manager->get_message(op,len);
@@ -98,10 +105,12 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"connection send");
}
// TODO:
if (m_state != session::state::open) {
return error::make_error_code(error::invalid_state);
{
scoped_lock_type lock(m_connection_state_lock);
if (m_state != session::state::open) {
return error::make_error_code(error::invalid_state);
}
}
message_ptr outgoing_msg;
@@ -142,14 +151,20 @@ lib::error_code connection<config>::send(typename config::message_type::ptr msg)
}
template <typename config>
void connection<config>::ping(const std::string& payload, lib::error_code& ec) {
void connection<config>::ping(std::string const& payload, lib::error_code& ec) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"connection ping");
}
if (m_state != session::state::open) {
ec = error::make_error_code(error::invalid_state);
return;
{
scoped_lock_type lock(m_connection_state_lock);
if (m_state != session::state::open) {
std::stringstream ss;
ss << "connection::ping called from invalid state " << m_state;
m_alog.write(log::alevel::devel,ss.str());
ec = error::make_error_code(error::invalid_state);
return;
}
}
message_ptr msg = m_msg_manager->get_message();
@@ -233,14 +248,20 @@ void connection<config>::handle_pong_timeout(std::string payload,
}
template <typename config>
void connection<config>::pong(const std::string& payload, lib::error_code& ec) {
void connection<config>::pong(std::string const& payload, lib::error_code& ec) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"connection pong");
}
if (m_state != session::state::open) {
ec = error::make_error_code(error::invalid_state);
return;
{
scoped_lock_type lock(m_connection_state_lock);
if (m_state != session::state::open) {
std::stringstream ss;
ss << "connection::pong called from invalid state " << m_state;
m_alog.write(log::alevel::devel,ss.str());
ec = error::make_error_code(error::invalid_state);
return;
}
}
message_ptr msg = m_msg_manager->get_message();
@@ -286,15 +307,17 @@ void connection<config>::close(close::status::value const code,
m_alog.write(log::alevel::devel,"connection close");
}
// Truncate reason to maximum size allowable in a close frame.
std::string tr(reason,0,std::min<size_t>(reason.size(),
frame::limits::close_reason_size));
scoped_lock_type lock(m_connection_state_lock);
if (m_state != session::state::open) {
ec = error::make_error_code(error::invalid_state);
return;
}
// Truncate reason to maximum size allowable in a close frame.
std::string tr(reason,0,std::min<size_t>(reason.size(),
frame::limits::close_reason_size));
ec = this->send_close_frame(code,tr,false,close::status::terminal(code));
}
@@ -385,13 +408,13 @@ bool connection<config>::get_secure() const {
}
template <typename config>
const std::string& connection<config>::get_host() const {
std::string const & connection<config>::get_host() const {
//scoped_lock_type lock(m_connection_state_lock);
return m_uri->get_host();
}
template <typename config>
const std::string& connection<config>::get_resource() const {
std::string const & connection<config>::get_resource() const {
//scoped_lock_type lock(m_connection_state_lock);
return m_uri->get_resource();
}
@@ -420,12 +443,12 @@ void connection<config>::set_uri(uri_ptr uri) {
template <typename config>
const std::string & connection<config>::get_subprotocol() const {
std::string const & connection<config>::get_subprotocol() const {
return m_subprotocol;
}
template <typename config>
const std::vector<std::string> &
std::vector<std::string> const &
connection<config>::get_requested_subprotocols() const {
return m_requested_subprotocols;
}
@@ -499,35 +522,36 @@ void connection<config>::select_subprotocol(std::string const & value) {
template <typename config>
const std::string &
connection<config>::get_request_header(std::string const & key) {
std::string const &
connection<config>::get_request_header(std::string const & key) const {
return m_request.get_header(key);
}
template <typename config>
const std::string &
connection<config>::get_response_header(std::string const & key) {
std::string const &
connection<config>::get_request_body() const {
return m_request.get_body();
}
template <typename config>
std::string const &
connection<config>::get_response_header(std::string const & key) const {
return m_response.get_header(key);
}
template <typename config>
void connection<config>::set_status(http::status_code::value code)
{
//scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
throw exception("Call to set_status from invalid state",
error::make_error_code(error::invalid_state));
}
m_response.set_status(code);
}
template <typename config>
void connection<config>::set_status(http::status_code::value code,
std::string const & msg)
{
//scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
throw exception("Call to set_status from invalid state",
error::make_error_code(error::invalid_state));
@@ -537,8 +561,6 @@ void connection<config>::set_status(http::status_code::value code,
}
template <typename config>
void connection<config>::set_body(std::string const & value) {
//scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
throw exception("Call to set_status from invalid state",
error::make_error_code(error::invalid_state));
@@ -551,8 +573,6 @@ template <typename config>
void connection<config>::append_header(std::string const & key,
std::string const & val)
{
//scoped_lock_type lock(m_connection_state_lock);
if (m_is_server) {
if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
// we are setting response headers for an incoming server connection
@@ -575,8 +595,6 @@ template <typename config>
void connection<config>::replace_header(std::string const & key,
std::string const & val)
{
// scoped_lock_type lock(m_connection_state_lock);
if (m_is_server) {
if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
// we are setting response headers for an incoming server connection
@@ -598,8 +616,6 @@ void connection<config>::replace_header(std::string const & key,
template <typename config>
void connection<config>::remove_header(std::string const & key)
{
//scoped_lock_type lock(m_connection_state_lock);
if (m_is_server) {
if (m_internal_state == istate::PROCESS_HTTP_REQUEST) {
// we are setting response headers for an incoming server connection
@@ -630,11 +646,13 @@ template <typename config>
void connection<config>::start() {
m_alog.write(log::alevel::devel,"connection start");
this->atomic_state_change(
istate::USER_INIT,
istate::TRANSPORT_INIT,
"Start must be called from user init state"
);
if (m_internal_state != istate::USER_INIT) {
m_alog.write(log::alevel::devel,"Start called in invalid state");
this->terminate(error::make_error_code(error::invalid_state));
return;
}
m_internal_state = istate::TRANSPORT_INIT;
// Depending on how the transport implements init this function may return
// immediately and call handle_transport_init later or call
@@ -652,39 +670,31 @@ template <typename config>
void connection<config>::handle_transport_init(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"connection handle_transport_init");
{
scoped_lock_type lock(m_connection_state_lock);
lib::error_code ecm = ec;
if (m_internal_state != istate::TRANSPORT_INIT) {
throw exception("handle_transport_init must be called from transport init state",
error::make_error_code(error::invalid_state));
}
if (!ec) {
// unless there was a transport error, advance internal state.
if (m_is_server) {
m_internal_state = istate::READ_HTTP_REQUEST;
} else {
m_internal_state = istate::WRITE_HTTP_REQUEST;
}
}
if (m_internal_state != istate::TRANSPORT_INIT) {
m_alog.write(log::alevel::devel,
"handle_transport_init must be called from transport init state");
ecm = error::make_error_code(error::invalid_state);
}
if (ec) {
if (ecm) {
std::stringstream s;
s << "handle_transport_init received error: "<< ec.message();
m_elog.write(log::elevel::fatal,s.str());
s << "handle_transport_init received error: "<< ecm.message();
m_elog.write(log::elevel::rerror,s.str());
this->terminate(ec);
this->terminate(ecm);
return;
}
// At this point the transport is ready to read and write bytes.
if (m_is_server) {
m_internal_state = istate::READ_HTTP_REQUEST;
this->read_handshake(1);
} else {
// We are a client. Set the processor to the version specified in the
// config file and send a handshake request.
m_internal_state = istate::WRITE_HTTP_REQUEST;
m_processor = get_processor(config::client_version);
this->send_http_request();
}
@@ -726,24 +736,37 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
{
m_alog.write(log::alevel::devel,"connection handle_read_handshake");
this->atomic_state_check(
istate::READ_HTTP_REQUEST,
"handle_read_handshake must be called from READ_HTTP_REQUEST state"
);
lib::error_code ecm = ec;
if (ec) {
if (ec == transport::error::eof) {
// we expect to get eof if the connection is closed already
if (m_state == session::state::closed) {
m_alog.write(log::alevel::devel,"got eof from closed con");
return;
}
if (!ecm) {
scoped_lock_type lock(m_connection_state_lock);
if (m_state == session::state::connecting) {
if (m_internal_state != istate::READ_HTTP_REQUEST) {
ecm = error::make_error_code(error::invalid_state);
}
} else if (m_state == session::state::closed) {
// The connection was canceled while the response was being sent,
// usually by the handshake timer. This is basically expected
// (though hopefully rare) and there is nothing we can do so ignore.
m_alog.write(log::alevel::devel,
"handle_read_handshake invoked after connection was closed");
return;
} else {
ecm = error::make_error_code(error::invalid_state);
}
}
std::stringstream s;
s << "error in handle_read_handshake: "<< ec.message();
m_elog.write(log::elevel::fatal,s.str());
this->terminate(ec);
if (ecm) {
if (ecm == transport::error::eof && m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
m_alog.write(log::alevel::devel,
"got (expected) eof/state error from closed con");
return;
}
log_err(log::elevel::rerror,"handle_read_handshake",ecm);
this->terminate(ecm);
return;
}
@@ -761,13 +784,13 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
// All HTTP exceptions will result in this request failing and an error
// response being returned. No more bytes will be read in this con.
m_response.set_status(e.m_error_code,e.m_error_msg);
this->send_http_response_error();
this->send_http_response_error(error::make_error_code(error::http_parse_error));
return;
}
// More paranoid boundaries checking.
// TODO: Is this overkill?
if (bytes_processed > config::connection_read_buffer_size) {
if (bytes_processed > bytes_transferred) {
m_elog.write(log::elevel::fatal,"Fatal boundaries checking error.");
this->terminate(make_error_code(error::general));
return;
@@ -781,8 +804,9 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
}
if (m_request.ready()) {
if (!this->initialize_processor()) {
this->send_http_response_error();
lib::error_code processor_ec = this->initialize_processor();
if (processor_ec) {
this->send_http_response_error(processor_ec);
return;
}
@@ -799,7 +823,7 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
// TODO: need more bytes
m_alog.write(log::alevel::devel,"short key3 read");
m_response.set_status(http::status_code::internal_server_error);
this->send_http_response_error();
this->send_http_response_error(processor::error::make_error_code(processor::error::short_key3));
return;
}
}
@@ -818,15 +842,12 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf);
m_buf_cursor = bytes_transferred-bytes_processed;
this->atomic_state_change(
istate::READ_HTTP_REQUEST,
istate::PROCESS_HTTP_REQUEST,
"send_http_response must be called from READ_HTTP_REQUEST state"
);
m_internal_state = istate::PROCESS_HTTP_REQUEST;
// We have the complete request. Process it.
this->process_handshake_request();
this->send_http_response();
lib::error_code handshake_ec = this->process_handshake_request();
this->send_http_response(handshake_ec);
} else {
// read at least 1 more byte
transport_con_type::async_read_at_least(
@@ -849,13 +870,17 @@ void connection<config>::handle_read_handshake(lib::error_code const & ec,
// sure if the hybi00 key3 bytes need to be read). This method sets the correct
// state and calls send_http_response
template <typename config>
void connection<config>::send_http_response_error() {
this->atomic_state_change(
istate::READ_HTTP_REQUEST,
istate::PROCESS_HTTP_REQUEST,
"send_http_response must be called from READ_HTTP_REQUEST state"
);
this->send_http_response();
void connection<config>::send_http_response_error(lib::error_code const & ec) {
if (m_internal_state != istate::READ_HTTP_REQUEST) {
m_alog.write(log::alevel::devel,
"send_http_response_error called in invalid state");
this->terminate(error::make_error_code(error::invalid_state));
return;
}
m_internal_state = istate::PROCESS_HTTP_REQUEST;
this->send_http_response(ec);
}
// All exit paths for this function need to call send_http_response() or submit
@@ -866,15 +891,16 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
{
//m_alog.write(log::alevel::devel,"connection handle_read_frame");
this->atomic_state_check(
istate::PROCESS_CONNECTION,
"handle_read_frame must be called from PROCESS_CONNECTION state"
);
lib::error_code ecm = ec;
if (ec) {
log::level echannel = log::elevel::fatal;
if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) {
ecm = error::make_error_code(error::invalid_state);
}
if (ecm) {
log::level echannel = log::elevel::rerror;
if (ec == transport::error::eof) {
if (ecm == transport::error::eof) {
if (m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
// just ignore it
@@ -887,8 +913,17 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
terminate(lib::error_code());
return;
}
}
if (ec == transport::error::tls_short_read) {
} else if (ecm == error::invalid_state) {
// In general, invalid state errors in the closed state are the
// result of handlers that were in the system already when the state
// changed and should be ignored as they pose no problems and there
// is nothing useful that we can do about them.
if (m_state == session::state::closed) {
m_alog.write(log::alevel::devel,
"handle_read_frame: got invalid istate in closed state");
return;
}
} else if (ecm == transport::error::tls_short_read) {
if (m_state == session::state::closed) {
// We expect to get a TLS short read if we try to read after the
// connection is closed. If this happens ignore and exit the
@@ -897,12 +932,12 @@ void connection<config>::handle_read_frame(lib::error_code const & ec,
return;
}
echannel = log::elevel::rerror;
} else if (ec == transport::error::action_after_shutdown) {
} else if (ecm == transport::error::action_after_shutdown) {
echannel = log::elevel::info;
}
log_err(echannel, "handle_read_frame", ec);
this->terminate(ec);
log_err(echannel, "handle_read_frame", ecm);
this->terminate(ecm);
return;
}
@@ -1014,12 +1049,12 @@ void connection<config>::read_frame() {
}
template <typename config>
bool connection<config>::initialize_processor() {
lib::error_code connection<config>::initialize_processor() {
m_alog.write(log::alevel::devel,"initialize_processor");
// if it isn't a websocket handshake nothing to do.
if (!processor::is_websocket_handshake(m_request)) {
return true;
return lib::error_code();
}
int version = processor::get_websocket_version(m_request);
@@ -1027,14 +1062,14 @@ bool connection<config>::initialize_processor() {
if (version < 0) {
m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version");
m_response.set_status(http::status_code::bad_request);
return false;
return error::make_error_code(error::invalid_version);
}
m_processor = get_processor(version);
// if the processor is not null we are done
if (m_processor) {
return true;
return lib::error_code();
}
// We don't have a processor for this version. Return bad request
@@ -1052,11 +1087,11 @@ bool connection<config>::initialize_processor() {
}
m_response.replace_header("Sec-WebSocket-Version",ss.str());
return false;
return error::make_error_code(error::unsupported_version);
}
template <typename config>
bool connection<config>::process_handshake_request() {
lib::error_code connection<config>::process_handshake_request() {
m_alog.write(log::alevel::devel,"process handshake request");
if (!processor::is_websocket_handshake(m_request)) {
@@ -1072,16 +1107,21 @@ bool connection<config>::process_handshake_request() {
if (!m_uri->get_valid()) {
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
m_response.set_status(http::status_code::bad_request);
return false;
return error::make_error_code(error::invalid_uri);
}
if (m_http_handler) {
m_is_http = true;
m_http_handler(m_connection_hdl);
if (m_state == session::state::closed) {
return error::make_error_code(error::http_connection_ended);
}
} else {
set_status(http::status_code::upgrade_required);
return error::make_error_code(error::upgrade_required);
}
return true;
return lib::error_code();
}
lib::error_code ec = m_processor->validate_handshake(m_request);
@@ -1091,7 +1131,7 @@ bool connection<config>::process_handshake_request() {
// Not a valid handshake request
m_alog.write(log::alevel::devel, "Bad request " + ec.message());
m_response.set_status(http::status_code::bad_request);
return false;
return ec;
}
// Read extension parameters and set up values necessary for the end user
@@ -1104,7 +1144,7 @@ bool connection<config>::process_handshake_request() {
// a failed connection attempt.
m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
m_response.set_status(http::status_code::bad_request);
return false;
return neg_results.first;
} else {
// extension negotiation succeeded, set response header accordingly
// we don't send an empty extensions header because it breaks many
@@ -1122,7 +1162,7 @@ bool connection<config>::process_handshake_request() {
if (!m_uri->get_valid()) {
m_alog.write(log::alevel::devel, "Bad request: failed to parse uri");
m_response.set_status(http::status_code::bad_request);
return false;
return error::make_error_code(error::invalid_uri);
}
// extract subprotocols
@@ -1147,7 +1187,7 @@ bool connection<config>::process_handshake_request() {
m_alog.write(log::alevel::devel, s.str());
m_response.set_status(http::status_code::internal_server_error);
return false;
return ec;
}
} else {
// User application has rejected the handshake
@@ -1159,19 +1199,27 @@ bool connection<config>::process_handshake_request() {
if (m_response.get_status_code() == http::status_code::uninitialized) {
m_response.set_status(http::status_code::bad_request);
}
return false;
return error::make_error_code(error::rejected);
}
return true;
return lib::error_code();
}
template <typename config>
void connection<config>::send_http_response() {
void connection<config>::send_http_response(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"connection send_http_response");
if (ec == error::make_error_code(error::http_connection_ended)) {
m_alog.write(log::alevel::http,"An HTTP handler took over the connection.");
return;
}
if (m_response.get_status_code() == http::status_code::uninitialized) {
m_response.set_status(http::status_code::internal_server_error);
m_ec = error::make_error_code(error::general);
} else {
m_ec = ec;
}
m_response.set_version("HTTP/1.1");
@@ -1217,18 +1265,39 @@ template <typename config>
void connection<config>::handle_send_http_response(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"handle_send_http_response");
this->atomic_state_check(
istate::PROCESS_HTTP_REQUEST,
"handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
);
lib::error_code ecm = ec;
if (ec) {
log_err(log::elevel::rerror,"handle_send_http_response",ec);
this->terminate(ec);
return;
if (!ecm) {
scoped_lock_type lock(m_connection_state_lock);
if (m_state == session::state::connecting) {
if (m_internal_state != istate::PROCESS_HTTP_REQUEST) {
ecm = error::make_error_code(error::invalid_state);
}
} else if (m_state == session::state::closed) {
// The connection was canceled while the response was being sent,
// usually by the handshake timer. This is basically expected
// (though hopefully rare) and there is nothing we can do so ignore.
m_alog.write(log::alevel::devel,
"handle_send_http_response invoked after connection was closed");
return;
} else {
ecm = error::make_error_code(error::invalid_state);
}
}
this->log_open_result();
if (ecm) {
if (ecm == transport::error::eof && m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
m_alog.write(log::alevel::devel,
"got (expected) eof/state error from closed con");
return;
}
log_err(log::elevel::rerror,"handle_send_http_response",ecm);
this->terminate(ecm);
return;
}
if (m_handshake_timer) {
m_handshake_timer->cancel();
@@ -1237,8 +1306,11 @@ void connection<config>::handle_send_http_response(lib::error_code const & ec) {
if (m_response.get_status_code() != http::status_code::switching_protocols)
{
if (m_processor) {
// this was a websocket connection that ended in an error
/*if (m_processor || m_ec == error::http_parse_error ||
m_ec == error::invalid_version || m_ec == error::unsupported_version
|| m_ec == error::upgrade_required)
{*/
if (!m_is_http) {
std::stringstream s;
s << "Handshake ended with HTTP error: "
<< m_response.get_status_code();
@@ -1246,18 +1318,24 @@ void connection<config>::handle_send_http_response(lib::error_code const & ec) {
} else {
// if this was not a websocket connection, we have written
// the expected response and the connection can be closed.
}
this->terminate(make_error_code(error::http_connection_ended));
this->log_http_result();
if (m_ec) {
m_alog.write(log::alevel::devel,
"got to writing HTTP results with m_ec set: "+m_ec.message());
}
m_ec = make_error_code(error::http_connection_ended);
}
this->terminate(m_ec);
return;
}
this->atomic_state_change(
istate::PROCESS_HTTP_REQUEST,
istate::PROCESS_CONNECTION,
session::state::connecting,
session::state::open,
"handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
);
this->log_open_result();
m_internal_state = istate::PROCESS_CONNECTION;
m_state = session::state::open;
if (m_open_handler) {
m_open_handler(m_connection_hdl);
@@ -1329,22 +1407,41 @@ template <typename config>
void connection<config>::handle_send_http_request(lib::error_code const & ec) {
m_alog.write(log::alevel::devel,"handle_send_http_request");
this->atomic_state_check(
istate::WRITE_HTTP_REQUEST,
"handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
);
lib::error_code ecm = ec;
if (ec) {
log_err(log::elevel::rerror,"handle_send_http_request",ec);
this->terminate(ec);
return;
if (!ecm) {
scoped_lock_type lock(m_connection_state_lock);
if (m_state == session::state::connecting) {
if (m_internal_state != istate::WRITE_HTTP_REQUEST) {
ecm = error::make_error_code(error::invalid_state);
} else {
m_internal_state = istate::READ_HTTP_RESPONSE;
}
} else if (m_state == session::state::closed) {
// The connection was canceled while the response was being sent,
// usually by the handshake timer. This is basically expected
// (though hopefully rare) and there is nothing we can do so ignore.
m_alog.write(log::alevel::devel,
"handle_send_http_request invoked after connection was closed");
return;
} else {
ecm = error::make_error_code(error::invalid_state);
}
}
this->atomic_state_change(
istate::WRITE_HTTP_REQUEST,
istate::READ_HTTP_RESPONSE,
"handle_send_http_request must be called from WRITE_HTTP_REQUEST state"
);
if (ecm) {
if (ecm == transport::error::eof && m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
m_alog.write(log::alevel::devel,
"got (expected) eof/state error from closed con");
return;
}
log_err(log::elevel::rerror,"handle_send_http_request",ecm);
this->terminate(ecm);
return;
}
transport_con_type::async_read_at_least(
1,
@@ -1365,16 +1462,40 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
{
m_alog.write(log::alevel::devel,"handle_read_http_response");
this->atomic_state_check(
istate::READ_HTTP_RESPONSE,
"handle_read_http_response must be called from READ_HTTP_RESPONSE state"
);
lib::error_code ecm = ec;
if (ec) {
log_err(log::elevel::rerror,"handle_send_http_response",ec);
this->terminate(ec);
if (!ecm) {
scoped_lock_type lock(m_connection_state_lock);
if (m_state == session::state::connecting) {
if (m_internal_state != istate::READ_HTTP_RESPONSE) {
ecm = error::make_error_code(error::invalid_state);
}
} else if (m_state == session::state::closed) {
// The connection was canceled while the response was being sent,
// usually by the handshake timer. This is basically expected
// (though hopefully rare) and there is nothing we can do so ignore.
m_alog.write(log::alevel::devel,
"handle_read_http_response invoked after connection was closed");
return;
} else {
ecm = error::make_error_code(error::invalid_state);
}
}
if (ecm) {
if (ecm == transport::error::eof && m_state == session::state::closed) {
// we expect to get eof if the connection is closed already
m_alog.write(log::alevel::devel,
"got (expected) eof/state error from closed con");
return;
}
log_err(log::elevel::rerror,"handle_read_http_response",ecm);
this->terminate(ecm);
return;
}
size_t bytes_processed = 0;
// TODO: refactor this to use error codes rather than exceptions
try {
@@ -1404,14 +1525,9 @@ void connection<config>::handle_read_http_response(lib::error_code const & ec,
return;
}
// response is valid, connection can now be assumed to be open
this->atomic_state_change(
istate::READ_HTTP_RESPONSE,
istate::PROCESS_CONNECTION,
session::state::connecting,
session::state::open,
"handle_read_http_response must be called from READ_HTTP_RESPONSE state"
);
// response is valid, connection can now be assumed to be open
m_internal_state = istate::PROCESS_CONNECTION;
m_state = session::state::open;
this->log_open_result();
@@ -1492,9 +1608,16 @@ void connection<config>::terminate(lib::error_code const & ec) {
m_local_close_reason = ec.message();
}
// TODO: does this need a mutex?
if (m_state == session::state::connecting) {
m_state = session::state::closed;
tstat = failed;
// Log fail result here before socket is shut down and we can't get
// the remote address, etc anymore
if (m_ec != error::http_connection_ended) {
log_fail_result();
}
} else if (m_state != session::state::closed) {
m_state = session::state::closed;
tstat = closed;
@@ -1531,10 +1654,11 @@ void connection<config>::handle_terminate(terminate_status tstat,
// clean shutdown
if (tstat == failed) {
if (m_fail_handler) {
m_fail_handler(m_connection_hdl);
if (m_ec != error::http_connection_ended) {
if (m_fail_handler) {
m_fail_handler(m_connection_hdl);
}
}
log_fail_result();
} else if (tstat == closed) {
if (m_close_handler) {
m_close_handler(m_connection_hdl);
@@ -1629,8 +1753,11 @@ void connection<config>::write_frame() {
if (m_alog.static_test(log::alevel::frame_payload)) {
if (m_alog.dynamic_test(log::alevel::frame_payload)) {
payload << "[" << i << "] ("
<< m_current_msgs[i]->get_payload().size() << ") "
<< utility::to_hex(m_current_msgs[i]->get_payload())
<< m_current_msgs[i]->get_payload().size() << ") ["<<m_current_msgs[i]->get_opcode()<<"] "
<< (m_current_msgs[i]->get_opcode() == frame::opcode::text ?
m_current_msgs[i]->get_payload() :
utility::to_hex(m_current_msgs[i]->get_payload())
)
<< "\n";
}
}
@@ -1693,45 +1820,7 @@ void connection<config>::handle_write_frame(lib::error_code const & ec)
}
template <typename config>
void connection<config>::atomic_state_change(istate_type req, istate_type dest,
std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != req) {
throw exception(msg,error::make_error_code(error::invalid_state));
}
m_internal_state = dest;
}
template <typename config>
void connection<config>::atomic_state_change(istate_type internal_req,
istate_type internal_dest, session::state::value external_req,
session::state::value external_dest, std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != internal_req || m_state != external_req) {
throw exception(msg,error::make_error_code(error::invalid_state));
}
m_internal_state = internal_dest;
m_state = external_dest;
}
template <typename config>
void connection<config>::atomic_state_check(istate_type req, std::string msg)
{
scoped_lock_type lock(m_connection_state_lock);
if (m_internal_state != req) {
throw exception(msg,error::make_error_code(error::invalid_state));
}
}
template <typename config>
const std::vector<int>& connection<config>::get_supported_versions() const
std::vector<int> const & connection<config>::get_supported_versions() const
{
return versions_supported;
}
@@ -2108,9 +2197,69 @@ void connection<config>::log_close_result()
template <typename config>
void connection<config>::log_fail_result()
{
// TODO: include more information about the connection?
// should this be filed under connect rather than disconnect?
m_alog.write(log::alevel::disconnect,"Failed: "+m_ec.message());
std::stringstream s;
int version = processor::get_websocket_version(m_request);
// Connection Type
s << "WebSocket Connection ";
// Remote endpoint address & WebSocket version
s << transport_con_type::get_remote_endpoint();
if (version < 0) {
s << " -";
} else {
s << " v" << version;
}
// User Agent
std::string ua = m_request.get_header("User-Agent");
if (ua == "") {
s << " \"\" ";
} else {
// check if there are any quotes in the user agent
s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
}
// URI
s << (m_uri ? m_uri->get_resource() : "-");
// HTTP Status code
s << " " << m_response.get_status_code();
// WebSocket++ error code & reason
s << " " << m_ec << " " << m_ec.message();
m_alog.write(log::alevel::fail,s.str());
}
template <typename config>
void connection<config>::log_http_result() {
std::stringstream s;
if (processor::is_websocket_handshake(m_request)) {
m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket");
return;
}
// Connection Type
s << (m_request.get_header("host") == "" ? "-" : m_request.get_header("host"))
<< " " << transport_con_type::get_remote_endpoint()
<< " \"" << m_request.get_method()
<< " " << (m_uri ? m_uri->get_resource() : "-")
<< " " << m_request.get_version() << "\" " << m_response.get_status_code()
<< " " << m_response.get_body().size();
// User Agent
std::string ua = m_request.get_header("User-Agent");
if (ua == "") {
s << " \"\" ";
} else {
// check if there are any quotes in the user agent
s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" ";
}
m_alog.write(log::alevel::http,s.str());
}
} // namespace websocketpp
@@ -28,6 +28,8 @@
#ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP
#define WEBSOCKETPP_ENDPOINT_IMPL_HPP
#include <string>
namespace websocketpp {
template <typename connection, typename config>
@@ -77,6 +79,7 @@ endpoint<connection,config>::create_connection() {
if (m_max_message_size != config::max_message_size) {
con->set_max_message_size(m_max_message_size);
}
con->set_max_http_body_size(m_max_http_body_size);
lib::error_code ec;
@@ -28,6 +28,9 @@
#ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP
#define WEBSOCKETPP_UTILITIES_IMPL_HPP
#include <algorithm>
#include <string>
namespace websocketpp {
namespace utility {
@@ -37,7 +40,7 @@ inline std::string to_lower(std::string const & in) {
return out;
}
inline std::string to_hex(const std::string& input) {
inline std::string to_hex(std::string const & input) {
std::string output;
std::string hex = "0123456789ABCDEF";
@@ -50,7 +53,7 @@ inline std::string to_hex(const std::string& input) {
return output;
}
inline std::string to_hex(const uint8_t* input,size_t length) {
inline std::string to_hex(uint8_t const * input, size_t length) {
std::string output;
std::string hex = "0123456789ABCDEF";
@@ -67,8 +70,8 @@ inline std::string to_hex(const char* input,size_t length) {
return to_hex(reinterpret_cast<const uint8_t*>(input),length);
}
inline std::string string_replace_all(std::string subject, const std::string&
search, const std::string& replace)
inline std::string string_replace_all(std::string subject, std::string const &
search, std::string const & replace)
{
size_t pos = 0;
while((pos = subject.find(search, pos)) != std::string::npos) {
+24 -11
View File
@@ -40,14 +40,16 @@
*
*/
#include <ctime>
#include <iostream>
#include <iomanip>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/stdint.hpp>
#include <websocketpp/common/time.hpp>
#include <websocketpp/logger/levels.hpp>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <string>
namespace websocketpp {
namespace log {
@@ -56,23 +58,23 @@ namespace log {
template <typename concurrency, typename names>
class basic {
public:
basic<concurrency,names>(channel_type_hint::value h =
basic<concurrency,names>(channel_type_hint::value h =
channel_type_hint::access)
: m_static_channels(0xffffffff)
, m_dynamic_channels(0)
, m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
basic<concurrency,names>(std::ostream * out)
: m_static_channels(0xffffffff)
, m_dynamic_channels(0)
, m_out(out) {}
basic<concurrency,names>(level c, channel_type_hint::value h =
basic<concurrency,names>(level c, channel_type_hint::value h =
channel_type_hint::access)
: m_static_channels(c)
, m_dynamic_channels(0)
, m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
basic<concurrency,names>(level c, std::ostream * out)
: m_static_channels(c)
, m_dynamic_channels(0)
@@ -97,6 +99,11 @@ public:
m_dynamic_channels &= ~channels;
}
/// Write a string message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, std::string const & msg) {
scoped_lock_type lock(m_lock);
if (!this->dynamic_test(channel)) { return; }
@@ -106,6 +113,11 @@ public:
m_out->flush();
}
/// Write a cstring message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(m_lock);
if (!this->dynamic_test(channel)) { return; }
@@ -122,10 +134,13 @@ public:
bool dynamic_test(level channel) {
return ((channel & m_dynamic_channels) != 0);
}
private:
protected:
typedef typename concurrency::scoped_lock_type scoped_lock_type;
typedef typename concurrency::mutex_type mutex_type;
mutex_type m_lock;
private:
// The timestamp does not include the time zone, because on Windows with the
// default registry settings, the time zone would be written out in full,
// which would be obnoxiously verbose.
@@ -143,8 +158,6 @@ private:
#endif
}
mutex_type m_lock;
level const m_static_channels;
level m_dynamic_channels;
std::ostream * m_out;
@@ -141,6 +141,13 @@ struct alevel {
static level const devel = 0x400;
/// Special channel for application specific logs. Not used by the library.
static level const app = 0x800;
/// Access related to HTTP requests
static level const http = 0x1000;
/// One line for each failed WebSocket connection with details
static level const fail = 0x2000;
/// Aggregate package representing the commonly used core access channels
/// Connect, Disconnect, Fail, and HTTP
static level const access_core = 0x00003003;
/// Special aggregate value representing "all levels"
static level const all = 0xffffffff;
@@ -180,6 +187,10 @@ struct alevel {
return "devel";
case app:
return "application";
case http:
return "http";
case fail:
return "fail";
default:
return "unknown";
}
+13 -12
View File
@@ -28,10 +28,11 @@
#ifndef WEBSOCKETPP_LOGGER_STUB_HPP
#define WEBSOCKETPP_LOGGER_STUB_HPP
#include <string>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
#include <string>
namespace websocketpp {
namespace log {
@@ -44,7 +45,7 @@ public:
* @param hint A channel type specific hint for how to construct the logger
*/
explicit stub(channel_type_hint::value) {}
/// Construct the logger
/**
* @param default_channels A set of channels to statically enable
@@ -55,16 +56,16 @@ public:
/// Dynamically enable the given list of channels
/**
* All operations on the stub logger are no-ops and all arguments are
* All operations on the stub logger are no-ops and all arguments are
* ignored
*
* @param channels The package of channels to enable
*/
void set_channels(level) {}
/// Dynamically disable the given list of channels
/**
* All operations on the stub logger are no-ops and all arguments are
* All operations on the stub logger are no-ops and all arguments are
* ignored
*
* @param channels The package of channels to disable
@@ -75,23 +76,23 @@ public:
/**
* Writing on the stub logger is a no-op and all arguments are ignored
*
* @param channel The package of channels to write to
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level, std::string const &) {}
/// Write a cstring message to the given channel
/**
* Writing on the stub logger is a no-op and all arguments are ignored
*
* @param channel The package of channels to write to
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level, char const *) {}
/// Test whether a channel is statically enabled
/**
* The stub logger has no channels so all arguments are ignored and
* The stub logger has no channels so all arguments are ignored and
* `static_test` always returns false.
*
* @param channel The package of channels to test
@@ -99,10 +100,10 @@ public:
_WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level) const {
return false;
}
/// Test whether a channel is dynamically enabled
/**
* The stub logger has no channels so all arguments are ignored and
* The stub logger has no channels so all arguments are ignored and
* `dynamic_test` always returns false.
*
* @param channel The package of channels to test
@@ -0,0 +1,146 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* The initial version of this logging policy was contributed to the WebSocket++
* project by Tom Hughes.
*/
#ifndef WEBSOCKETPP_LOGGER_SYSLOG_HPP
#define WEBSOCKETPP_LOGGER_SYSLOG_HPP
#include <syslog.h>
#include <websocketpp/logger/basic.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
namespace websocketpp {
namespace log {
/// Basic logger that outputs to syslog
template <typename concurrency, typename names>
class syslog : public basic<concurrency, names> {
public:
typedef basic<concurrency, names> base;
/// Construct the logger
/**
* @param hint A channel type specific hint for how to construct the logger
*/
syslog<concurrency,names>(channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(hint), m_channel_type_hint(hint) {}
/// Construct the logger
/**
* @param channels A set of channels to statically enable
* @param hint A channel type specific hint for how to construct the logger
*/
syslog<concurrency,names>(level channels, channel_type_hint::value hint =
channel_type_hint::access)
: basic<concurrency,names>(channels, hint), m_channel_type_hint(hint) {}
/// Write a string message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, std::string const & msg) {
write(channel, msg.c_str());
}
/// Write a cstring message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(base::m_lock);
if (!this->dynamic_test(channel)) { return; }
::syslog(syslog_priority(channel), "[%s] %s",
names::channel_name(channel), msg);
}
private:
typedef typename base::scoped_lock_type scoped_lock_type;
/// The default level is used for all access logs and any error logs that
/// don't trivially map to one of the standard syslog levels.
static int const default_level = LOG_INFO;
/// retrieve the syslog priority code given a WebSocket++ channel
/**
* @param channel The level to look up
* @return The syslog level associated with `channel`
*/
int syslog_priority(level channel) const {
if (m_channel_type_hint == channel_type_hint::access) {
return syslog_priority_access(channel);
} else {
return syslog_priority_error(channel);
}
}
/// retrieve the syslog priority code given a WebSocket++ error channel
/**
* @param channel The level to look up
* @return The syslog level associated with `channel`
*/
int syslog_priority_error(level channel) const {
switch (channel) {
case elevel::devel:
return LOG_DEBUG;
case elevel::library:
return LOG_DEBUG;
case elevel::info:
return LOG_INFO;
case elevel::warn:
return LOG_WARNING;
case elevel::rerror:
return LOG_ERR;
case elevel::fatal:
return LOG_CRIT;
default:
return default_level;
}
}
/// retrieve the syslog priority code given a WebSocket++ access channel
/**
* @param channel The level to look up
* @return The syslog level associated with `channel`
*/
_WEBSOCKETPP_CONSTEXPR_TOKEN_ int syslog_priority_access(level) const {
return default_level;
}
channel_type_hint::value m_channel_type_hint;
};
} // log
} // websocketpp
#endif // WEBSOCKETPP_LOGGER_SYSLOG_HPP
@@ -43,7 +43,7 @@ namespace message_buffer {
*
* # connection_message_manager:
* An object that manages all of the message_buffers associated with a given
* connection. Impliments the get_message_buffer(size) method that returns
* connection. Implements the get_message_buffer(size) method that returns
* a message buffer at least size bytes long.
*
* Message buffers are reference counted with shared ownership semantics. Once
@@ -54,7 +54,7 @@ namespace message_buffer {
* in the manager.
*
* # endpoint_message_manager:
* An object that manages connection_message_managers. Impliments the
* An object that manages connection_message_managers. Implements the
* get_message_manager() method. This is used once by each connection to
* request the message manager that they are supposed to use to manage message
* buffers for their own use.
@@ -42,7 +42,7 @@ namespace message_buffer {
*
* # connection_message_manager:
* An object that manages all of the message_buffers associated with a given
* connection. Impliments the get_message_buffer(size) method that returns
* connection. Implements the get_message_buffer(size) method that returns
* a message buffer at least size bytes long.
*
* Message buffers are reference counted with shared ownership semantics. Once
@@ -53,7 +53,7 @@ namespace message_buffer {
* in the manager.
*
* # endpoint_message_manager:
* An object that manages connection_message_managers. Impliments the
* An object that manages connection_message_managers. Implements the
* get_message_manager() method. This is used once by each connection to
* request the message manager that they are supposed to use to manage message
* buffers for their own use.
+10 -5
View File
@@ -28,14 +28,13 @@
#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP
#define WEBSOCKETPP_PROCESSOR_BASE_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/close.hpp>
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <map>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
@@ -151,7 +150,11 @@ enum processor_errors {
extension_parse_error,
/// Extension related operation was ignored because extensions are disabled
extensions_disabled
extensions_disabled,
/// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes
/// after the handshake. Less than 8 bytes were read.
short_key3
};
/// Category for processor errors
@@ -223,6 +226,8 @@ public:
return "Error parsing extension header";
case error::extensions_disabled:
return "Extensions are disabled";
case error::short_key3:
return "Short Hybi00 Key 3 read";
default:
return "Unknown";
}
@@ -28,8 +28,6 @@
#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#include <cstdlib>
#include <websocketpp/frame.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/common/network.hpp>
@@ -38,6 +36,11 @@
#include <websocketpp/processors/processor.hpp>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
@@ -108,7 +111,7 @@ public:
// 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 = req.get_header("Sec-WebSocket-Key3");
std::string const & key3 = req.get_header("Sec-WebSocket-Key3");
std::copy(key3.c_str(),
key3.c_str()+(std::min)(static_cast<size_t>(8), key3.size()),
&key_final[8]);
@@ -364,7 +367,7 @@ public:
* @param out The message buffer to prepare the pong in.
* @return Status code, zero on success, non-zero on failure
*/
lib::error_code prepare_pong(const std::string &, message_ptr) const
lib::error_code prepare_pong(std::string const &, message_ptr) const
{
return lib::error_code(error::no_protocol_support);
}
@@ -30,6 +30,9 @@
#include <websocketpp/processors/hybi08.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
@@ -30,6 +30,9 @@
#include <websocketpp/processors/hybi13.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
@@ -68,7 +71,7 @@ public:
return 8;
}
const std::string& get_origin(request_type const & r) const {
std::string const & get_origin(request_type const & r) const {
return r.get_header("Sec-WebSocket-Origin");
}
private:
+12 -10
View File
@@ -28,19 +28,20 @@
#ifndef WEBSOCKETPP_PROCESSOR_HYBI13_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP
#include <cassert>
#include <websocketpp/frame.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/frame.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/sha1/sha1.hpp>
#include <websocketpp/base64/base64.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/common/platforms.hpp>
#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
#include <utility>
@@ -160,8 +161,8 @@ public:
* generic struct if other user input parameters to the processed handshake
* are found.
*/
lib::error_code process_handshake(request_type const & request, const
std::string & subprotocol, response_type& response) const
lib::error_code process_handshake(request_type const & request,
std::string const & subprotocol, response_type & response) const
{
std::string server_key = request.get_header("Sec-WebSocket-Key");
@@ -588,6 +589,7 @@ public:
}
out->set_prepared(true);
out->set_opcode(op);
return lib::error_code();
}
@@ -35,8 +35,10 @@
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace websocketpp {
/// Processors encapsulate the protocol rules specific to each WebSocket version
@@ -103,6 +105,10 @@ bool is_websocket_handshake(request_type& r) {
*/
template <typename request_type>
int get_websocket_version(request_type& r) {
if (!r.ready()) {
return -2;
}
if (r.get_header("Sec-WebSocket-Version") == "") {
return 0;
}
@@ -29,13 +29,15 @@
#define WEBSOCKETPP_CLIENT_ENDPOINT_HPP
#include <websocketpp/endpoint.hpp>
#include <websocketpp/logger/levels.hpp>
#include <iostream>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
/// Client endpoint role based on the given config
/**
*
@@ -29,13 +29,13 @@
#define WEBSOCKETPP_SERVER_ENDPOINT_HPP
#include <websocketpp/endpoint.hpp>
#include <websocketpp/logger/levels.hpp>
#include <iostream>
#include <websocketpp/common/system_error.hpp>
namespace websocketpp {
/// Server endpoint role based on the given config
/**
*
@@ -28,22 +28,27 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/transport/asio/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/transport/asio/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/base64/base64.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <istream>
#include <sstream>
#include <string>
#include <vector>
namespace websocketpp {
@@ -243,7 +248,7 @@ public:
if (ec) { throw exception(ec); }
}
const std::string & get_proxy() const {
std::string const & get_proxy() const {
return m_proxy;
}
@@ -899,7 +904,7 @@ protected:
);
}
void async_write(const std::vector<buffer>& bufs, write_handler handler) {
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
if (!m_async_write_handler) {
m_alog.write(log::alevel::devel,
"async_write (vector) called after async_shutdown");
@@ -28,16 +28,22 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_HPP
#include <websocketpp/common/functional.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/endpoint.hpp>
#include <websocketpp/transport/asio/connection.hpp>
#include <websocketpp/transport/asio/security/none.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/functional.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/system/error_code.hpp>
#include <sstream>
#include <string>
namespace websocketpp {
namespace transport {
namespace asio {
@@ -179,7 +185,7 @@ public:
m_external_io_service = true;
m_acceptor = lib::make_shared<boost::asio::ip::tcp::acceptor>(
lib::ref(*m_io_service));
m_state = READY;
ec = lib::error_code();
}
@@ -289,10 +295,10 @@ public:
void set_listen_backlog(int backlog) {
m_listen_backlog = backlog;
}
/// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
/**
* Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
* Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
* this flag does depends on your operating system. Please consult operating
* system documentation for more details.
*
@@ -356,6 +362,9 @@ public:
m_acceptor->listen(m_listen_backlog,bec);
}
if (bec) {
if (m_acceptor->is_open()) {
m_acceptor->close();
}
log_err(log::elevel::info,"asio listen",bec);
ec = make_error_code(error::pass_through);
} else {
@@ -97,14 +97,14 @@ namespace error {
missing_tls_init_handler,
/// TLS Handshake Failed
tls_handshake_failed,
tls_handshake_failed
};
} // namespace error
/// Error category related to asio transport socket policies
class socket_category : public lib::error_category {
public:
const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
return "websocketpp.transport.asio.socket";
}
@@ -132,7 +132,7 @@ public:
}
};
inline const lib::error_category& get_socket_category() {
inline lib::error_category const & get_socket_category() {
static socket_category instance;
return instance;
}
@@ -28,11 +28,13 @@
#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/transport/asio/security/base.hpp>
#include <websocketpp/common/memory.hpp>
#include <boost/asio.hpp>
#include <sstream>
#include <string>
namespace websocketpp {
@@ -29,6 +29,7 @@
#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
#include <websocketpp/transport/asio/security/base.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/memory.hpp>
@@ -37,6 +38,7 @@
#include <boost/asio/ssl.hpp>
#include <boost/system/error_code.hpp>
#include <sstream>
#include <string>
namespace websocketpp {
@@ -31,9 +31,10 @@
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
/// Transport policies provide network connectivity and timers
/**
@@ -176,7 +177,7 @@ enum value {
action_after_shutdown,
/// Other TLS error
tls_error,
tls_error
};
class category : public lib::error_category {
@@ -28,13 +28,8 @@
#ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_BASE_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/uri.hpp>
#include <iostream>
namespace websocketpp {
/// Transport policies provide network connectivity and timers
@@ -0,0 +1,104 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <string>
namespace websocketpp {
namespace transport {
/// Debug transport policy that is used for various mocking and stubbing duties
/// in unit tests.
namespace debug {
/// debug transport errors
namespace error {
enum value {
/// Catch-all error for transport policy errors that don't fit in other
/// categories
general = 1,
/// not implemented
not_implemented,
invalid_num_bytes,
double_read
};
/// debug transport error category
class category : public lib::error_category {
public:
category() {}
char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
return "websocketpp.transport.debug";
}
std::string message(int value) const {
switch(value) {
case general:
return "Generic stub transport policy error";
case not_implemented:
return "feature not implemented";
case invalid_num_bytes:
return "Invalid number of bytes";
case double_read:
return "Read while another read was outstanding";
default:
return "Unknown";
}
}
};
/// Get a reference to a static copy of the debug transport error category
inline lib::error_category const & get_category() {
static category instance;
return instance;
}
/// Get an error code with the given value and the debug transport category
inline lib::error_code make_error_code(error::value e) {
return lib::error_code(static_cast<int>(e), get_category());
}
} // namespace error
} // namespace debug
} // namespace transport
} // namespace websocketpp
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
template<> struct is_error_code_enum<websocketpp::transport::debug::error::value>
{
static bool const value = true;
};
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
#endif // WEBSOCKETPP_TRANSPORT_DEBUG_BASE_HPP
@@ -0,0 +1,397 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
#define WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
#include <websocketpp/transport/debug/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace transport {
namespace debug {
/// Empty timer class to stub out for timer functionality that stub
/// transport doesn't support
struct timer {
void cancel() {}
};
template <typename config>
class connection : public lib::enable_shared_from_this< connection<config> > {
public:
/// Type of this connection transport component
typedef connection<config> type;
/// Type of a shared pointer to this connection transport component
typedef lib::shared_ptr<type> ptr;
/// transport concurrency policy
typedef typename config::concurrency_type concurrency_type;
/// Type of this transport's access logging policy
typedef typename config::alog_type alog_type;
/// Type of this transport's error logging policy
typedef typename config::elog_type elog_type;
// Concurrency policy types
typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
typedef typename concurrency_type::mutex_type mutex_type;
typedef lib::shared_ptr<timer> timer_ptr;
explicit connection(bool is_server, alog_type & alog, elog_type & elog)
: m_reading(false), m_is_server(is_server), m_alog(alog), m_elog(elog)
{
m_alog.write(log::alevel::devel,"debug con transport constructor");
}
/// Get a shared pointer to this component
ptr get_shared() {
return type::shared_from_this();
}
/// Set whether or not this connection is secure
/**
* Todo: docs
*
* @since 0.3.0-alpha4
*
* @param value Whether or not this connection is secure.
*/
void set_secure(bool) {}
/// Tests whether or not the underlying transport is secure
/**
* TODO: docs
*
* @return Whether or not the underlying transport is secure
*/
bool is_secure() const {
return false;
}
/// Set human readable remote endpoint address
/**
* Sets the remote endpoint address returned by `get_remote_endpoint`. This
* value should be a human readable string that describes the remote
* endpoint. Typically an IP address or hostname, perhaps with a port. But
* may be something else depending on the nature of the underlying
* transport.
*
* If none is set a default is returned.
*
* @since 0.3.0-alpha4
*
* @param value The remote endpoint address to set.
*/
void set_remote_endpoint(std::string) {}
/// Get human readable remote endpoint address
/**
* TODO: docs
*
* This value is used in access and error logs and is available to the end
* application for including in user facing interfaces and messages.
*
* @return A string identifying the address of the remote endpoint
*/
std::string get_remote_endpoint() const {
return "unknown (debug transport)";
}
/// Get the connection handle
/**
* @return The handle for this connection.
*/
connection_hdl get_handle() const {
return connection_hdl();
}
/// Call back a function after a period of time.
/**
* Timers are not implemented in this transport. The timer pointer will
* always be empty. The handler will never be called.
*
* @param duration Length of time to wait in milliseconds
* @param callback The function to call back when the timer has expired
* @return A handle that can be used to cancel the timer if it is no longer
* needed.
*/
timer_ptr set_timer(long, timer_handler handler) {
m_alog.write(log::alevel::devel,"debug connection set timer");
m_timer_handler = handler;
return timer_ptr();
}
/// Manual input supply (read all)
/**
* Similar to read_some, but continues to read until all bytes in the
* supplied buffer have been read or the connection runs out of read
* requests.
*
* This method still may not read all of the bytes in the input buffer. if
* it doesn't it indicates that the connection was most likely closed or
* is in an error state where it is no longer accepting new input.
*
* @since 0.3.0
*
* @param buf Char buffer to read into the websocket
* @param len Length of buf
* @return The number of characters from buf actually read.
*/
size_t read_all(char const * buf, size_t len) {
size_t total_read = 0;
size_t temp_read = 0;
do {
temp_read = this->read_some_impl(buf+total_read,len-total_read);
total_read += temp_read;
} while (temp_read != 0 && total_read < len);
return total_read;
}
// debug stuff to invoke the async handlers
void expire_timer(lib::error_code const & ec) {
m_timer_handler(ec);
}
void fullfil_write() {
m_write_handler(lib::error_code());
}
protected:
/// Initialize the connection transport
/**
* Initialize the connection's transport component.
*
* @param handler The `init_handler` to call when initialization is done
*/
void init(init_handler handler) {
m_alog.write(log::alevel::devel,"debug connection init");
handler(lib::error_code());
}
/// Initiate an async_read for at least num_bytes bytes into buf
/**
* Initiates an async_read request for at least num_bytes bytes. The input
* will be read into buf. A maximum of len bytes will be input. When the
* operation is complete, handler will be called with the status and number
* of bytes read.
*
* This method may or may not call handler from within the initial call. The
* application should be prepared to accept either.
*
* The application should never call this method a second time before it has
* been called back for the first read. If this is done, the second read
* will be called back immediately with a double_read error.
*
* If num_bytes or len are zero handler will be called back immediately
* indicating success.
*
* @param num_bytes Don't call handler until at least this many bytes have
* been read.
* @param buf The buffer to read bytes into
* @param len The size of buf. At maximum, this many bytes will be read.
* @param handler The callback to invoke when the operation is complete or
* ends in an error
*/
void async_read_at_least(size_t num_bytes, char * buf, size_t len,
read_handler handler)
{
std::stringstream s;
s << "debug_con async_read_at_least: " << num_bytes;
m_alog.write(log::alevel::devel,s.str());
if (num_bytes > len) {
handler(make_error_code(error::invalid_num_bytes),size_t(0));
return;
}
if (m_reading == true) {
handler(make_error_code(error::double_read),size_t(0));
return;
}
if (num_bytes == 0 || len == 0) {
handler(lib::error_code(),size_t(0));
return;
}
m_buf = buf;
m_len = len;
m_bytes_needed = num_bytes;
m_read_handler = handler;
m_cursor = 0;
m_reading = true;
}
/// Asyncronous Transport Write
/**
* Write len bytes in buf to the output stream. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
* Will return 0 on success.
*
* @param buf buffer to read bytes from
* @param len number of bytes to write
* @param handler Callback to invoke with operation status.
*/
void async_write(char const *, size_t, write_handler handler) {
m_alog.write(log::alevel::devel,"debug_con async_write");
m_write_handler = handler;
}
/// Asyncronous Transport Write (scatter-gather)
/**
* Write a sequence of buffers to the output stream. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
* Will return 0 on success.
*
* @param bufs vector of buffers to write
* @param handler Callback to invoke with operation status.
*/
void async_write(std::vector<buffer> const &, write_handler handler) {
m_alog.write(log::alevel::devel,"debug_con async_write buffer list");
m_write_handler = handler;
}
/// Set Connection Handle
/**
* @param hdl The new handle
*/
void set_handle(connection_hdl) {}
/// Call given handler back within the transport's event system (if present)
/**
* Invoke a callback within the transport's event system if it has one. If
* it doesn't, the handler will be invoked immediately before this function
* returns.
*
* @param handler The callback to invoke
*
* @return Whether or not the transport was able to register the handler for
* callback.
*/
lib::error_code dispatch(dispatch_handler handler) {
handler();
return lib::error_code();
}
/// Perform cleanup on socket shutdown_handler
/**
* @param h The `shutdown_handler` to call back when complete
*/
void async_shutdown(shutdown_handler handler) {
handler(lib::error_code());
}
size_t read_some_impl(char const * buf, size_t len) {
m_alog.write(log::alevel::devel,"debug_con read_some");
if (!m_reading) {
m_elog.write(log::elevel::devel,"write while not reading");
return 0;
}
size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
m_cursor += bytes_to_copy;
if (m_cursor >= m_bytes_needed) {
complete_read(lib::error_code());
}
return bytes_to_copy;
}
/// Signal that a requested read is complete
/**
* Sets the reading flag to false and returns the handler that should be
* called back with the result of the read. The cursor position that is sent
* is whatever the value of m_cursor is.
*
* It MUST NOT be called when m_reading is false.
* it MUST be called while holding the read lock
*
* It is important to use this method rather than directly setting/calling
* m_read_handler back because this function makes sure to delete the
* locally stored handler which contains shared pointers that will otherwise
* cause circular reference based memory leaks.
*
* @param ec The error code to forward to the read handler
*/
void complete_read(lib::error_code const & ec) {
m_reading = false;
read_handler handler = m_read_handler;
m_read_handler = read_handler();
handler(ec,m_cursor);
}
private:
timer_handler m_timer_handler;
// Read space (Protected by m_read_mutex)
char * m_buf;
size_t m_len;
size_t m_bytes_needed;
read_handler m_read_handler;
size_t m_cursor;
// transport resources
connection_hdl m_connection_hdl;
write_handler m_write_handler;
shutdown_handler m_shutdown_handler;
bool m_reading;
bool const m_is_server;
bool m_is_secure;
alog_type & m_alog;
elog_type & m_elog;
std::string m_remote_endpoint;
};
} // namespace debug
} // namespace transport
} // namespace websocketpp
#endif // WEBSOCKETPP_TRANSPORT_DEBUG_CON_HPP
@@ -0,0 +1,140 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_TRANSPORT_DEBUG_HPP
#define WEBSOCKETPP_TRANSPORT_DEBUG_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/endpoint.hpp>
#include <websocketpp/transport/debug/connection.hpp>
namespace websocketpp {
namespace transport {
namespace debug {
template <typename config>
class endpoint {
public:
/// Type of this endpoint transport component
typedef endpoint type;
/// Type of a pointer to this endpoint transport component
typedef lib::shared_ptr<type> ptr;
/// Type of this endpoint's concurrency policy
typedef typename config::concurrency_type concurrency_type;
/// Type of this endpoint's error logging policy
typedef typename config::elog_type elog_type;
/// Type of this endpoint's access logging policy
typedef typename config::alog_type alog_type;
/// Type of this endpoint transport component's associated connection
/// transport component.
typedef debug::connection<config> transport_con_type;
/// Type of a shared pointer to this endpoint transport component's
/// associated connection transport component
typedef typename transport_con_type::ptr transport_con_ptr;
// generate and manage our own io_service
explicit endpoint()
{
//std::cout << "transport::iostream::endpoint constructor" << std::endl;
}
/// Set whether or not endpoint can create secure connections
/**
* TODO: docs
*
* Setting this value only indicates whether or not the endpoint is capable
* of producing and managing secure connections. Connections produced by
* this endpoint must also be individually flagged as secure if they are.
*
* @since 0.3.0-alpha4
*
* @param value Whether or not the endpoint can create secure connections.
*/
void set_secure(bool) {}
/// Tests whether or not the underlying transport is secure
/**
* TODO: docs
*
* @return Whether or not the underlying transport is secure
*/
bool is_secure() const {
return false;
}
protected:
/// Initialize logging
/**
* The loggers are located in the main endpoint class. As such, the
* transport doesn't have direct access to them. This method is called
* by the endpoint constructor to allow shared logging from the transport
* component. These are raw pointers to member variables of the endpoint.
* In particular, they cannot be used in the transport constructor as they
* haven't been constructed yet, and cannot be used in the transport
* destructor as they will have been destroyed by then.
*
* @param a A pointer to the access logger to use.
* @param e A pointer to the error logger to use.
*/
void init_logging(alog_type *, elog_type *) {}
/// Initiate a new connection
/**
* @param tcon A pointer to the transport connection component of the
* connection to connect.
* @param u A URI pointer to the URI to connect to.
* @param cb The function to call back with the results when complete.
*/
void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
cb(lib::error_code());
}
/// Initialize a connection
/**
* Init is called by an endpoint once for each newly created connection.
* It's purpose is to give the transport policy the chance to perform any
* transport specific initialization that couldn't be done via the default
* constructor.
*
* @param tcon A pointer to the transport portion of the connection.
* @return A status code indicating the success or failure of the operation
*/
lib::error_code init(transport_con_ptr) {
return lib::error_code();
}
private:
};
} // namespace debug
} // namespace transport
} // namespace websocketpp
#endif // WEBSOCKETPP_TRANSPORT_DEBUG_HPP
@@ -30,6 +30,8 @@
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <string>
@@ -38,6 +40,14 @@ namespace transport {
/// Transport policy that uses STL iostream for I/O and does not support timers
namespace iostream {
/// The type and signature of the callback used by iostream transport to write
typedef lib::function<lib::error_code(connection_hdl, char const *, size_t)>
write_handler;
/// The type and signature of the callback used by iostream transport to signal
/// a transport shutdown.
typedef lib::function<lib::error_code(connection_hdl)> shutdown_handler;
/// iostream transport errors
namespace error {
enum value {
@@ -28,15 +28,20 @@
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#include <websocketpp/transport/iostream/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/transport/iostream/base.hpp>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace websocketpp {
@@ -307,6 +312,44 @@ public:
timer_ptr set_timer(long, timer_handler) {
return timer_ptr();
}
/// Sets the write handler
/**
* The write handler is called when the iostream transport receives data
* that needs to be written to the appropriate output location. This handler
* can be used in place of registering an ostream for output.
*
* The signature of the handler is
* `lib::error_code (connection_hdl, char const *, size_t)` The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_write_handler(write_handler h) {
m_write_handler = h;
}
/// Sets the shutdown handler
/**
* The shutdown handler is called when the iostream transport receives a
* notification from the core library that it is finished with all read and
* write operations and that the underlying transport can be cleaned up.
*
* If you are using iostream transport with another socket library, this is
* a good time to close/shutdown the socket for this connection.
*
* The signature of the handler is `lib::error_code (connection_hdl)`. The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_shutdown_handler(shutdown_handler h) {
m_shutdown_handler = h;
}
protected:
/// Initialize the connection transport
/**
@@ -375,7 +418,7 @@ protected:
/// Asyncronous Transport Write
/**
* Write len bytes in buf to the output stream. Call handler to report
* Write len bytes in buf to the output method. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
@@ -383,31 +426,40 @@ protected:
* output_stream_required: No output stream was registered to write to
* bad_stream: a ostream pass through error
*
* This method will attempt to write to the registered ostream first. If an
* ostream is not registered it will use the write handler. If neither are
* registered then an error is passed up to the connection.
*
* @param buf buffer to read bytes from
* @param len number of bytes to write
* @param handler Callback to invoke with operation status.
*/
void async_write(char const * buf, size_t len, write_handler handler) {
void async_write(char const * buf, size_t len, transport::write_handler
handler)
{
m_alog.write(log::alevel::devel,"iostream_con async_write");
// TODO: lock transport state?
if (!m_output_stream) {
handler(make_error_code(error::output_stream_required));
return;
}
lib::error_code ec;
m_output_stream->write(buf,len);
if (m_output_stream->bad()) {
handler(make_error_code(error::bad_stream));
if (m_output_stream) {
m_output_stream->write(buf,len);
if (m_output_stream->bad()) {
ec = make_error_code(error::bad_stream);
}
} else if (m_write_handler) {
ec = m_write_handler(m_connection_hdl, buf, len);
} else {
handler(lib::error_code());
ec = make_error_code(error::output_stream_required);
}
handler(ec);
}
/// Asyncronous Transport Write (scatter-gather)
/**
* Write a sequence of buffers to the output stream. Call handler to report
* Write a sequence of buffers to the output method. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
@@ -415,28 +467,43 @@ protected:
* output_stream_required: No output stream was registered to write to
* bad_stream: a ostream pass through error
*
* This method will attempt to write to the registered ostream first. If an
* ostream is not registered it will use the write handler. If neither are
* registered then an error is passed up to the connection.
*
* @param bufs vector of buffers to write
* @param handler Callback to invoke with operation status.
*/
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
void async_write(std::vector<buffer> const & bufs, transport::write_handler
handler)
{
m_alog.write(log::alevel::devel,"iostream_con async_write buffer list");
// TODO: lock transport state?
if (!m_output_stream) {
handler(make_error_code(error::output_stream_required));
return;
}
lib::error_code ec;
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
m_output_stream->write((*it).buf,(*it).len);
if (m_output_stream) {
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
m_output_stream->write((*it).buf,(*it).len);
if (m_output_stream->bad()) {
handler(make_error_code(error::bad_stream));
if (m_output_stream->bad()) {
ec = make_error_code(error::bad_stream);
break;
}
}
} else if (m_write_handler) {
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len);
if (ec) {break;}
}
} else {
ec = make_error_code(error::output_stream_required);
}
handler(lib::error_code());
handler(ec);
}
/// Set Connection Handle
@@ -465,10 +532,20 @@ protected:
/// Perform cleanup on socket shutdown_handler
/**
* @param h The `shutdown_handler` to call back when complete
* If a shutdown handler is set, call it and pass through its return error
* code. Otherwise assume there is nothing to do and pass through a success
* code.
*
* @param handler The `shutdown_handler` to call back when complete
*/
void async_shutdown(shutdown_handler handler) {
handler(lib::error_code());
void async_shutdown(transport::shutdown_handler handler) {
lib::error_code ec;
if (m_shutdown_handler) {
ec = m_shutdown_handler(m_connection_hdl);
}
handler(ec);
}
private:
void read(std::istream &in) {
@@ -558,6 +635,8 @@ private:
// transport resources
std::ostream * m_output_stream;
connection_hdl m_connection_hdl;
write_handler m_write_handler;
shutdown_handler m_shutdown_handler;
bool m_reading;
bool const m_is_server;
@@ -28,13 +28,15 @@
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/endpoint.hpp>
#include <websocketpp/transport/iostream/connection.hpp>
#include <iostream>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/memory.hpp>
#include <ostream>
namespace websocketpp {
namespace transport {
@@ -114,6 +116,44 @@ public:
bool is_secure() const {
return m_is_secure;
}
/// Sets the write handler
/**
* The write handler is called when the iostream transport receives data
* that needs to be written to the appropriate output location. This handler
* can be used in place of registering an ostream for output.
*
* The signature of the handler is
* `lib::error_code (connection_hdl, char const *, size_t)` The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_write_handler(write_handler h) {
m_write_handler = h;
}
/// Sets the shutdown handler
/**
* The shutdown handler is called when the iostream transport receives a
* notification from the core library that it is finished with all read and
* write operations and that the underlying transport can be cleaned up.
*
* If you are using iostream transport with another socket library, this is
* a good time to close/shutdown the socket for this connection.
*
* The signature of the handler is lib::error_code (connection_hdl). The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_shutdown_handler(shutdown_handler h) {
m_shutdown_handler = h;
}
protected:
/// Initialize logging
/**
@@ -156,10 +196,19 @@ protected:
*/
lib::error_code init(transport_con_ptr tcon) {
tcon->register_ostream(m_output_stream);
if (m_shutdown_handler) {
tcon->set_shutdown_handler(m_shutdown_handler);
}
if (m_write_handler) {
tcon->set_write_handler(m_write_handler);
}
return lib::error_code();
}
private:
std::ostream * m_output_stream;
shutdown_handler m_shutdown_handler;
write_handler m_write_handler;
elog_type * m_elog;
alog_type * m_alog;
bool m_is_secure;
@@ -45,11 +45,11 @@ enum value {
/// categories
general = 1,
/// not implimented
not_implimented
/// not implemented
not_implemented
};
/// iostream transport error category
/// stub transport error category
class category : public lib::error_category {
public:
category() {}
@@ -62,8 +62,8 @@ class category : public lib::error_category {
switch(value) {
case general:
return "Generic stub transport policy error";
case not_implimented:
return "feature not implimented";
case not_implemented:
return "feature not implemented";
default:
return "Unknown";
}
@@ -28,13 +28,18 @@
#ifndef WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
#define WEBSOCKETPP_TRANSPORT_STUB_CON_HPP
#include <websocketpp/transport/stub/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/transport/stub/base.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace transport {
@@ -68,6 +73,7 @@ public:
typedef lib::shared_ptr<timer> timer_ptr;
explicit connection(bool is_server, alog_type & alog, elog_type & elog)
: m_alog(alog), m_elog(elog)
{
m_alog.write(log::alevel::devel,"stub con transport constructor");
}
@@ -156,7 +162,7 @@ protected:
*/
void init(init_handler handler) {
m_alog.write(log::alevel::devel,"stub connection init");
handler(make_error_code(error::not_implimented));
handler(make_error_code(error::not_implemented));
}
/// Initiate an async_read for at least num_bytes bytes into buf
@@ -183,11 +189,11 @@ protected:
* @param handler The callback to invoke when the operation is complete or
* ends in an error
*/
void async_read_at_least(size_t num_bytes, char *buf, size_t len,
void async_read_at_least(size_t num_bytes, char * buf, size_t len,
read_handler handler)
{
m_alog.write(log::alevel::devel, "stub_con async_read_at_least");
handler(make_error_code(error::not_implimented));
handler(make_error_code(error::not_implemented), 0);
}
/// Asyncronous Transport Write
@@ -204,7 +210,7 @@ protected:
*/
void async_write(char const * buf, size_t len, write_handler handler) {
m_alog.write(log::alevel::devel,"stub_con async_write");
handler(make_error_code(error::not_implimented));
handler(make_error_code(error::not_implemented));
}
/// Asyncronous Transport Write (scatter-gather)
@@ -220,7 +226,7 @@ protected:
*/
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
m_alog.write(log::alevel::devel,"stub_con async_write buffer list");
handler(make_error_code(error::not_implimented));
handler(make_error_code(error::not_implemented));
}
/// Set Connection Handle
@@ -254,6 +260,8 @@ protected:
}
private:
// member variables!
alog_type & m_alog;
elog_type & m_elog;
};
@@ -55,7 +55,7 @@ public:
/// Type of this endpoint transport component's associated connection
/// transport component.
typedef iostream::connection<config> transport_con_type;
typedef stub::connection<config> transport_con_type;
/// Type of a shared pointer to this endpoint transport component's
/// associated connection transport component
typedef typename transport_con_type::ptr transport_con_ptr;
@@ -113,7 +113,7 @@ protected:
* @param cb The function to call back with the results when complete.
*/
void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
cb(make_error_code(error::not_implimented));
cb(make_error_code(error::not_implemented));
}
/// Initialize a connection
@@ -127,7 +127,7 @@ protected:
* @return A status code indicating the success or failure of the operation
*/
lib::error_code init(transport_con_ptr tcon) {
cb(make_error_code(error::not_implimented));
return make_error_code(error::not_implemented);
}
private:
+2 -2
View File
@@ -28,11 +28,11 @@
#ifndef WEBSOCKETPP_URI_HPP
#define WEBSOCKETPP_URI_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/common/memory.hpp>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
@@ -31,6 +31,8 @@
#include <websocketpp/common/stdint.hpp>
#include <string>
namespace websocketpp {
namespace utf8_validator {
+2 -2
View File
@@ -42,7 +42,7 @@ namespace websocketpp {
/// Library major version number
static int const major_version = 0;
/// Library minor version number
static int const minor_version = 4;
static int const minor_version = 5;
/// Library patch version number
static int const patch_version = 0;
/// Library pre-release flag
@@ -53,7 +53,7 @@ static int const patch_version = 0;
static char const prerelease_flag[] = "";
/// Default user agent string
static char const user_agent[] = "WebSocket++/0.4.0";
static char const user_agent[] = "WebSocket++/0.5.0";
} // namespace websocketpp