mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
http parser and logger work
This commit is contained in:
@@ -47,8 +47,6 @@
|
||||
#include <string>
|
||||
#include <queue>
|
||||
|
||||
using websocketpp::session_ptr;
|
||||
|
||||
namespace websocketecho {
|
||||
|
||||
class echo_client_handler : public websocketpp::connection_handler {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
147
src/logger/logger.hpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Peter Thorson. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the WebSocket++ Project nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef 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
|
||||
49
src/session_handler_interface.hpp
Normal file
49
src/session_handler_interface.hpp
Normal 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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
40
test/basic/logging.cpp
Normal 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;
|
||||
|
||||
}
|
||||
@@ -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 */,
|
||||
|
||||
Reference in New Issue
Block a user