1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-12-20 06:57:19 +01:00

Initial preparations for CURL and Discord integration.

This commit is contained in:
Sandu Liviu Catalin
2021-01-27 07:27:48 +02:00
parent 8257eb61d6
commit 95705e87c8
1751 changed files with 440547 additions and 854 deletions

View File

@@ -0,0 +1,299 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP
#define WEBSOCKETPP_PROCESSOR_BASE_HPP
#include <websocketpp/close.hpp>
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
namespace processor {
/// Constants related to processing WebSocket connections
namespace constants {
static char const upgrade_token[] = "websocket";
static char const connection_token[] = "Upgrade";
static char const handshake_guid[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
} // namespace constants
/// Processor class related error codes
namespace error_cat {
enum value {
BAD_REQUEST = 0, // Error was the result of improperly formatted user input
INTERNAL_ERROR = 1, // Error was a logic error internal to WebSocket++
PROTOCOL_VIOLATION = 2,
MESSAGE_TOO_BIG = 3,
PAYLOAD_VIOLATION = 4 // Error was due to receiving invalid payload data
};
} // namespace error_cat
/// Error code category and codes used by all processor types
namespace error {
enum processor_errors {
/// Catch-all error for processor policy errors that don't fit in other
/// categories
general = 1,
/// Error was the result of improperly formatted user input
bad_request,
/// Processor encountered a protocol violation in an incoming message
protocol_violation,
/// Processor encountered a message that was too large
message_too_big,
/// Processor encountered invalid payload data.
invalid_payload,
/// The processor method was called with invalid arguments
invalid_arguments,
/// Opcode was invalid for requested operation
invalid_opcode,
/// Control frame too large
control_too_big,
/// Illegal use of reserved bit
invalid_rsv_bit,
/// Fragmented control message
fragmented_control,
/// Continuation without message
invalid_continuation,
/// Clients may not send unmasked frames
masking_required,
/// Servers may not send masked frames
masking_forbidden,
/// Payload length not minimally encoded
non_minimal_encoding,
/// Not supported on 32 bit systems
requires_64bit,
/// Invalid UTF-8 encoding
invalid_utf8,
/// Operation required not implemented functionality
not_implemented,
/// Invalid HTTP method
invalid_http_method,
/// Invalid HTTP version
invalid_http_version,
/// Invalid HTTP status
invalid_http_status,
/// Missing Required Header
missing_required_header,
/// Embedded SHA-1 library error
sha1_library,
/// No support for this feature in this protocol version.
no_protocol_support,
/// Reserved close code used
reserved_close_code,
/// Invalid close code used
invalid_close_code,
/// Using a reason requires a close code
reason_requires_code,
/// Error parsing subprotocols
subprotocol_parse_error,
/// Error parsing extensions
extension_parse_error,
/// Extension related operation was ignored because extensions are disabled
extensions_disabled,
/// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes
/// after the handshake. Less than 8 bytes were read.
short_key3
};
/// Category for processor errors
class processor_category : public lib::error_category {
public:
processor_category() {}
char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
return "websocketpp.processor";
}
std::string message(int value) const {
switch(value) {
case error::general:
return "Generic processor error";
case error::bad_request:
return "invalid user input";
case error::protocol_violation:
return "Generic protocol violation";
case error::message_too_big:
return "A message was too large";
case error::invalid_payload:
return "A payload contained invalid data";
case error::invalid_arguments:
return "invalid function arguments";
case error::invalid_opcode:
return "invalid opcode";
case error::control_too_big:
return "Control messages are limited to fewer than 125 characters";
case error::invalid_rsv_bit:
return "Invalid use of reserved bits";
case error::fragmented_control:
return "Control messages cannot be fragmented";
case error::invalid_continuation:
return "Invalid message continuation";
case error::masking_required:
return "Clients may not send unmasked frames";
case error::masking_forbidden:
return "Servers may not send masked frames";
case error::non_minimal_encoding:
return "Payload length was not minimally encoded";
case error::requires_64bit:
return "64 bit frames are not supported on 32 bit systems";
case error::invalid_utf8:
return "Invalid UTF8 encoding";
case error::not_implemented:
return "Operation required not implemented functionality";
case error::invalid_http_method:
return "Invalid HTTP method.";
case error::invalid_http_version:
return "Invalid HTTP version.";
case error::invalid_http_status:
return "Invalid HTTP status.";
case error::missing_required_header:
return "A required HTTP header is missing";
case error::sha1_library:
return "SHA-1 library error";
case error::no_protocol_support:
return "The WebSocket protocol version in use does not support this feature";
case error::reserved_close_code:
return "Reserved close code used";
case error::invalid_close_code:
return "Invalid close code used";
case error::reason_requires_code:
return "Using a close reason requires a valid close code";
case error::subprotocol_parse_error:
return "Error parsing subprotocol header";
case error::extension_parse_error:
return "Error parsing extension header";
case error::extensions_disabled:
return "Extensions are disabled";
case error::short_key3:
return "Short Hybi00 Key 3 read";
default:
return "Unknown";
}
}
};
/// Get a reference to a static copy of the processor error category
inline lib::error_category const & get_processor_category() {
static processor_category instance;
return instance;
}
/// Create an error code with the given value and the processor category
inline lib::error_code make_error_code(error::processor_errors e) {
return lib::error_code(static_cast<int>(e), get_processor_category());
}
/// Converts a processor error_code into a websocket close code
/**
* Looks up the appropriate WebSocket close code that should be sent after an
* error of this sort occurred.
*
* If the error is not in the processor category close::status::blank is
* returned.
*
* If the error isn't normally associated with reasons to close a connection
* (such as errors intended to be used internally or delivered to client
* applications, ex: invalid arguments) then
* close::status::internal_endpoint_error is returned.
*/
inline close::status::value to_ws(lib::error_code ec) {
if (ec.category() != get_processor_category()) {
return close::status::blank;
}
switch (ec.value()) {
case error::protocol_violation:
case error::control_too_big:
case error::invalid_opcode:
case error::invalid_rsv_bit:
case error::fragmented_control:
case error::invalid_continuation:
case error::masking_required:
case error::masking_forbidden:
case error::reserved_close_code:
case error::invalid_close_code:
return close::status::protocol_error;
case error::invalid_payload:
case error::invalid_utf8:
return close::status::invalid_payload;
case error::message_too_big:
return close::status::message_too_big;
default:
return close::status::internal_endpoint_error;
}
}
} // namespace error
} // namespace processor
} // namespace websocketpp
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
template<> struct is_error_code_enum<websocketpp::processor::error::processor_errors>
{
static bool const value = true;
};
_WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
#endif //WEBSOCKETPP_PROCESSOR_BASE_HPP

View File

@@ -0,0 +1,462 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#include <websocketpp/frame.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/common/md5.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/processors/processor.hpp>
#include <algorithm>
#include <cstdlib>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
/// Processor for Hybi Draft version 00
/**
* There are many differences between Hybi 00 and Hybi 13
*/
template <typename config>
class hybi00 : public processor<config> {
public:
typedef processor<config> base;
typedef typename config::request_type request_type;
typedef typename config::response_type response_type;
typedef typename config::message_type message_type;
typedef typename message_type::ptr message_ptr;
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager)
: processor<config>(secure, p_is_server)
, msg_hdr(0x00)
, msg_ftr(0xff)
, m_state(HEADER)
, m_msg_manager(manager) {}
int get_version() const {
return 0;
}
lib::error_code validate_handshake(request_type const & r) const {
if (r.get_method() != "GET") {
return make_error_code(error::invalid_http_method);
}
if (r.get_version() != "HTTP/1.1") {
return make_error_code(error::invalid_http_version);
}
// required headers
// Host is required by HTTP/1.1
// Connection is required by is_websocket_handshake
// Upgrade is required by is_websocket_handshake
if (r.get_header("Sec-WebSocket-Key1").empty() ||
r.get_header("Sec-WebSocket-Key2").empty() ||
r.get_header("Sec-WebSocket-Key3").empty())
{
return make_error_code(error::missing_required_header);
}
return lib::error_code();
}
lib::error_code process_handshake(request_type const & req,
std::string const & subprotocol, response_type & res) const
{
char key_final[16];
// copy key1 into final key
decode_client_key(req.get_header("Sec-WebSocket-Key1"), &key_final[0]);
// copy key2 into final key
decode_client_key(req.get_header("Sec-WebSocket-Key2"), &key_final[4]);
// copy key3 into final key
// key3 should be exactly 8 bytes. If it is more it will be truncated
// if it is less the final key will almost certainly be wrong.
// TODO: decide if it is best to silently fail here or produce some sort
// of warning or exception.
std::string const & key3 = req.get_header("Sec-WebSocket-Key3");
std::copy(key3.c_str(),
key3.c_str()+(std::min)(static_cast<size_t>(8), key3.size()),
&key_final[8]);
res.append_header(
"Sec-WebSocket-Key3",
md5::md5_hash_string(std::string(key_final,16))
);
res.append_header("Upgrade","WebSocket");
res.append_header("Connection","Upgrade");
// Echo back client's origin unless our local application set a
// more restrictive one.
if (res.get_header("Sec-WebSocket-Origin").empty()) {
res.append_header("Sec-WebSocket-Origin",req.get_header("Origin"));
}
// Echo back the client's request host unless our local application
// set a different one.
if (res.get_header("Sec-WebSocket-Location").empty()) {
uri_ptr uri = get_uri(req);
res.append_header("Sec-WebSocket-Location",uri->str());
}
if (!subprotocol.empty()) {
res.replace_header("Sec-WebSocket-Protocol",subprotocol);
}
return lib::error_code();
}
/// Fill in a set of request headers for a client connection request
/**
* The Hybi 00 processor only implements incoming connections so this will
* always return an error.
*
* @param [out] req Set of headers to fill in
* @param [in] uri The uri being connected to
* @param [in] subprotocols The list of subprotocols to request
*/
lib::error_code client_handshake_request(request_type &, uri_ptr,
std::vector<std::string> const &) const
{
return error::make_error_code(error::no_protocol_support);
}
/// Validate the server's response to an outgoing handshake request
/**
* The Hybi 00 processor only implements incoming connections so this will
* always return an error.
*
* @param req The original request sent
* @param res The reponse to generate
* @return An error code, 0 on success, non-zero for other errors
*/
lib::error_code validate_server_handshake_response(request_type const &,
response_type &) const
{
return error::make_error_code(error::no_protocol_support);
}
std::string get_raw(response_type const & res) const {
response_type temp = res;
temp.remove_header("Sec-WebSocket-Key3");
return temp.raw() + res.get_header("Sec-WebSocket-Key3");
}
std::string const & get_origin(request_type const & r) const {
return r.get_header("Origin");
}
/// Extracts requested subprotocols from a handshake request
/**
* hybi00 does support subprotocols
* https://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-1.9
*
* @param [in] req The request to extract from
* @param [out] subprotocol_list A reference to a vector of strings to store
* the results in.
*/
lib::error_code extract_subprotocols(request_type const & req,
std::vector<std::string> & subprotocol_list)
{
if (!req.get_header("Sec-WebSocket-Protocol").empty()) {
http::parameter_list p;
if (!req.get_header_as_plist("Sec-WebSocket-Protocol",p)) {
http::parameter_list::const_iterator it;
for (it = p.begin(); it != p.end(); ++it) {
subprotocol_list.push_back(it->first);
}
} else {
return error::make_error_code(error::subprotocol_parse_error);
}
}
return lib::error_code();
}
uri_ptr get_uri(request_type const & request) const {
std::string h = request.get_header("Host");
size_t last_colon = h.rfind(":");
size_t last_sbrace = h.rfind("]");
// no : = hostname with no port
// last : before ] = ipv6 literal with no port
// : with no ] = hostname with port
// : after ] = ipv6 literal with port
if (last_colon == std::string::npos ||
(last_sbrace != std::string::npos && last_sbrace > last_colon))
{
return lib::make_shared<uri>(base::m_secure, h, request.get_uri());
} else {
return lib::make_shared<uri>(base::m_secure,
h.substr(0,last_colon),
h.substr(last_colon+1),
request.get_uri());
}
// TODO: check if get_uri is a full uri
}
/// Get hybi00 handshake key3
/**
* @todo This doesn't appear to be used anymore. It might be able to be
* removed
*/
std::string get_key3() const {
return "";
}
/// Process new websocket connection bytes
size_t consume(uint8_t * buf, size_t len, lib::error_code & ec) {
// if in state header we are expecting a 0x00 byte, if we don't get one
// it is a fatal error
size_t p = 0; // bytes processed
size_t l = 0;
ec = lib::error_code();
while (p < len) {
if (m_state == HEADER) {
if (buf[p] == msg_hdr) {
p++;
m_msg_ptr = m_msg_manager->get_message(frame::opcode::text,1);
if (!m_msg_ptr) {
ec = make_error_code(websocketpp::error::no_incoming_buffers);
m_state = FATAL_ERROR;
} else {
m_state = PAYLOAD;
}
} else {
ec = make_error_code(error::protocol_violation);
m_state = FATAL_ERROR;
}
} else if (m_state == PAYLOAD) {
uint8_t *it = std::find(buf+p,buf+len,msg_ftr);
// 0 1 2 3 4 5
// 0x00 0x23 0x23 0x23 0xff 0xXX
// Copy payload bytes into message
l = static_cast<size_t>(it-(buf+p));
m_msg_ptr->append_payload(buf+p,l);
p += l;
if (it != buf+len) {
// message is done, copy it and the trailing
p++;
// TODO: validation
m_state = READY;
}
} else {
// TODO
break;
}
}
// If we get one, we create a new message and move to application state
// if in state application we are copying bytes into the output message
// and validating them for UTF8 until we hit a 0xff byte. Once we hit
// 0x00, the message is complete and is dispatched. Then we go back to
// header state.
//ec = make_error_code(error::not_implemented);
return p;
}
bool ready() const {
return (m_state == READY);
}
bool get_error() const {
return false;
}
message_ptr get_message() {
message_ptr ret = m_msg_ptr;
m_msg_ptr = message_ptr();
m_state = HEADER;
return ret;
}
/// Prepare a message for writing
/**
* Performs validation, masking, compression, etc. will return an error if
* there was an error, otherwise msg will be ready to be written
*/
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out)
{
if (!in || !out) {
return make_error_code(error::invalid_arguments);
}
// TODO: check if the message is prepared already
// validate opcode
if (in->get_opcode() != frame::opcode::text) {
return make_error_code(error::invalid_opcode);
}
std::string& i = in->get_raw_payload();
//std::string& o = out->get_raw_payload();
// validate payload utf8
if (!utf8_validator::validate(i)) {
return make_error_code(error::invalid_payload);
}
// generate header
out->set_header(std::string(reinterpret_cast<char const *>(&msg_hdr),1));
// process payload
out->set_payload(i);
out->append_payload(std::string(reinterpret_cast<char const *>(&msg_ftr),1));
// hybi00 doesn't support compression
// hybi00 doesn't have masking
out->set_prepared(true);
return lib::error_code();
}
/// Prepare a ping frame
/**
* Hybi 00 doesn't support pings so this will always return an error
*
* @param in The string to use for the ping payload
* @param out The message buffer to prepare the ping in.
* @return Status code, zero on success, non-zero on failure
*/
lib::error_code prepare_ping(std::string const &, message_ptr) const
{
return lib::error_code(error::no_protocol_support);
}
/// Prepare a pong frame
/**
* Hybi 00 doesn't support pongs so this will always return an error
*
* @param in The string to use for the pong payload
* @param out The message buffer to prepare the pong in.
* @return Status code, zero on success, non-zero on failure
*/
lib::error_code prepare_pong(std::string const &, message_ptr) const
{
return lib::error_code(error::no_protocol_support);
}
/// Prepare a close frame
/**
* Hybi 00 doesn't support the close code or reason so these parameters are
* ignored.
*
* @param code The close code to send
* @param reason The reason string to send
* @param out The message buffer to prepare the fame in
* @return Status code, zero on success, non-zero on failure
*/
lib::error_code prepare_close(close::status::value, std::string const &,
message_ptr out) const
{
if (!out) {
return lib::error_code(error::invalid_arguments);
}
std::string val;
val.append(1,'\xff');
val.append(1,'\x00');
out->set_payload(val);
out->set_prepared(true);
return lib::error_code();
}
private:
void decode_client_key(std::string const & key, char * result) const {
unsigned int spaces = 0;
std::string digits;
uint32_t num;
// key2
for (size_t i = 0; i < key.size(); i++) {
if (key[i] == ' ') {
spaces++;
} else if (key[i] >= '0' && key[i] <= '9') {
digits += key[i];
}
}
num = static_cast<uint32_t>(strtoul(digits.c_str(), NULL, 10));
if (spaces > 0 && num > 0) {
num = htonl(num/spaces);
std::copy(reinterpret_cast<char*>(&num),
reinterpret_cast<char*>(&num)+4,
result);
} else {
std::fill(result,result+4,0);
}
}
enum state {
HEADER = 0,
PAYLOAD = 1,
READY = 2,
FATAL_ERROR = 3
};
uint8_t const msg_hdr;
uint8_t const msg_ftr;
state m_state;
msg_manager_ptr m_msg_manager;
message_ptr m_msg_ptr;
utf8_validator::validator m_validator;
};
} // namespace processor
} // namespace websocketpp
#endif //WEBSOCKETPP_PROCESSOR_HYBI00_HPP

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_PROCESSOR_HYBI07_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI07_HPP
#include <websocketpp/processors/hybi08.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
/// Processor for Hybi Draft version 07
/**
* The primary difference between 07 and 08 is a version number.
*/
template <typename config>
class hybi07 : public hybi08<config> {
public:
typedef typename config::request_type request_type;
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi08<config>(secure, p_is_server, manager, rng) {}
/// Fill in a set of request headers for a client connection request
/**
* The Hybi 07 processor only implements incoming connections so this will
* always return an error.
*
* @param [out] req Set of headers to fill in
* @param [in] uri The uri being connected to
* @param [in] subprotocols The list of subprotocols to request
*/
lib::error_code client_handshake_request(request_type &, uri_ptr,
std::vector<std::string> const &) const
{
return error::make_error_code(error::no_protocol_support);
}
int get_version() const {
return 7;
}
private:
};
} // namespace processor
} // namespace websocketpp
#endif //WEBSOCKETPP_PROCESSOR_HYBI07_HPP

View File

@@ -0,0 +1,83 @@
/*
* Copyright (c) 2014, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_PROCESSOR_HYBI08_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI08_HPP
#include <websocketpp/processors/hybi13.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
/// Processor for Hybi Draft version 08
/**
* The primary difference between 08 and 13 is a different origin header name
*/
template <typename config>
class hybi08 : public hybi13<config> {
public:
typedef hybi08<config> type;
typedef typename config::request_type request_type;
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi13<config>(secure, p_is_server, manager, rng) {}
/// Fill in a set of request headers for a client connection request
/**
* The Hybi 08 processor only implements incoming connections so this will
* always return an error.
*
* @param [out] req Set of headers to fill in
* @param [in] uri The uri being connected to
* @param [in] subprotocols The list of subprotocols to request
*/
lib::error_code client_handshake_request(request_type &, uri_ptr,
std::vector<std::string> const &) const
{
return error::make_error_code(error::no_protocol_support);
}
int get_version() const {
return 8;
}
std::string const & get_origin(request_type const & r) const {
return r.get_header("Sec-WebSocket-Origin");
}
private:
};
} // namespace processor
} // namespace websocketpp
#endif //WEBSOCKETPP_PROCESSOR_HYBI08_HPP

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,407 @@
/*
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * 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_PROCESSOR_HPP
#define WEBSOCKETPP_PROCESSOR_HPP
#include <websocketpp/processors/base.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/close.hpp>
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace websocketpp {
/// Processors encapsulate the protocol rules specific to each WebSocket version
/**
* The processors namespace includes a number of free functions that operate on
* various WebSocket related data structures and perform processing that is not
* related to specific versions of the protocol.
*
* It also includes the abstract interface for the protocol specific processing
* engines. These engines wrap all of the logic necessary for parsing and
* validating WebSocket handshakes and messages of specific protocol version
* and set of allowed extensions.
*
* An instance of a processor represents the state of a single WebSocket
* connection of the associated version. One processor instance is needed per
* logical WebSocket connection.
*/
namespace processor {
/// Determine whether or not a generic HTTP request is a WebSocket handshake
/**
* @param r The HTTP request to read.
*
* @return True if the request is a WebSocket handshake, false otherwise
*/
template <typename request_type>
bool is_websocket_handshake(request_type& r) {
using utility::ci_find_substr;
std::string const & upgrade_header = r.get_header("Upgrade");
if (ci_find_substr(upgrade_header, constants::upgrade_token,
sizeof(constants::upgrade_token)-1) == upgrade_header.end())
{
return false;
}
std::string const & con_header = r.get_header("Connection");
if (ci_find_substr(con_header, constants::connection_token,
sizeof(constants::connection_token)-1) == con_header.end())
{
return false;
}
return true;
}
/// Extract the version from a WebSocket handshake request
/**
* A blank version header indicates a spec before versions were introduced.
* The only such versions in shipping products are Hixie Draft 75 and Hixie
* Draft 76. Draft 75 is present in Chrome 4-5 and Safari 5.0.0, Draft 76 (also
* known as hybi 00 is present in Chrome 6-13 and Safari 5.0.1+. As
* differentiating between these two sets of browsers is very difficult and
* Safari 5.0.1+ accounts for the vast majority of cases in the wild this
* function assumes that all handshakes without a valid version header are
* Hybi 00.
*
* @param r The WebSocket handshake request to read.
*
* @return The WebSocket handshake version or -1 if there was an extraction
* error.
*/
template <typename request_type>
int get_websocket_version(request_type& r) {
if (!r.ready()) {
return -2;
}
if (r.get_header("Sec-WebSocket-Version").empty()) {
return 0;
}
int version;
std::istringstream ss(r.get_header("Sec-WebSocket-Version"));
if ((ss >> version).fail()) {
return -1;
}
return version;
}
/// Extract a URI ptr from the host header of the request
/**
* @param request The request to extract the Host header from.
*
* @param scheme The scheme under which this request was received (ws, wss,
* http, https, etc)
*
* @return A uri_pointer that encodes the value of the host header.
*/
template <typename request_type>
uri_ptr get_uri_from_host(request_type & request, std::string scheme) {
std::string h = request.get_header("Host");
size_t last_colon = h.rfind(":");
size_t last_sbrace = h.rfind("]");
// no : = hostname with no port
// last : before ] = ipv6 literal with no port
// : with no ] = hostname with port
// : after ] = ipv6 literal with port
if (last_colon == std::string::npos ||
(last_sbrace != std::string::npos && last_sbrace > last_colon))
{
return lib::make_shared<uri>(scheme, h, request.get_uri());
} else {
return lib::make_shared<uri>(scheme,
h.substr(0,last_colon),
h.substr(last_colon+1),
request.get_uri());
}
}
/// WebSocket protocol processor abstract base class
template <typename config>
class processor {
public:
typedef processor<config> type;
typedef typename config::request_type request_type;
typedef typename config::response_type response_type;
typedef typename config::message_type::ptr message_ptr;
typedef std::pair<lib::error_code,std::string> err_str_pair;
explicit processor(bool secure, bool p_is_server)
: m_secure(secure)
, m_server(p_is_server)
, m_max_message_size(config::max_message_size)
{}
virtual ~processor() {}
/// Get the protocol version of this processor
virtual int get_version() const = 0;
/// Get maximum message size
/**
* Get maximum message size. Maximum message size determines the point at which the
* processor will fail a connection with the message_too_big protocol error.
*
* The default is retrieved from the max_message_size value from the template config
*
* @since 0.3.0
*/
size_t get_max_message_size() const {
return m_max_message_size;
}
/// Set maximum message size
/**
* Set maximum message size. Maximum message size determines the point at which the
* processor will fail a connection with the message_too_big protocol error.
*
* The default is retrieved from the max_message_size value from the template config
*
* @since 0.3.0
*
* @param new_value The value to set as the maximum message size.
*/
void set_max_message_size(size_t new_value) {
m_max_message_size = new_value;
}
/// Returns whether or not the permessage_compress extension is implemented
/**
* Compile time flag that indicates whether this processor has implemented
* the permessage_compress extension. By default this is false.
*/
virtual bool has_permessage_compress() const {
return false;
}
/// Initializes extensions based on the Sec-WebSocket-Extensions header
/**
* Reads the Sec-WebSocket-Extensions header and determines if any of the
* requested extensions are supported by this processor. If they are their
* settings data is initialized and an extension string to send to the
* is returned.
*
* @param request The request or response headers to look at.
*/
virtual err_str_pair negotiate_extensions(request_type const &) {
return err_str_pair();
}
/// Initializes extensions based on the Sec-WebSocket-Extensions header
/**
* Reads the Sec-WebSocket-Extensions header and determines if any of the
* requested extensions were accepted by the server. If they are their
* settings data is initialized. If they are not a list of required
* extensions (if any) is returned. This list may be sent back to the server
* as a part of the 1010/Extension required close code.
*
* @param response The request or response headers to look at.
*/
virtual err_str_pair negotiate_extensions(response_type const &) {
return err_str_pair();
}
/// validate a WebSocket handshake request for this version
/**
* @param request The WebSocket handshake request to validate.
* is_websocket_handshake(request) must be true and
* get_websocket_version(request) must equal this->get_version().
*
* @return A status code, 0 on success, non-zero for specific sorts of
* failure
*/
virtual lib::error_code validate_handshake(request_type const & request) const = 0;
/// Calculate the appropriate response for this websocket request
/**
* @param req The request to process
*
* @param subprotocol The subprotocol in use
*
* @param res The response to store the processed response in
*
* @return An error code, 0 on success, non-zero for other errors
*/
virtual lib::error_code process_handshake(request_type const & req,
std::string const & subprotocol, response_type& res) const = 0;
/// Fill in an HTTP request for an outgoing connection handshake
/**
* @param req The request to process.
*
* @return An error code, 0 on success, non-zero for other errors
*/
virtual lib::error_code client_handshake_request(request_type & req,
uri_ptr uri, std::vector<std::string> const & subprotocols) const = 0;
/// Validate the server's response to an outgoing handshake request
/**
* @param req The original request sent
* @param res The reponse to generate
* @return An error code, 0 on success, non-zero for other errors
*/
virtual lib::error_code validate_server_handshake_response(request_type
const & req, response_type & res) const = 0;
/// Given a completed response, get the raw bytes to put on the wire
virtual std::string get_raw(response_type const & request) const = 0;
/// Return the value of the header containing the CORS origin.
virtual std::string const & get_origin(request_type const & request) const = 0;
/// Extracts requested subprotocols from a handshake request
/**
* Extracts a list of all subprotocols that the client has requested in the
* given opening handshake request.
*
* @param [in] req The request to extract from
* @param [out] subprotocol_list A reference to a vector of strings to store
* the results in.
*/
virtual lib::error_code extract_subprotocols(const request_type & req,
std::vector<std::string> & subprotocol_list) = 0;
/// Extracts client uri from a handshake request
virtual uri_ptr get_uri(request_type const & request) const = 0;
/// process new websocket connection bytes
/**
* WebSocket connections are a continous stream of bytes that must be
* interpreted by a protocol processor into discrete frames.
*
* @param buf Buffer from which bytes should be read.
* @param len Length of buffer
* @param ec Reference to an error code to return any errors in
* @return Number of bytes processed
*/
virtual size_t consume(uint8_t *buf, size_t len, lib::error_code & ec) = 0;
/// Checks if there is a message ready
/**
* Checks if the most recent consume operation processed enough bytes to
* complete a new WebSocket message. The message can be retrieved by calling
* get_message() which will reset the internal state to not-ready and allow
* consume to read more bytes.
*
* @return Whether or not a message is ready.
*/
virtual bool ready() const = 0;
/// Retrieves the most recently processed message
/**
* Retrieves a shared pointer to the recently completed message if there is
* one. If ready() returns true then there is a message available.
* Retrieving the message with get_message will reset the state of ready.
* As such, each new message may be retrieved only once. Calling get_message
* when there is no message available will result in a null pointer being
* returned.
*
* @return A pointer to the most recently processed message or a null shared
* pointer.
*/
virtual message_ptr get_message() = 0;
/// Tests whether the processor is in a fatal error state
virtual bool get_error() const = 0;
/// Retrieves the number of bytes presently needed by the processor
/// This value may be used as a hint to the transport layer as to how many
/// bytes to wait for before running consume again.
virtual size_t get_bytes_needed() const {
return 1;
}
/// Prepare a data message for writing
/**
* Performs validation, masking, compression, etc. will return an error if
* there was an error, otherwise msg will be ready to be written
*/
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
/// Prepare a ping frame
/**
* Ping preparation is entirely state free. There is no payload validation
* other than length. Payload need not be UTF-8.
*
* @param in The string to use for the ping payload
* @param out The message buffer to prepare the ping in.
* @return Status code, zero on success, non-zero on failure
*/
virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
= 0;
/// Prepare a pong frame
/**
* Pong preparation is entirely state free. There is no payload validation
* other than length. Payload need not be UTF-8.
*
* @param in The string to use for the pong payload
* @param out The message buffer to prepare the pong in.
* @return Status code, zero on success, non-zero on failure
*/
virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
= 0;
/// Prepare a close frame
/**
* Close preparation is entirely state free. The code and reason are both
* subject to validation. Reason must be valid UTF-8. Code must be a valid
* un-reserved WebSocket close code. Use close::status::no_status to
* indicate no code. If no code is supplied a reason may not be specified.
*
* @param code The close code to send
* @param reason The reason string to send
* @param out The message buffer to prepare the fame in
* @return Status code, zero on success, non-zero on failure
*/
virtual lib::error_code prepare_close(close::status::value code,
std::string const & reason, message_ptr out) const = 0;
protected:
bool const m_secure;
bool const m_server;
size_t m_max_message_size;
};
} // namespace processor
} // namespace websocketpp
#endif //WEBSOCKETPP_PROCESSOR_HPP