http parser and logger work

This commit is contained in:
Peter Thorson
2011-11-05 11:01:01 -05:00
parent 7ff1e6a546
commit d6a59f459c
13 changed files with 1215 additions and 255 deletions

View File

@@ -47,8 +47,6 @@
#include <string>
#include <queue>
using websocketpp::session_ptr;
namespace websocketecho {
class echo_client_handler : public websocketpp::connection_handler {

View File

@@ -27,6 +27,7 @@
#ifndef ECHO_SERVER_HANDLER_HPP
#define ECHO_SERVER_HANDLER_HPP
#include "../../src/websocketpp.hpp"
#include "../../src/websocket_connection_handler.hpp"
#include <boost/shared_ptr.hpp>

View File

@@ -23,8 +23,6 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This Makefile was derived from a similar one included in the libjson project
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
*/
#ifndef HTTP_CONSTANTS_HPP
@@ -36,6 +34,7 @@ namespace http {
enum value {
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
@@ -43,6 +42,7 @@ namespace http {
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
@@ -50,6 +50,7 @@ namespace http {
NOT_MODIFIED = 304,
USE_PROXY = 305,
TEMPORARY_REDIRECT = 307,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
@@ -68,9 +69,7 @@ namespace http {
UNSUPPORTED_MEDIA_TYPE = 415,
REQUEST_RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
IM_A_TEAPOT = 418,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
@@ -82,7 +81,6 @@ namespace http {
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511
};

View File

@@ -23,24 +23,240 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This Makefile was derived from a similar one included in the libjson project
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
*/
#ifndef BLANK_RNG_HPP
#define BLANK_RNG_HPP
#ifndef HTTP_PARSER_HPP
#define HTTP_PARSER_HPP
#include <stdint.h>
#include <map>
#include <string>
#include "constants.hpp"
namespace websocketpp {
class blank_rng {
public:
int32_t gen() {
throw "Random Number generation not supported";
namespace http {
namespace parser {
namespace state {
enum value {
METHOD,
RESOURCE,
VERSION,
HEADERS
};
}
typedef std::map<std::string,std::string> header_list;
class parser {
public:
// consumes bytes from the stream and returns true if enough bytes have
// been read
bool consume (std::istream& s) {
throw "No Implimented";
}
void set_version(const std::string& version) {
// TODO: validation?
m_version = version;
}
const std::string& version() const {
return m_version;
}
std::string header(const std::string& key) const {
header_list::const_iterator h = m_headers.find(key);
if (h == m_headers.end()) {
return "";
} else {
return h->second;
}
}
// multiple calls to set header will result in values aggregating.
// use replace_header if you do not want this behavior.
void set_header(const std::string &key,const std::string &val) {
// TODO: prevent use of reserved headers?
if (this->header(key) == "") {
m_headers[key] = val;
} else {
m_headers[key] += ", " + val;
}
}
void replace_header(const std::string &key,const std::string &val) {
m_headers[key] = val;
}
protected:
bool parse_headers(std::istream& s) {
std::string header;
std::string::size_type end;
// get headers
while (std::getline(s, header) && header != "\r") {
if (header[header.size()-1] != '\r') {
continue; // ignore malformed header lines?
} else {
header.erase(header.end()-1);
}
end = header.find(": ",0);
if (end != std::string::npos) {
set_header(header.substr(0,end),header.substr(end+2));
}
}
return true;
}
std::string raw_headers() {
std::stringstream raw;
header_list::iterator it;
for (it = m_headers.begin(); it != m_headers.end(); it++) {
raw << it->first << ": " << it->second << "\r\n";
}
return raw.str();
}
private:
std::string m_version;
header_list m_headers;
};
class request : public parser {
public:
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
bool parse_complete(std::istream& s) {
std::string request;
// get status line
std::getline(s, request);
if (request[request.size()-1] == '\r') {
request.erase(request.end()-1);
std::stringstream ss(request);
std::string val;
ss >> val;
set_method(val);
ss >> val;
set_uri(val);
ss >> val;
set_version(val);
} else {
return false;
}
return parse_headers(s);
}
// TODO: validation. Make sure all required fields have been set?
std::string raw() {
std::stringstream raw;
raw << m_method << " " << m_uri << " " << version() << "\r\n";
raw << raw_headers() << "\r\n";
return raw.str();
}
void set_method(const std::string& method) {
// TODO: validation?
m_method = method;
}
const std::string& method() const {
return m_method;
}
void set_uri(const std::string& uri) {
// TODO: validation?
m_uri = uri;
}
const std::string& uri() const {
return m_uri;
}
private:
std::string m_method;
std::string m_uri;
};
class response : public parser {
public:
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
bool parse_complete(std::istream& s) {
std::string response;
// get status line
std::getline(s, response);
if (response[response.size()-1] == '\r') {
response.erase(response.end()-1);
std::stringstream ss(response);
std::string str_val;
int int_val;
ss >> str_val;
set_version(str_val);
ss >> int_val;
set_status(status_code::value(int_val),str_val);
} else {
return false;
}
return parse_headers(s);
}
// TODO: validation. Make sure all required fields have been set?
std::string raw() {
std::stringstream raw;
raw << version() << " " << m_status_code << " " << m_status_msg << "\r\n";
raw << raw_headers() << "\r\n";
return raw.str();
}
void set_status(status_code::value code) {
// TODO: validation?
m_status_code = code;
m_status_msg = get_string(code);
}
void set_status(status_code::value code, const std::string& msg) {
// TODO: validation?
m_status_code = code;
m_status_msg = msg;
}
status_code::value status_code() const {
return m_status_code;
}
const std::string& status_msg() const {
return m_status_msg;
}
private:
status_code::value m_status_code;
std::string m_status_msg;
};
}
}
}
#endif // BLANK_RNG_HPP
#endif // HTTP_PARSER_HPP

147
src/logger/logger.hpp Normal file
View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef ZS_LOGGER_HPP
#define ZS_LOGGER_HPP
#include <iostream>
#include <sstream>
namespace websocketpp {
namespace log {
namespace alog_level {
typedef uint16_t value;
static const value OFF = 0x0;
// A single line on connect with connecting ip, websocket version,
// request resource, user agent, and the response code.
static const value CONNECT = 0x1;
// A single line on disconnect with wasClean status and local and remote
// close codes and reasons.
static const value DISCONNECT = 0x2;
// A single line on incoming and outgoing control messages.
static const value CONTROL = 0x4;
// A single line on incoming and outgoing frames with full frame headers
static const value FRAME_HEADER = 0x10;
// Adds payloads to frame logs. Note these can be long!
static const value FRAME_PAYLOAD = 0x20;
// A single line on incoming and outgoing messages with metadata about type,
// length, etc
static const value MESSAGE_HEADER = 0x40;
// Adds payloads to message logs. Note these can be long!
static const value MESSAGE_PAYLOAD = 0x80;
static const value ALL = 0xFFFF;
}
namespace elog_level {
typedef uint16_t value;
static const value OFF = 0x0;
static const value DEBUG = 0x1;
static const value INFO = 0x2;
static const value WARN = 0x4;
static const value ERROR = 0x10;
static const value FATAL = 0x20;
static const value ALL = 0xFFFF;
}
template <typename level_type>
class logger {
public:
//template <typename T>
//logger& operator<<(T a);
template <typename T>
logger<level_type>& operator<<(T a) {
if (test_level(m_write_level)) {
oss << a;
}
return *this;
}
logger<level_type>& operator<<(logger<level_type>& (*f)(logger<level_type>& out)) {
return f(*this);
}
/*
logger& operator<<(endl) {
std::cout << "fff" << oss.str() << std::endl;
oss.str("");
return *this;
}*/
bool test_level(level_type l) {
return (m_level & l) != 0;
}
void set_level(level_type l) {
m_level |= l;
}
void unset_level(level_type l) {
m_level &= ~l;
}
logger<level_type>& print() {
if (test_level(m_write_level)) {
std::cout << "[timestamp] " << oss.str() << std::endl;
oss.str("");
}
return *this;
}
logger<level_type>& at(level_type l) {
m_write_level = l;
return *this;
}
private:
std::ostringstream oss;
level_type m_write_level;
level_type m_level;
};
template <typename level_type>
logger<level_type>& endl(logger<level_type>& out)
{
return out.print();
}
}
}
#endif // ZS_LOGGER_HPP

View File

@@ -0,0 +1,49 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_SESSION_HANDLER_INTERFACE_HPP
#define WEBSOCKETPP_SESSION_HANDLER_INTERFACE_HPP
#include <boost/shared_ptr.hpp>
#include <string>
#include <map>
namespace websocketpp {
namespace session {
class session_handler_interface {
public:
};
}
}
#endif // WEBSOCKETPP_SESSION_HANDLER_INTERFACE_HPP

View File

@@ -30,18 +30,22 @@
#include <boost/asio.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/program_options.hpp>
namespace po = boost::program_options;
#include <set>
namespace websocketpp {
class client;
typedef boost::shared_ptr<client> client_ptr;
}
#include "websocketpp.hpp"
#include "websocket_client_session.hpp"
#include "websocket_session.hpp"
#include "websocket_connection_handler.hpp"
#include <boost/date_time/posix_time/posix_time.hpp>
#include "rng/boost_rng.hpp"
#include "http/parser.hpp"
using boost::asio::ip::tcp;
namespace websocketpp {
@@ -59,75 +63,231 @@ private:
std::string m_msg;
};
class client : public boost::enable_shared_from_this<client> {
public:
static const uint16_t CLIENT_STATE_NULL = 0;
static const uint16_t CLIENT_STATE_INITIALIZED = 1;
static const uint16_t CLIENT_STATE_CONNECTING = 2;
static const uint16_t CLIENT_STATE_CONNECTED = 3;
template <typename rng_policy = boost_rng>
class client : public boost::enable_shared_from_this<client<rng_policy> > {
public:
typedef rng_policy rng_t;
typedef client<rng_policy> endpoint_type;
typedef session<endpoint_type> session_type;
typedef connection_handler<session_type> connection_handler_type;
typedef boost::shared_ptr<endpoint_type> ptr;
typedef boost::shared_ptr<session_type> session_ptr;
typedef boost::shared_ptr<connection_handler_type> connection_handler_ptr;
static const uint16_t CLIENT_STATE_NULL = 0;
static const uint16_t CLIENT_STATE_INITIALIZED = 1;
static const uint16_t CLIENT_STATE_CONNECTING = 2;
static const uint16_t CLIENT_STATE_CONNECTED = 3;
client(boost::asio::io_service& io_service,
connection_handler_ptr defc);
client<rng_policy>(boost::asio::io_service& io_service,
connection_handler_ptr defc)
: m_elog_level(LOG_OFF),
m_alog_level(ALOG_OFF),
m_state(CLIENT_STATE_NULL),
m_max_message_size(DEFAULT_MAX_MESSAGE_SIZE),
m_io_service(io_service),
m_resolver(io_service),
m_def_con_handler(defc) {}
// INTERFACE FOR LOCAL APPLICATIONS
// initializes the session. Methods that affect the opening handshake
// such as add_protocol and set_header must be called after init and
// before connect.
void init();
// INTERFACE FOR LOCAL APPLICATIONS
// initializes the session. Methods that affect the opening handshake
// such as add_protocol and set_header must be called after init and
// before connect.
void init() {
// TODO: sanity check whether the session buffer size bound could be reduced
m_client_session = session_ptr(
new session_type(
endpoint_type::shared_from_this(),
m_io_service,
m_def_con_handler,
m_max_message_size*2
)
);
m_state = CLIENT_STATE_INITIALIZED;
}
// starts the connection process. Should be called before
// io_service.run(), connection process will not start until run() has
// been called.
void connect(const std::string& url);
// starts the connection process. Should be called before
// io_service.run(), connection process will not start until run() has
// been called.
void connect(const std::string& u) {
if (m_state != CLIENT_STATE_INITIALIZED) {
throw client_error("connect can only be called after init and before a connection has been established");
}
// Adds a protocol to the opening handshake.
// Must be called before connect
void add_subprotocol(const std::string& p);
ws_uri uri;
// Sets the value of the given HTTP header to be sent during the
// opening handshake. Must be called before connect
void set_header(const std::string& key,const std::string& val);
if (!uri.parse(u)) {
throw client_error("Invalid WebSocket URI");
}
void set_origin(const std::string& val);
if (uri.secure) {
throw client_error("wss / secure connections are not supported at this time");
}
m_client_session->set_uri(uri);
std::stringstream port;
port << uri.port;
tcp::resolver::query query(uri.host,port.str());
tcp::resolver::iterator iterator = m_resolver.resolve(query);
boost::asio::async_connect(
m_client_session->socket(),
iterator,
boost::bind(
&endpoint_type::handle_connect,
endpoint_type::shared_from_this(),
boost::asio::placeholders::error
)
);
m_state = CLIENT_STATE_CONNECTING;
}
// Adds a protocol to the opening handshake.
// Must be called before connect
void add_subprotocol(const std::string& p) {
if (m_state != CLIENT_STATE_INITIALIZED) {
throw client_error("add_protocol can only be called after init and before connect");
}
m_client_session->add_subprotocol(p);
}
// Sets the value of the given HTTP header to be sent during the
// opening handshake. Must be called before connect
void set_header(const std::string& key,const std::string& val) {
if (m_state != CLIENT_STATE_INITIALIZED) {
throw client_error("set_header can only be called after init and before connect");
}
m_client_session->set_request_header(key,val);
}
void set_origin(const std::string& val) {
if (m_state != CLIENT_STATE_INITIALIZED) {
throw client_error("set_origin can only be called after init and before connect");
}
m_client_session->set_origin(val);
}
void set_max_message_size(uint64_t val);
void set_max_message_size(uint64_t val) {
if (val > frame::limits::PAYLOAD_SIZE_JUMBO) {
std::stringstream err;
err << "Invalid maximum message size: " << val;
// TODO: Figure out what the ideal error behavior for this method.
// Options:
// Throw exception
// Log error and set value to maximum allowed
// Log error and leave value at whatever it was before
log(err.str(),LOG_WARN);
//throw client_error(err.str());
}
m_max_message_size = val;
}
// Test methods determine if a message of the given level should be
// written. elog shows all values above the level set. alog shows only
// the values explicitly set.
bool test_elog_level(uint16_t level) {
return (level >= m_elog_level);
}
void set_elog_level(uint16_t level) {
std::stringstream msg;
msg << "Error logging level changing from "
<< m_elog_level << " to " << level;
log(msg.str(),LOG_INFO);
// Test methods determine if a message of the given level should be
// written. elog shows all values above the level set. alog shows only
// the values explicitly set.
bool test_elog_level(uint16_t level);
void set_elog_level(uint16_t level);
m_elog_level = level;
}
bool test_alog_level(uint16_t level) {
return ((level & m_alog_level) != 0);
}
void set_alog_level(uint16_t level) {
if (test_alog_level(level)) {
return;
}
std::stringstream msg;
msg << "Access logging level " << level << " being set";
access_log(msg.str(),ALOG_INFO);
bool test_alog_level(uint16_t level);
void set_alog_level(uint16_t level);
void unset_alog_level(uint16_t level);
m_alog_level |= level;
}
void unset_alog_level(uint16_t level) {
if (!test_alog_level(level)) {
return;
}
std::stringstream msg;
msg << "Access logging level " << level << " being unset";
access_log(msg.str(),ALOG_INFO);
m_alog_level &= ~level;
}
// INTERFACE FOR SESSIONS
// INTERFACE FOR SESSIONS
// Check if message size is within server's acceptable parameters
bool validate_message_size(uint64_t val);
// write to the server's logs
void log(std::string msg,uint16_t level = LOG_ERROR);
void access_log(std::string msg,uint16_t level);
private:
// if no errors starts the session's read loop and returns to the
// start_accept phase.
void handle_connect(const boost::system::error_code& error);
private:
uint16_t m_elog_level;
uint16_t m_alog_level;
uint16_t m_state;
// Check if message size is within server's acceptable parameters
bool validate_message_size(uint64_t val) {
if (val > m_max_message_size) {
return false;
}
return true;
}
// write to the server's logs
void log(std::string msg,uint16_t level = LOG_ERROR) {
if (!test_elog_level(level)) {
return;
}
std::cerr << "[Error Log] "
<< boost::posix_time::to_iso_extended_string(
boost::posix_time::second_clock::local_time())
<< " " << msg << std::endl;
}
void access_log(std::string msg,uint16_t level) {
if (!test_alog_level(level)) {
return;
}
std::cout << "[Access Log] "
<< boost::posix_time::to_iso_extended_string(
boost::posix_time::second_clock::local_time())
<< " " << msg << std::endl;
}
private:
// if no errors starts the session's read loop and returns to the
// start_accept phase.
void handle_connect(const boost::system::error_code& error) {
if (!error) {
std::stringstream err;
err << "Successful Connection ";
log(err.str(),LOG_ERROR);
m_state = CLIENT_STATE_CONNECTED;
m_client_session->on_connect();
} else {
std::stringstream err;
err << "An error occurred while establishing a connection: " << error;
log(err.str(),LOG_ERROR);
throw client_error(err.str());
}
}
private:
uint16_t m_elog_level;
uint16_t m_alog_level;
uint16_t m_state;
std::set<std::string> m_hosts;
uint64_t m_max_message_size;
boost::asio::io_service& m_io_service;
tcp::resolver m_resolver;
client_session_ptr m_client_session;
connection_handler_ptr m_def_con_handler;
std::set<std::string> m_hosts;
uint64_t m_max_message_size;
boost::asio::io_service& m_io_service;
tcp::resolver m_resolver;
session_ptr m_client_session;
connection_handler_ptr m_def_con_handler;
};
}

View File

@@ -23,8 +23,6 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This Makefile was derived from a similar one included in the libjson project
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
*/
#ifndef WEBSOCKET_CONSTANTS_HPP

View File

@@ -44,22 +44,25 @@ namespace po = boost::program_options;
#include "rng/blank_rng.hpp"
#include "http/parser.hpp"
using boost::asio::ip::tcp;
namespace websocketpp {
// TODO: potential policies:
// - http parser
template <typename rng_policy = blank_rng>
class server : public boost::enable_shared_from_this< server<rng_policy> > {
public:
typedef rng_policy rng_t;
typedef server<rng_policy> server_type;
typedef session<server_type> session_type;
typedef server<rng_policy> endpoint_type;
typedef session<endpoint_type> session_type;
typedef connection_handler<session_type> connection_handler_type;
typedef boost::shared_ptr<server_type> ptr;
typedef boost::shared_ptr<endpoint_type> ptr;
typedef boost::shared_ptr<session_type> session_ptr;
typedef boost::shared_ptr<connection_handler_type> connection_handler_ptr;
@@ -93,7 +96,7 @@ public:
// TODO: sanity check whether the session buffer size bound could be reduced
session_ptr new_session(
new session_type(
server_type::shared_from_this(),
endpoint_type::shared_from_this(),
m_io_service,
m_def_con_handler,
m_max_message_size*2
@@ -103,8 +106,8 @@ public:
m_acceptor.async_accept(
new_session->socket(),
boost::bind(
&server_type::handle_accept,
server_type::shared_from_this(),
&endpoint_type::handle_accept,
endpoint_type::shared_from_this(),
new_session,
boost::asio::placeholders::error
)
@@ -202,6 +205,75 @@ public:
return m_rng;
}
// checks a handshake for validity. Returns true if valid and throws a
// handshake_error otherwise
bool validate_handshake(const http::parser::request& handshake) {
std::stringstream err;
std::string h;
if (handshake.method() != "GET") {
err << "Websocket handshake has invalid method: "
<< handshake.method();
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
// TODO: allow versions greater than 1.1
if (handshake.version() != "HTTP/1.1") {
err << "Websocket handshake has invalid HTTP version: "
<< handshake.method();
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
// verify the presence of required headers
h = handshake.header("Host");
if (h == "") {
throw(handshake_error("Required Host header is missing",http::status_code::BAD_REQUEST));
} else if (!this->validate_host(h)) {
err << "Host " << h << " is not one of this server's names.";
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
h = handshake.header("Upgrade");
if (h == "") {
throw(handshake_error("Required Upgrade header is missing",http::status_code::BAD_REQUEST));
} else if (!boost::ifind_first(h,"websocket")) {
err << "Upgrade header \"" << h << "\", does not contain required token \"websocket\"";
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
h = handshake.header("Connection");
if (h == "") {
throw(handshake_error("Required Connection header is missing",http::status_code::BAD_REQUEST));
} else if (!boost::ifind_first(h,"upgrade")) {
err << "Connection header, \"" << h
<< "\", does not contain required token \"upgrade\"";
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
if (handshake.header("Sec-WebSocket-Key") == "") {
throw(handshake_error("Required Sec-WebSocket-Key header is missing",http::status_code::BAD_REQUEST));
}
h = handshake.header("Sec-WebSocket-Version");
if (h == "") {
// TODO: if we want to support draft 00 this line should set version to 0
// rather than bail
throw(handshake_error("Required Sec-WebSocket-Version header is missing",http::status_code::BAD_REQUEST));
} else {
int version = atoi(h.c_str());
if (version != 7 && version != 8 && version != 13) {
err << "This server doesn't support WebSocket protocol version "
<< version;
throw(handshake_error(err.str(),http::status_code::BAD_REQUEST));
}
}
return true;
}
// Confirms that the port in the host string matches the port we are listening
// on. End user application is responsible for checking the /host/ part.
bool validate_host(std::string host) {
@@ -251,7 +323,7 @@ private:
void handle_accept(session_ptr session,const boost::system::error_code& error)
{
if (!error) {
session->on_connect();
session->read_request();
} else {
std::stringstream err;
err << "Error accepting socket connection: " << error;

View File

@@ -70,6 +70,96 @@
- idle client timeout? API specifiable?
- wait for pong?
INTERFACES
CLIENT ENDPOINT
SERVER ENDPOINT
CLIENT HANDLER
Valid for OPEN connections
- get_state() // CONNECTING, OPEN, CLOSING, CLOSED
- get_origin()
- get_request_header(const std::string&)
- get_version()
- get_uri() // includes secure, host, port, resource
- get_secure()
- send(const std::string&)
- send(const std::vector<unsigned char>&)
- close(status::code::value,const std::string&)
- ping(const std::string&)
- pong(const std::string&)
- get_subprotocol()
- **** get extensions ****
- get_request_header(const std::string&)
- get_response_header(const std::string&)
Valid for CLOSED connections
- get_local_close_code()
- get_local_close_reason()
- get_remote_close_code()
- get_remote_close_reason()
- dropped_by_me?
- failed_by_me?
- closed_by_me?
Callbacks that may be implimented
- on_message(const std::string&)
- on_message(const std::vector<unsigned char>&)
- on_close
- on_fail?
- on_write_avaliable
SERVER HANDLER
Valid for CONNECTING connections
- **** get subprotocols ****
- **** get extensions ****
- set_request_header(const std::string&,const std::string&)
- select_subprotocol(const std::string&)
- select_extension(const std::string&)
Valid during and after CONNECTING
- get_origin()
- get_request_header(const std::string&)
- get_version()
- get_uri() // includes secure, host, port, resource
- get_secure()
- get_state() // CONNECTING, OPEN, CLOSING, CLOSED
Valid for OPEN connections
- send(const std::string&)
- send(const std::vector<unsigned char>&)
- close(status::code::value,const std::string&)
- ping(const std::string&)
- pong(const std::string&)
- get_subprotocol()
- **** get extensions ****
- get_response_header(const std::string&)
Valid for CLOSED connections
- get_local_close_code()
- get_local_close_reason()
- get_remote_close_code()
- get_remote_close_reason()
- dropped_by_me?
- failed_by_me?
- closed_by_me?
Callbacks that may be implimented
- validate
- on_message(const std::string&)
- on_message(const std::vector<unsigned char>&)
- on_close
- on_fail?
- on_write_avaliable
*/
#ifndef WEBSOCKET_SESSION_HPP
@@ -111,6 +201,8 @@ namespace websocketpp {
#include "sha1/sha1.h"
#include "utf8_validator/utf8_validator.hpp"
#include "http/parser.hpp"
using boost::asio::ip::tcp;
namespace websocketpp {
@@ -129,7 +221,7 @@ namespace state {
class handshake_error : public std::exception {
public:
handshake_error(const std::string& msg,
int http_error,
http::status_code::value http_error,
const std::string& http_msg = "")
: m_msg(msg),m_http_error_code(http_error),m_http_error_msg(http_msg) {}
~handshake_error() throw() {}
@@ -138,9 +230,9 @@ public:
return m_msg.c_str();
}
std::string m_msg;
int m_http_error_code;
std::string m_http_error_msg;
std::string m_msg;
http::status_code::value m_http_error_code;
std::string m_http_error_msg;
};
typedef std::map<std::string,std::string> header_list;
@@ -162,7 +254,8 @@ public:
boost::asio::io_service& io_service,
connection_handler_ptr defc,
uint64_t buf_size)
: m_state(state::CONNECTING),
: m_secure(false),
m_state(state::CONNECTING),
m_writing(false),
m_local_close_code(close::status::NO_STATUS),
m_remote_close_code(close::status::NO_STATUS),
@@ -178,29 +271,93 @@ public:
m_utf8_state(utf8_validator::UTF8_ACCEPT),
m_utf8_codepoint(0),
m_read_frame(e->get_rng()),
m_write_frame(e->get_rng())
{
}
m_write_frame(e->get_rng()) {}
/*** ENDPOINT INTERFACE ***/
tcp::socket& socket() {
return m_socket;
}
boost::asio::io_service& io_service() {
return m_io_service;
}
/*** SERVER INTERFACE ***/
// This function is called to begin the session loop. This method and all
// that come after it are called as a result of an async event completing.
// if any method in this chain returns before adding a new async event the
// session will end.
// TODO: this needs to be a template specialization or member of the endpoint
void on_connect() {
read_handshake();
void set_uri(const ws_uri& uri) {
m_uri = uri;
}
const ws_uri& get_uri() {
return m_uri;
}
void set_origin(const std::string& val) {
// TODO: input validation
m_origin = val;
}
// TODO: should these be one set?
void set_request_header(const std::string& key,const std::string& val) {
// TODO: input validation
m_request.set_header(key,val);
}
void set_response_header(const std::string& key,const std::string& val) {
// TODO: input validation
m_response.set_header(key,val);
}
// Adds a subprotocol to the list to propose to the remote endpoint
// TODO: this should not be callable by server handlers
void request_subprotocol(const std::string &val) {
// TODO: input validation
m_requested_subprotocols.push_back(val);
}
// Adds an extension to the list to propose to the remote endpoint
// TODO: this should not be callable by server handlers
void request_extension(const std::string& val) {
// TODO: input validation
m_requested_extensions.push_back(val);
}
// Selects a subprotocol from the requested list to use.
// TODO: this should only be callable by server handlers
void select_subprotocol(const std::string& val) {
std::vector<std::string>::iterator it;
it = std::find(m_requested_subprotocols.begin(),
m_requested_subprotocols.end(),
val);
if (val != "" && it == m_requested_subprotocols.end()) {
throw server_error("Attempted to choose a subprotocol not proposed by the client");
}
m_subprotocol = val;
}
// Selects an extension from the requested list to use.
// TODO: this should only be callable by server handlers
void select_extension(const std::string& val) {
if (val == "") {
return;
}
std::vector<std::string>::iterator it;
it = std::find(m_requested_extensions.begin(),
m_requested_extensions.end(),
val);
if (it == m_requested_extensions.end()) {
throw server_error("Attempted to choose an extension not proposed by the client");
}
m_extensions.push_back(val);
}
/*** SERVER INTERFACE ***/
// sets the internal connection handler of this connection to new_con.
// This is useful if you want to switch handler objects during a connection
// Example: a generic lobby handler could validate the handshake negotiate a
@@ -231,23 +388,23 @@ public:
//throw server_error("Subprotocol is not avaliable before the handshake has completed.");
throw "Subprotocol is not avaliable before the handshake has completed";
}
return m_server_subprotocol;
return m_subprotocol;
}
const std::string& get_resource() const {
return m_resource;
return m_uri.resource;
}
const std::string& get_origin() const {
return m_client_origin;
return m_origin;
}
std::string get_client_header(const std::string& key) const {
return get_header(key,m_client_headers);
std::string get_request_header(const std::string& key) const {
return m_request.header(key);
}
std::string get_server_header(const std::string& key) const {
return get_header(key,m_server_headers);
std::string get_response_header(const std::string& key) const {
return m_response.header(key);
}
const std::vector<std::string>& get_extensions() const {
return m_server_extensions;
return m_extensions;
}
unsigned int get_version() const {
return m_version;
@@ -255,7 +412,7 @@ public:
/**** TODO: SERVER SPECIFIC ****/
/*
void set_header(const std::string &key,const std::string &val) {
// TODO: prevent use of reserved headers;
m_server_headers[key] = val;
@@ -291,7 +448,7 @@ public:
}
m_server_extensions.push_back(val);
}
}*/
/********/
@@ -353,55 +510,114 @@ public:
bool is_server() const {
return endpoint_type::is_server;
}
// Opening handshake processors and callbacks. These need to be defined in
// derived classes.
// TODO: endpoint specific
//virtual void handle_write_handshake(const boost::system::error_code& e) = 0;
void handle_write_handshake(const boost::system::error_code& error) {
if (error) {
log_error("Error writing handshake response",error);
drop_tcp();
return;
}
// These two function series are called to begin the session loop. The first
// method and all that come after it are called as a result of an async
// event completing. if any method in this chain returns before adding a new
// async event the session will end.
// ****** Read Handshake Thread ***********************
// read_request -> handle_read_request ->
// write_response > handle_write_response ->
// read frame
// ****************************************************
// Initiates the read of an HTTP request
void read_request() {
m_timer.expires_from_now(boost::posix_time::seconds(5));
log_open_result();
m_timer.async_wait(
boost::bind(
&session_type::handle_handshake_expired,
session_type::shared_from_this(),
boost::asio::placeholders::error
)
);
if (m_server_http_code != 101) {
std::stringstream err;
err << "Handshake ended with HTTP error: " << m_server_http_code << " "
<< (m_server_http_string != "" ? m_server_http_string : lookup_http_error_string(m_server_http_code));
log(err.str(),LOG_ERROR);
drop_tcp();
// TODO: tell client that connection failed.
return;
}
m_state = state::OPEN;
// stop the handshake timer
m_timer.cancel();
if (m_local_interface) {
m_local_interface->on_open(session_type::shared_from_this());
}
reset_message();
this->read_frame();
boost::asio::async_read_until(
m_socket,
m_buf,
"\r\n\r\n",
boost::bind(
&session_type::handle_read_request,
session_type::shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
// TODO: endpoint specific
//virtual void handle_read_handshake(const boost::system::error_code& e,std::size_t bytes_transferred) = 0;
void handle_read_handshake(const boost::system::error_code& e,
// Callback for reading an HTTP request
void handle_read_request(const boost::system::error_code& e,
std::size_t bytes_transferred) {
std::ostringstream line;
line << &m_buf;
m_raw_client_handshake += line.str();
if (e) {
log_error("Error reading HTTP request",e);
drop_tcp();
return;
}
access_log(m_raw_client_handshake,ALOG_HANDSHAKE);
try {
std::istream request(&m_buf);
// TODO: use a more generic consume api where we just call read_some
// and have the handshake consume and validate as we go.
//
// For now, because it simplifies things we will use the parse_header
// member function which requires the complete header to be passed in
// initially. ASIO can guarantee us this.
//
//
//m_remote_handshake.consume(response_stream);
if (!m_request.parse_complete(request)) {
// not a valid HTTP request/response
throw handshake_error("Recieved invalid HTTP Request",http::status_code::BAD_REQUEST);
}
// Log the raw handshake.
access_log(m_request.raw(),ALOG_HANDSHAKE);
// confirm that this is a valid handshake / response for our endpoint type.
m_endpoint->validate_handshake(m_request);
// set some connection state from the handshake
// The endpoint validation should have ensured that all of these values
// exist and are acceptable.
std::string h = m_request.header("Sec-WebSocket-Version");
m_version = (h == "" ? 0 : atoi(h.c_str()));
h = (m_version < 13 ? "Sec-WebSocket-Origin" : "Origin");
m_origin = m_request.header(h);
// TODO: extract subprotocols?
// TODO: extract extensions?
// Check with the local interface to confirm that it wants to accept
// this connection.
if (m_local_interface) {
m_local_interface->validate(session_type::shared_from_this());
}
m_response.set_status(http::status_code::SWITCHING_PROTOCOLS);
} catch (const handshake_error& e) {
// TODO: add a hook here for passing the request to the local handler
// in case they can answer it instead of returning an error.
std::stringstream err;
err << "Caught handshake exception: " << e.what();
access_log(e.what(),ALOG_HANDSHAKE);
log(err.str(),LOG_ERROR);
m_response.set_status(e.m_http_error_code,e.m_http_error_msg);
}
std::vector<std::string> tokens;
write_response();
/* legacy code here until I am sure I wont need any of it */
/*std::vector<std::string> tokens;
std::string::size_type start = 0;
std::string::size_type end;
@@ -432,39 +648,15 @@ public:
m_client_headers[h] += ", " + tokens[i].substr(end+2);
}
}
}
}*/
// handshake error checking
try {
/*try {
std::stringstream err;
std::string h;
// check the method
if (m_client_http_request.substr(0,4) != "GET ") {
err << "Websocket handshake has invalid method: "
<< m_client_http_request.substr(0,4);
throw(handshake_error(err.str(),400));
}
// check the HTTP version
// TODO: allow versions greater than 1.1
end = m_client_http_request.find(" HTTP/1.1",4);
if (end == std::string::npos) {
err << "Websocket handshake has invalid HTTP version";
throw(handshake_error(err.str(),400));
}
m_resource = m_client_http_request.substr(4,end-4);
// verify the presence of required headers
h = get_client_header("Host");
if (h == "") {
throw(handshake_error("Required Host header is missing",400));
} else if (!m_endpoint->validate_host(h)) {
err << "Host " << h << " is not one of this server's names.";
throw(handshake_error(err.str(),400));
}
h = get_client_header("Upgrade");
if (h == "") {
@@ -529,17 +721,17 @@ public:
m_server_http_code = e.m_http_error_code;
m_server_http_string = e.m_http_error_msg;
}
write_handshake();
}*/
}
public: //protected:
// TODO: endpoint specific
void write_handshake() {
// write the response to the client's request.
void write_response() {
std::stringstream h;
if (m_server_http_code == 101) {
std::string server_key = get_client_header("Sec-WebSocket-Key");
m_response.set_version("HTTP/1.1");
if (m_response.status_code() == http::status_code::SWITCHING_PROTOCOLS) {
std::string server_key = m_request.header("Sec-WebSocket-Key");
server_key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1 sha;
@@ -556,67 +748,155 @@ public: //protected:
}
server_key = base64_encode(
reinterpret_cast<const unsigned char*>(message_digest),20);
reinterpret_cast<const unsigned char*>(message_digest),20
);
// set handshake accept headers
set_header("Sec-WebSocket-Accept",server_key);
set_header("Upgrade","websocket");
set_header("Connection","Upgrade");
m_response.replace_header("Sec-WebSocket-Accept",server_key);
m_response.set_header("Upgrade","websocket");
m_response.set_header("Connection","Upgrade");
} else {
log("Error computing handshake sha1 hash.",LOG_ERROR);
m_server_http_code = 500;
m_server_http_string = "";
m_response.set_status(http::status_code::INTERNAL_SERVER_ERROR);
}
}
// hardcoded server headers
set_header("Server","WebSocket++/2011-09-25");
h << "HTTP/1.1 " << m_server_http_code << " "
<< (m_server_http_string != "" ? m_server_http_string :
lookup_http_error_string(m_server_http_code))
<< "\r\n";
header_list::iterator it;
for (it = m_server_headers.begin(); it != m_server_headers.end(); it++) {
h << it->first << ": " << it->second << "\r\n";
if (m_subprotocol != "") {
m_response.replace_header("Sec-WebSocket-Protocol",m_subprotocol);
}
h << "\r\n";
// TODO: return negotiated extensions
m_raw_server_handshake = h.str();
// hardcoded server headers
// TODO: make this configurable
m_response.replace_header("Server","WebSocket++/2011-10-31");
access_log(m_response.raw(),ALOG_HANDSHAKE);
// start async write to handle_write_handshake
boost::asio::async_write(
m_socket,
boost::asio::buffer(m_raw_server_handshake),
boost::asio::buffer(m_response.raw()),
boost::bind(
&session_type::handle_write_handshake,
&session_type::handle_write_response,
session_type::shared_from_this(),
boost::asio::placeholders::error
)
);
}
// TODO: endpoint specific
//virtual void read_handshake() = 0;
void read_handshake() {
m_timer.expires_from_now(boost::posix_time::seconds(5));
//
void handle_write_response(const boost::system::error_code& error) {
if (error) {
log_error("Error writing handshake response",error);
drop_tcp();
return;
}
m_timer.async_wait(
log_open_result();
if (m_response.status_code() != http::status_code::SWITCHING_PROTOCOLS) {
std::stringstream err;
err << "Handshake ended with HTTP error: " << m_response.status_code()
<< " " << m_response.status_code();
log(err.str(),LOG_ERROR);
drop_tcp();
// TODO: tell client that connection failed?
// use on_fail?
return;
}
m_state = state::OPEN;
// stop the handshake timer
m_timer.cancel();
if (m_local_interface) {
m_local_interface->on_open(session_type::shared_from_this());
}
reset_message();
this->read_frame();
}
// ****** Write Handshake Thread **********************
// write_request -> handle_write_request ->
// read_response > handle_read_response ->
// read frame
// ****************************************************
void write_request() {
m_request.set_method("GET");
m_request.set_uri(m_uri.resource);
m_request.set_version("HTTP/1.1");
// Set request headers
m_request.set_header("Upgrade","websocket");
m_request.set_header("Connection","Upgrade");
m_request.replace_header("Sec-WebSocket-Version","13");
m_version = 13;
std::stringstream host;
if (m_uri.port == (m_secure ? 443 : 80)) {
host << m_uri.host;
} else {
host << m_uri.host << ":" << m_uri.port;
}
m_request.replace_header("Host",host.str());
if (m_origin != "") {
m_request.replace_header("Origin",m_origin);
}
std::string client_key;
int32_t raw_key[4];
/*boost::random::random_device rng;
boost::random::variate_generator<boost::random::random_device&, boost::random::uniform_int_distribution<> > gen(rng, boost::random::uniform_int_distribution<>(INT32_MIN,INT32_MAX));*/
for (int i = 0; i < 4; i++) {
raw_key[i] = m_endpoint->get_rng().gen();
}
client_key = base64_encode(reinterpret_cast<unsigned char const*>(raw_key), 16);
access_log("Client key chosen: "+client_key, ALOG_HANDSHAKE);
m_request.replace_header("Sec-WebSocket-Key",client_key);
m_request.replace_header("User Agent","WebSocket++/2011-10-31");
// start async write to write the request
boost::asio::async_write(
m_socket,
boost::asio::buffer(m_request.raw()),
boost::bind(
&session_type::handle_handshake_expired,
&session_type::handle_write_request,
session_type::shared_from_this(),
boost::asio::placeholders::error
)
);
}
void handle_write_request(const boost::system::error_code& error) {
if (error) {
log_error("Error writing HTTP request",error);
drop_tcp();
return;
}
read_response();
}
void read_response() {
boost::asio::async_read_until(
m_socket,
m_buf,
"\r\n\r\n",
boost::bind(
&session_type::handle_read_handshake,
&session_type::handle_read_response,
session_type::shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
@@ -624,6 +904,10 @@ public: //protected:
);
}
void handle_read_response(const boost::system::error_code& e,std::size_t bytes_transferred) {
}
void read_frame() {
// the initial read in the handshake may have read in the first frame.
// handle it (if it exists) before we read anything else.
@@ -1053,7 +1337,6 @@ public: //protected:
// reset session for a new message
void reset_message() {
m_error = false;
m_fragmented = false;
m_current_message.clear();
@@ -1088,8 +1371,8 @@ public: //protected:
msg << "[Connection " << this << "] "
<< m_socket.remote_endpoint()
<< " v" << m_version << " "
<< (get_client_header("User-Agent") == "" ? "NULL" : get_client_header("User-Agent"))
<< " " << m_resource << " " << m_server_http_code;
<< (get_request_header("User-Agent") == "" ? "NULL" : get_request_header("User-Agent"))
<< " " << m_uri.resource << " " << m_response.status_code();
access_log(msg.str(),ALOG_HANDSHAKE);
}
@@ -1116,6 +1399,7 @@ public: //protected:
return false;
}
void send_close(close::status::value status,const std::string& reason) {
if (m_state != state::OPEN) {
log("Tried to disconnect a session that wasn't open",LOG_WARN);
@@ -1187,27 +1471,19 @@ private:
}
}
protected:
// Immutable state about the current connection from the handshake
// Client handshake
std::string m_raw_client_handshake;
std::string m_client_http_request;
std::string m_resource;
std::string m_client_origin;
header_list m_client_headers;
std::vector<std::string> m_client_subprotocols;
std::vector<std::string> m_client_extensions;
http::parser::request m_request;
http::parser::response m_response;
// some settings about the connection
std::vector<std::string> m_requested_subprotocols;
std::vector<std::string> m_requested_extensions;
std::string m_subprotocol;
std::vector<std::string> m_extensions;
std::string m_origin;
unsigned int m_version;
// Server handshake
std::string m_raw_server_handshake;
std::string m_server_http_request;
header_list m_server_headers;
std::string m_server_subprotocol;
std::vector<std::string> m_server_extensions;
uint16_t m_server_http_code;
std::string m_server_http_string;
bool m_secure;
ws_uri m_uri;
// Mutable connection state;
uint8_t m_state;
bool m_writing;
@@ -1241,13 +1517,8 @@ protected:
// frame parsers
frame::parser<typename endpoint_policy::rng_t> m_read_frame;
frame::parser<typename endpoint_policy::rng_t> m_write_frame;
// unknown
bool m_error;
};
}
#endif // WEBSOCKET_SESSION_HPP

View File

@@ -23,8 +23,6 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This Makefile was derived from a similar one included in the libjson project
* It's authors were Jonathan Wallace and Bernhard Fluehmann.
*/
#ifndef WEBSOCKETPP_HPP

40
test/basic/logging.cpp Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright (c) 2011, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "../../src/logger/logger.hpp"
int main () {
websocketpp::log::logger<websocketpp::log::elog_level::value> log;
log.set_level(websocketpp::log::elog_level::ALL);
log.at(websocketpp::log::elog_level::DEBUG) << "debug: " << 5 << websocketpp::log::endl;
log.at(websocketpp::log::elog_level::INFO) << "info: " << 5 << websocketpp::log::endl;
log.at(websocketpp::log::elog_level::WARN) << "warn: " << 5 << websocketpp::log::endl;
log.at(websocketpp::log::elog_level::ERROR) << "error: " << 5 << websocketpp::log::endl;
log.at(websocketpp::log::elog_level::FATAL) << "fatal: " << 5 << websocketpp::log::endl;
}

View File

@@ -135,6 +135,8 @@
B61387A3145D846400ED9B19 /* boost_rng.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = boost_rng.hpp; path = src/rng/boost_rng.hpp; sourceTree = "<group>"; };
B61387A5145D849E00ED9B19 /* constants.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = constants.hpp; path = src/http/constants.hpp; sourceTree = "<group>"; };
B61387A6145D849E00ED9B19 /* parser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = parser.hpp; path = src/http/parser.hpp; sourceTree = "<group>"; };
B61387B31462AD4900ED9B19 /* session_handler_interface.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = session_handler_interface.hpp; path = src/session_handler_interface.hpp; sourceTree = "<group>"; };
B61387B61462B35700ED9B19 /* logger.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = logger.hpp; path = src/logger/logger.hpp; sourceTree = "<group>"; };
B6828875143745DA002BA48B /* chat_client_handler.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client_handler.cpp; path = examples/chat_client/chat_client_handler.cpp; sourceTree = "<group>"; };
B6828876143745DA002BA48B /* chat_client_handler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = chat_client_handler.hpp; path = examples/chat_client/chat_client_handler.hpp; sourceTree = "<group>"; };
B6828877143745DA002BA48B /* chat_client.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = chat_client.cpp; path = examples/chat_client/chat_client.cpp; sourceTree = "<group>"; };
@@ -250,6 +252,14 @@
name = http;
sourceTree = "<group>";
};
B61387B51462B34400ED9B19 /* logger */ = {
isa = PBXGroup;
children = (
B61387B61462B35700ED9B19 /* logger.hpp */,
);
name = logger;
sourceTree = "<group>";
};
B6CF18121437C370009295BE /* echo_client */ = {
isa = PBXGroup;
children = (
@@ -295,6 +305,7 @@
B6DF1C7F1434ABB70029A1B1 /* src */ = {
isa = PBXGroup;
children = (
B61387B51462B34400ED9B19 /* logger */,
B61387A4145D847A00ED9B19 /* http */,
B6FE8D09145B0F7400B32547 /* rng */,
B6FE8D05145AFF5F00B32547 /* websocket_constants.hpp */,
@@ -302,6 +313,7 @@
B6DF1C931434AC470029A1B1 /* websocket_client_session.hpp */,
B6DF1C941434AC470029A1B1 /* websocket_client.cpp */,
B6DF1C951434AC470029A1B1 /* websocket_client.hpp */,
B61387B31462AD4900ED9B19 /* session_handler_interface.hpp */,
B6DF1C961434AC470029A1B1 /* websocket_connection_handler.hpp */,
B6BE76E9144EF53000716A77 /* websocket_endpoint.hpp */,
B6DF1C971434AC470029A1B1 /* websocket_frame.cpp */,