#319, #320, c++11 mode, mysql errors to server log

This commit is contained in:
Albert Medela committed 2017-02-05 00:44:48 +01:00
1 parent 16c7200028
commit 19f95f6204
91 files changed
+4561 -2146

No files matched your search

+6 -5
View File
@@ -482,11 +482,11 @@ ServerDBThread::EstablishDBConnection()
if (!prepareNick.exec() || !prepareAvatarBlacklist.exec() || !prepareLogin.exec() || !prepareCreateGame.exec()
|| !prepareEndGame.exec() || !prepareRelation.exec() || !prepareScore.exec() || !prepareReportAvatar.exec()
|| !prepareReportGame.exec() || !prepareAdminPlayer.exec() || !prepareBlockPlayer.exec()) {
string tmpError = string(prepareNick.error()) + prepareAvatarBlacklist.error() + prepareLogin.error() + prepareCreateGame.error() +
prepareEndGame.error() + prepareRelation.error() + prepareScore.error() + prepareReportAvatar.error() +
prepareReportGame.error() + prepareAdminPlayer.error() + prepareBlockPlayer.error();
m_connData->conn.disconnect();
m_ioService->post(boost::bind(&ServerDBCallback::ConnectFailed, &m_callback,
string(prepareNick.error()) + prepareAvatarBlacklist.error() + prepareLogin.error() + prepareCreateGame.error()
+ prepareEndGame.error() + prepareRelation.error() + prepareScore.error() + prepareReportAvatar.error()
+ prepareReportGame.error() + prepareAdminPlayer.error() + prepareBlockPlayer.error()));
m_ioService->post(boost::bind(&ServerDBCallback::ConnectFailed, &m_callback, tmpError));
m_permanentError = true;
} else {
m_ioService->post(boost::bind(&ServerDBCallback::ConnectSuccess, &m_callback));
@@ -537,8 +537,9 @@ ServerDBThread::HandleNextQuery()
++i;
}
if (!paramQuery.exec()) {
string tmpError = paramQuery.error();
m_connData->conn.disconnect();
m_ioService->post(boost::bind(&ServerDBCallback::QueryError, &m_callback, paramQuery.error()));
m_ioService->post(boost::bind(&ServerDBCallback::QueryError, &m_callback, tmpError));
break;
}
}
+1 -1
View File
@@ -35,7 +35,7 @@
#ifdef ANDROID
#ifndef ANDROID_TEST
#include "QtGui/5.2.0/QtGui/qpa/qplatformnativeinterface.h"
#include "QtGui/5.7.1/QtGui/qpa/qplatformnativeinterface.h"
#include <jni.h>
#endif
#endif
@@ -275,6 +275,9 @@ int gameLobbyDialogImpl::exec()
registeredUserMode();
}
// Bug #320: https://github.com/pokerth/pokerth/issues/320
myChat->refreshIgnoreList();
#ifdef ANDROID
this->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
#endif
+1 -1
View File
@@ -69,7 +69,7 @@
#ifdef ANDROID
#ifndef ANDROID_TEST
#include "QtGui/5.2.0/QtGui/qpa/qplatformnativeinterface.h"
#include "QtGui/5.7.1/QtGui/qpa/qplatformnativeinterface.h"
#include <jni.h>
#endif
#endif
+13 -3
View File
@@ -156,9 +156,14 @@ void MyAvatarLabel::setPlayerRating(QString playerInfo)
std::list<std::string> result;
std::string separator="(!#$%)";
std::list<std::string>::iterator iterator;
// Bug #319: https://github.com/pokerth/pokerth/issues/319
QString playerName = playerInfoList.at(0);
playerName = QUrl::fromPercentEncoding(playerName.toUtf8());
for(iterator = tipsList.begin(); iterator != tipsList.end(); ++iterator) {
tipInfo=QString::fromUtf8(iterator->c_str()).split("(!#$%)", QString::KeepEmptyParts, Qt::CaseSensitive);
if(tipInfo.at(0)==playerInfoList.at(0)) {
if(tipInfo.at(0)==playerName) {
result.push_back(tipInfo.at(0).toUtf8().constData()+separator+tipInfo.at(1).toUtf8().constData()+separator+playerInfoList.at(1).toUtf8().constData()+separator);
found=1;
} else {
@@ -166,7 +171,7 @@ void MyAvatarLabel::setPlayerRating(QString playerInfo)
}
}
if(found==0) {
result.push_back(playerInfoList.at(0).toUtf8().constData()+separator+separator+playerInfoList.at(1).toUtf8().constData()+separator);
result.push_back(playerName.toUtf8().constData()+separator+separator+playerInfoList.at(1).toUtf8().constData()+separator);
}
myW->getMyConfig()->writeConfigStringList("PlayerTooltips", result);
myW->getMyConfig()->writeBuffer();
@@ -224,7 +229,12 @@ void MyAvatarLabel::refreshStars()
for(int i=1; i<=5; i++)myW->playerStarsArray[i][seatPlace]->setText("");
if(myW->myStartWindow->getSession()->getGameType() == Session::GAME_TYPE_INTERNET && !myW->getSession()->getClientPlayerInfo((*it_c)->getMyUniqueID()).isGuest && (*it_c)->getMyType() != PLAYER_TYPE_COMPUTER) {
if((*it_c)->getMyStayOnTableStatus() == true && (*it_c)->getMyName()!="" && seatPlace!=0) {
int playerStars=getPlayerRating(QString::fromUtf8((*it_c)->getMyName().c_str()));
// Bug #319: https://github.com/pokerth/pokerth/issues/319
QString playerName = QString::fromUtf8((*it_c)->getMyName().c_str());
int playerStars=getPlayerRating(playerName);
playerName = QString(QUrl::toPercentEncoding(playerName));
for(int i=1; i<=5; i++) {
myW->playerStarsArray[i][seatPlace]->setText("<a style='color: #"+myW->getMyGameTableStyle()->getRatingStarsColor()+"; "+fontFamily+" font-size: "+fontSize+"px; text-decoration: none;' href='"+QString::fromUtf8((*it_c)->getMyName().c_str())+"\""+QString::number(i)+"'>&#9734;</a>");
}
+1 -1
View File
@@ -61,7 +61,7 @@
#ifdef ANDROID
#ifndef ANDROID_TEST
#include "QtGui/5.2.0/QtGui/qpa/qplatformnativeinterface.h"
#include "QtGui/5.7.1/QtGui/qpa/qplatformnativeinterface.h"
#include <jni.h>
#endif
#endif
+5
View File
@@ -312,8 +312,13 @@ void
SessionData::CloseWebSocketHandle()
{
if (m_webData) {
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) // c++11
std::error_code std_ec;
m_webData->webSocketServer->close(m_webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", std_ec);
#else
boost::system::error_code ec;
m_webData->webSocketServer->close(m_webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", ec);
#endif
}
}
+13 -2
View File
@@ -57,9 +57,14 @@ void
WebSendBuffer::AsyncSendNextPacket(boost::shared_ptr<SessionData> session)
{
if (closeAfterSend) {
boost::system::error_code ec;
boost::shared_ptr<WebSocketData> webData = session->GetWebData();
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
std::error_code std_ec;
webData->webSocketServer->close(webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", std_ec);
#else
boost::system::error_code ec;
webData->webSocketServer->close(webData->webHandle, websocketpp::close::status::normal, "PokerTH server closed the connection.", ec);
#endif
}
}
@@ -70,10 +75,16 @@ WebSendBuffer::InternalStorePacket(boost::shared_ptr<SessionData> session, boost
google::protobuf::uint8 *buf = new google::protobuf::uint8[packetSize];
packet->GetMsg()->SerializeWithCachedSizesToArray(buf);
boost::system::error_code ec;
boost::shared_ptr<WebSocketData> webData = session->GetWebData();
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L)
std::error_code std_ec;
webData->webSocketServer->send(webData->webHandle, string((const char *)buf, packetSize), websocketpp::frame::opcode::BINARY, std_ec);
if (std_ec) {
#else
boost::system::error_code ec;
webData->webSocketServer->send(webData->webHandle, string((const char *)buf, packetSize), websocketpp::frame::opcode::BINARY, ec);
if (ec) {
#endif
SetCloseAfterSend();
}
+4 -2
View File
@@ -49,9 +49,11 @@ public:
virtual void Close();
protected:
#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) // c++11
typedef std::map<websocketpp::connection_hdl, boost::weak_ptr<SessionData>, std::owner_less<websocketpp::connection_hdl> > SessionMap;
#else
typedef std::map<websocketpp::connection_hdl, boost::weak_ptr<SessionData> > SessionMap;
#endif
bool validate(websocketpp::connection_hdl hdl);
void on_open(websocketpp::connection_hdl hdl);
void on_close(websocketpp::connection_hdl hdl);
+1 -1
View File
@@ -1,6 +1,6 @@
Main Library:
Copyright (c) 2013, Peter Thorson. All rights reserved.
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:
+35 -13
View File
@@ -45,6 +45,11 @@ static std::string const base64_chars =
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/// Test whether a character is a valid base64 character
/**
* @param c The character to test
* @return true if c is a valid base64 character
*/
static inline bool is_base64(unsigned char c) {
return (c == 43 || // +
(c >= 47 && c <= 57) || // /-9
@@ -52,17 +57,21 @@ static inline bool is_base64(unsigned char c) {
(c >= 97 && c <= 122)); // a-z
}
inline std::string base64_encode(unsigned char const * bytes_to_encode, unsigned
int in_len)
{
/// Encode a char buffer into a base64 string
/**
* @param input The input data
* @param len The length of input in bytes
* @return A base64 encoded string representing input
*/
inline std::string base64_encode(unsigned char const * input, size_t len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
while (len--) {
char_array_3[i++] = *(input++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) +
@@ -97,25 +106,38 @@ inline std::string base64_encode(unsigned char const * bytes_to_encode, unsigned
while((i++ < 3)) {
ret += '=';
}
}
}
return ret;
return ret;
}
inline std::string base64_encode(std::string const & data) {
return base64_encode(reinterpret_cast<const unsigned char *>(data.data()),data.size());
/// Encode a string into a base64 string
/**
* @param input The input data
* @return A base64 encoded string representing input
*/
inline std::string base64_encode(std::string const & input) {
return base64_encode(
reinterpret_cast<const unsigned char *>(input.data()),
input.size()
);
}
inline std::string base64_decode(std::string const & encoded_string) {
size_t in_len = encoded_string.size();
/// Decode a base64 encoded string into a string of raw bytes
/**
* @param input The base64 encoded input data
* @return A string representing the decoded raw bytes
*/
inline std::string base64_decode(std::string const & input) {
size_t in_len = input.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
while (in_len-- && ( input[in_] != '=') && is_base64(input[in_])) {
char_array_4[i++] = input[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++) {
char_array_4[i] = static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+61 -2
View File
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -150,6 +150,21 @@ namespace status {
* illegal on the wire.
*/
static value const tls_handshake = 1015;
/// A generic subprotocol error
/**
* Indicates that a subprotocol error occurred. Typically this involves
* receiving a message that is not formatted as a valid message for the
* subprotocol in use.
*/
static value const subprotocol_error = 3000;
/// A invalid subprotocol data
/**
* Indicates that data was received that violated the specification of the
* subprotocol in use.
*/
static value const invalid_subprotocol_data = 3001;
/// First value in range reserved for future protocol use
static value const rsv_start = 1016;
@@ -199,6 +214,50 @@ namespace status {
code == policy_violation || code == message_too_big ||
code == internal_endpoint_error);
}
/// Return a human readable interpretation of a WebSocket close code
/**
* See https://tools.ietf.org/html/rfc6455#section-7.4 for more details.
*
* @since 0.3.0
*
* @param [in] code The code to look up.
* @return A human readable interpretation of the code.
*/
inline std::string get_string(value code) {
switch (code) {
case normal:
return "Normal close";
case going_away:
return "Going away";
case protocol_error:
return "Protocol error";
case unsupported_data:
return "Unsupported data";
case no_status:
return "No status set";
case abnormal_close:
return "Abnormal close";
case invalid_payload:
return "Invalid payload";
case policy_violation:
return "Policy violoation";
case message_too_big:
return "Message too big";
case extension_required:
return "Extension required";
case internal_endpoint_error:
return "Internal endpoint error";
case tls_handshake:
return "TLS handshake failure";
case subprotocol_error:
return "Generic subprotocol error";
case invalid_subprotocol_data:
return "Invalid subprotocol data";
default:
return "Unknown";
}
}
} // namespace status
/// Type used to convert close statuses between integer and wire representations
@@ -263,7 +322,7 @@ inline status::value extract_code(std::string const & payload, lib::error_code
inline std::string extract_reason(std::string const & payload, lib::error_code
& ec)
{
std::string reason = "";
std::string reason;
ec = lib::error_code();
if (payload.size() > 2) {
+17 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,7 +28,20 @@
#ifndef WEBSOCKETPP_COMMON_CHRONO_HPP
#define WEBSOCKETPP_COMMON_CHRONO_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 functional header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_
#ifndef _WEBSOCKETPP_CPP11_CHRONO_
#define _WEBSOCKETPP_CPP11_CHRONO_
#endif
#endif
// If we're on Visual Studio 2012 or higher and haven't explicitly disabled
// the use of C++11 chrono header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1700 && !defined _WEBSOCKETPP_NO_CPP11_CHRONO_
#ifndef _WEBSOCKETPP_CPP11_CHRONO_
#define _WEBSOCKETPP_CPP11_CHRONO_
#endif
@@ -44,9 +57,9 @@ namespace websocketpp {
namespace lib {
#ifdef _WEBSOCKETPP_CPP11_CHRONO_
using std::chrono::system_clock;
namespace chrono = std::chrono;
#else
using boost::chrono::system_clock;
namespace chrono = boost::chrono;
#endif
} // namespace lib
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -34,7 +34,7 @@ namespace websocketpp {
/// A handle to uniquely identify a connection.
/**
* This type uniquely identifies a connection. It is implimented as a weak
* This type uniquely identifies a connection. It is implemented as a weak
* pointer to the connection in question. This provides uniqueness across
* multiple endpoints and ensures that IDs never conflict or run out.
*
+71 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -40,10 +40,34 @@
#define __has_extension __has_feature // Compatibility with pre-3.0 compilers.
#endif
// The code below attempts to use information provided by the build system or
// user supplied defines to selectively enable C++11 language and library
// features. In most cases features that are targeted individually may also be
// selectively disabled via an associated _WEBSOCKETPP_NOXXX_ define.
#ifdef _WEBSOCKETPP_CPP11_STL_
// This flag indicates that all of the C++11 language features are available
// to us.
#if defined(_WEBSOCKETPP_CPP11_STL_) || __cplusplus >= 201103L || defined(_WEBSOCKETPP_CPP11_STRICT_)
// This check tests for blanket c++11 coverage. It can be activated in one
// of three ways. Either the compiler itself reports that it is a full
// C++11 compiler via the __cplusplus macro or the user/build system
// supplies one of the two preprocessor defines below:
// This is defined to allow other WebSocket++ common headers to enable
// C++11 features when they are detected by this file rather than
// duplicating the above logic in every common header.
#define _WEBSOCKETPP_CPP11_INTERNAL_
// _WEBSOCKETPP_CPP11_STRICT_
//
// This define reports to WebSocket++ that 100% of the language and library
// features of C++11 are available. Using this define on a non-C++11
// compiler will result in problems.
// _WEBSOCKETPP_CPP11_STL_
//
// This define enables *most* C++11 options that were implemented early on
// by compilers. It is typically used for compilers that have many, but not
// all C++11 features. It should be safe to use on GCC 4.7-4.8 and perhaps
// earlier.
#ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
#endif
@@ -53,7 +77,25 @@
#ifndef _WEBSOCKETPP_INITIALIZER_LISTS_
#define _WEBSOCKETPP_INITIALIZER_LISTS_
#endif
#ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
#endif
#ifndef _WEBSOCKETPP_MOVE_SEMANTICS_
#define _WEBSOCKETPP_MOVE_SEMANTICS_
#endif
#ifndef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#define _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#endif
#ifndef __GNUC__
// GCC as of version 4.9 (latest) does not support std::put_time yet.
// so ignore it
#define _WEBSOCKETPP_PUTTIME_
#endif
#else
// In the absence of a blanket define, try to use compiler versions or
// feature testing macros to selectively enable what we can.
// Test for noexcept
#ifndef _WEBSOCKETPP_NOEXCEPT_TOKEN_
#ifdef _WEBSOCKETPP_NOEXCEPT_
@@ -63,6 +105,9 @@
#if __has_feature(cxx_noexcept)
// clang feature detect says we have noexcept
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
#elif defined(_MSC_VER) && _MSC_VER >= 1900
// Visual Studio 2015+ has noexcept
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ noexcept
#else
// assume we don't have noexcept
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_
@@ -79,6 +124,9 @@
#if __has_feature(cxx_constexpr)
// clang feature detect says we have constexpr
#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
#elif defined(_MSC_VER) && _MSC_VER >= 1900
// Visual Studio 2015+ has constexpr
#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ constexpr
#else
// assume we don't have constexpr
#define _WEBSOCKETPP_CONSTEXPR_TOKEN_
@@ -90,6 +138,25 @@
#if __has_feature(cxx_generalized_initializers) && !defined(_WEBSOCKETPP_INITIALIZER_LISTS_)
#define _WEBSOCKETPP_INITIALIZER_LISTS_
#endif
// Test for nullptr
#ifndef _WEBSOCKETPP_NULLPTR_TOKEN_
#ifdef _WEBSOCKETPP_NULLPTR_
// build system says we have nullptr
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
#else
#if __has_feature(cxx_nullptr)
// clang feature detect says we have nullptr
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
#elif defined(_MSC_VER) &&_MSC_VER >= 1600
// Visual Studio version that has nullptr
#define _WEBSOCKETPP_NULLPTR_TOKEN_ nullptr
#else
// assume we don't have nullptr
#define _WEBSOCKETPP_NULLPTR_TOKEN_ 0
#endif
#endif
#endif
#endif
#endif // WEBSOCKETPP_COMMON_CPP11_HPP
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,19 +28,37 @@
#ifndef WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
#define WEBSOCKETPP_COMMON_FUNCTIONAL_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 functional header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 functional header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_FUNCTIONAL_
#ifndef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_FUNCTIONAL_
#include <functional>
#else
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/ref.hpp>
#endif
namespace websocketpp {
namespace lib {
@@ -49,6 +67,18 @@ namespace lib {
using std::bind;
using std::ref;
namespace placeholders = std::placeholders;
// There are some cases where a C++11 compiler balks at using std::ref
// but a C++03 compiler using boost function requires boost::ref. As such
// lib::ref is not useful in these cases. Instead this macro allows the use
// of boost::ref in the case of a boost compile or no reference wrapper at
// all in the case of a C++11 compile
#define _WEBSOCKETPP_REF(x) x
template <typename T>
void clear_function(T & x) {
x = nullptr;
}
#else
using boost::function;
using boost::bind;
@@ -57,6 +87,15 @@ namespace lib {
/// \todo this feels hacky, is there a better way?
using ::_1;
using ::_2;
using ::_3;
}
// See above definition for more details on what this is and why it exists
#define _WEBSOCKETPP_REF(x) boost::ref(x)
template <typename T>
void clear_function(T & x) {
x.clear();
}
#endif
+70 -70
View File
@@ -34,7 +34,7 @@
This code implements the MD5 Algorithm defined in RFC 1321, whose
text is available at
http://www.ietf.org/rfc/rfc1321.txt
http://www.ietf.org/rfc/rfc1321.txt
The code is derived from the text of the RFC, including the test suite
(section A.5) but excluding the rest of Appendix A. It does not include
any code or documentation that is identified in the RFC as being
@@ -45,12 +45,12 @@
that follows (in reverse chronological order):
2002-04-13 lpd Removed support for non-ANSI compilers; removed
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
references to Ghostscript; clarified derivation from RFC 1321;
now handles byte order either statically or dynamically.
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
added conditionalization for C++ compilation from Martin
Purschke <purschke@bnl.gov>.
1999-05-03 lpd Original version.
*/
@@ -80,9 +80,9 @@ typedef unsigned int md5_word_t; /* 32-bit word */
/* Define the state of the MD5 Algorithm. */
typedef struct md5_state_s {
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
md5_word_t count[2]; /* message length in bits, lsw first */
md5_word_t abcd[4]; /* digest buffer */
md5_byte_t buf[64]; /* accumulate block */
} md5_state_t;
/* Initialize the algorithm. */
@@ -94,7 +94,7 @@ inline void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes)
/* Finish the message and return the digest. */
inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#undef ZSW_MD5_BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
#ifdef ARCH_IS_BIG_ENDIAN
# define ZSW_MD5_BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
#else
@@ -169,8 +169,8 @@ inline void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) {
md5_word_t
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3];
md5_word_t t;
#if ZSW_MD5_BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */
@@ -183,51 +183,51 @@ static void md5_process(md5_state_t *pms, md5_byte_t const * data /*[64]*/) {
{
#if ZSW_MD5_BYTE_ORDER == 0
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static int const w = 1;
/*
* Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient
* algorithm on the latter.
*/
static int const w = 1;
if (*((md5_byte_t const *)&w)) /* dynamic little-endian */
if (*((md5_byte_t const *)&w)) /* dynamic little-endian */
#endif
#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (md5_byte_t const *)0) & 3)) {
/* data are properly aligned */
X = (md5_word_t const *)data;
} else {
/* not aligned */
std::memcpy(xbuf, data, 64);
X = xbuf;
}
}
#if ZSW_MD5_BYTE_ORDER <= 0 /* little-endian */
{
/*
* On little-endian machines, we can process properly aligned
* data without copying it.
*/
if (!((data - (md5_byte_t const *)0) & 3)) {
/* data are properly aligned */
X = (md5_word_t const *)data;
} else {
/* not aligned */
std::memcpy(xbuf, data, 64);
X = xbuf;
}
}
#endif
#if ZSW_MD5_BYTE_ORDER == 0
else /* dynamic big-endian */
else /* dynamic big-endian */
#endif
#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
#if ZSW_MD5_BYTE_ORDER >= 0 /* big-endian */
{
/*
* On big-endian machines, we must arrange the bytes in the
* right order.
*/
const md5_byte_t *xp = data;
int i;
# if ZSW_MD5_BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */
X = xbuf; /* (dynamic only) */
# else
# define xbuf X /* (static only) */
# define xbuf X /* (static only) */
# endif
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
}
#endif
}
@@ -361,71 +361,71 @@ void md5_append(md5_state_t *pms, md5_byte_t const * data, size_t nbytes) {
md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0)
return;
return;
/* Update the message length. */
pms->count[1] += nbytes >> 29;
pms->count[0] += nbits;
if (pms->count[0] < nbits)
pms->count[1]++;
pms->count[1]++;
/* Process an initial partial block. */
if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
int copy = (offset + nbytes > 64 ? 64 - offset : static_cast<int>(nbytes));
std::memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
std::memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64)
return;
p += copy;
left -= copy;
md5_process(pms, pms->buf);
}
/* Process full blocks. */
for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p);
md5_process(pms, p);
/* Process a final partial block. */
if (left)
std::memcpy(pms->buf, p, left);
std::memcpy(pms->buf, p, left);
}
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) {
static md5_byte_t const pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
md5_byte_t data[8];
int i;
/* Save the length before padding. */
for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */
md5_append(pms, data, 8);
for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
}
// some convenience c++ functions
inline std::string md5_hash_string(std::string const & s) {
char digest[16];
char digest[16];
md5_state_t state;
md5_state_t state;
md5_init(&state);
md5_append(&state, (md5_byte_t const *)s.c_str(), s.size());
md5_finish(&state, (md5_byte_t *)digest);
md5_init(&state);
md5_append(&state, (md5_byte_t const *)s.c_str(), s.size());
md5_finish(&state, (md5_byte_t *)digest);
std::string ret;
ret.resize(16);
std::copy(digest,digest+16,ret.begin());
return ret;
return ret;
}
const char hexval[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+23 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,16 +28,32 @@
#ifndef WEBSOCKETPP_COMMON_MEMORY_HPP
#define WEBSOCKETPP_COMMON_MEMORY_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 memory header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 functional header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
#include <memory>
#else
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/scoped_array.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/pointer_cast.hpp>
@@ -49,15 +65,20 @@ namespace lib {
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
using std::shared_ptr;
using std::weak_ptr;
using std::auto_ptr;
using std::enable_shared_from_this;
using std::static_pointer_cast;
using std::make_shared;
using std::unique_ptr;
typedef std::unique_ptr<unsigned char[]> unique_ptr_uchar_array;
#else
using boost::shared_ptr;
using boost::weak_ptr;
using std::auto_ptr;
using boost::enable_shared_from_this;
using boost::static_pointer_cast;
using boost::make_shared;
typedef boost::scoped_array<unsigned char> unique_ptr_uchar_array;
#endif
+30 -6
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,20 +29,22 @@
#define WEBSOCKETPP_COMMON_NETWORK_HPP
// For ntohs and htons
#if defined(WIN32)
#if defined(_WIN32)
#include <winsock2.h>
#else
//#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include <websocketpp/common/stdint.hpp>
namespace websocketpp {
namespace lib {
namespace net {
inline bool is_little_endian() {
short int val = 0x1;
char *ptr = (char*)&val;
char *ptr = reinterpret_cast<char *>(&val);
return (ptr[0] == 1);
}
@@ -50,7 +52,18 @@ inline bool is_little_endian() {
#define TYP_SMLE 1
#define TYP_BIGE 2
inline uint64_t htonll(uint64_t src) {
/// Convert 64 bit value to network byte order
/**
* This method is prefixed to avoid conflicts with operating system level
* macros for this functionality.
*
* TODO: figure out if it would be beneficial to use operating system level
* macros for this.
*
* @param src The integer in host byte order
* @return src converted to network byte order
*/
inline uint64_t _htonll(uint64_t src) {
static int typ = TYP_INIT;
unsigned char c;
union {
@@ -71,8 +84,19 @@ inline uint64_t htonll(uint64_t src) {
return x.ull;
}
inline uint64_t ntohll(uint64_t src) {
return htonll(src);
/// Convert 64 bit value to host byte order
/**
* This method is prefixed to avoid conflicts with operating system level
* macros for this functionality.
*
* TODO: figure out if it would be beneficial to use operating system level
* macros for this.
*
* @param src The integer in network byte order
* @return src converted to host byte order
*/
inline uint64_t _ntohll(uint64_t src) {
return _htonll(src);
}
} // net
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -33,9 +33,14 @@
* don't fit somewhere else better.
*/
#if defined(WIN32) && !defined(NOMINMAX)
#if defined(_WIN32) && !defined(NOMINMAX)
// don't define min and max macros that conflict with std::min and std::max
#define NOMINMAX
#endif
// Bump up the variadic parameter max for Visual Studio 2012
#if defined(_MSC_VER) && _MSC_VER == 1700
#define _VARIADIC_MAX 8
#endif
#endif // WEBSOCKETPP_COMMON_PLATFORMS_HPP
+18 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,12 +28,28 @@
#ifndef WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
#define WEBSOCKETPP_COMMON_RANDOM_DEVICE_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 random header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_RANDOM_DEVICE_
#ifndef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#define _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 random header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_
#ifndef _WEBSOCKETPP_CPP11_MEMORY_
#define _WEBSOCKETPP_CPP11_MEMORY_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_RANDOM_DEVICE_
#include <random>
#else
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -32,7 +32,7 @@
#define __STDC_LIMIT_MACROS 1
#endif
#if WIN32 && (_MSC_VER < 1600)
#if defined (_WIN32) && defined (_MSC_VER) && (_MSC_VER < 1600)
#include <boost/cstdint.hpp>
using boost::int8_t;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,12 +28,28 @@
#ifndef WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
#define WEBSOCKETPP_COMMON_SYSTEM_ERROR_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#include <websocketpp/common/cpp11.hpp>
// If we've determined that we're in full C++11 mode and the user hasn't
// explicitly disabled the use of C++11 system_error header, then prefer it to
// boost.
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#endif
#endif
// If we're on Visual Studio 2010 or higher and haven't explicitly disabled
// the use of C++11 system_error header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1600 && !defined _WEBSOCKETPP_NO_CPP11_SYSTEM_ERROR_
#ifndef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#endif
#endif
#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
#include <system_error>
#else
@@ -45,6 +61,7 @@ namespace websocketpp {
namespace lib {
#ifdef _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
using std::errc;
using std::error_code;
using std::error_category;
using std::error_condition;
@@ -52,6 +69,7 @@ namespace lib {
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_ namespace std {
#define _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_ }
#else
namespace errc = boost::system::errc;
using boost::system::error_code;
using boost::system::error_category;
using boost::system::error_condition;
+19 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,7 +28,24 @@
#ifndef WEBSOCKETPP_COMMON_THREAD_HPP
#define WEBSOCKETPP_COMMON_THREAD_HPP
#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
#include <websocketpp/common/cpp11.hpp>
// If we autodetect C++11 and haven't been explicitly instructed to not use
// C++11 threads, then set the defines that instructs the rest of this header
// to use C++11 <thread> and <mutex>
#if defined _WEBSOCKETPP_CPP11_INTERNAL_ && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
// MinGW by default does not support C++11 thread/mutex so even if the
// internal check for C++11 passes, ignore it if we are on MinGW
#if (!defined(__MINGW32__) && !defined(__MINGW64__))
#ifndef _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_THREAD_
#endif
#endif
#endif
// If we're on Visual Studio 2013 or higher and haven't explicitly disabled
// the use of C++11 thread header then prefer it to boost.
#if defined(_MSC_VER) && _MSC_VER >= 1800 && !defined _WEBSOCKETPP_NO_CPP11_THREAD_
#ifndef _WEBSOCKETPP_CPP11_THREAD_
#define _WEBSOCKETPP_CPP11_THREAD_
#endif
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,26 +29,48 @@
#define WEBSOCKETPP_CONCURRENCY_NONE_HPP
namespace websocketpp {
/// Concurrency handling support
namespace concurrency {
/// Implementation for no-op locking primitives
namespace none_impl {
/// A fake mutex implementation that does nothing
class fake_mutex {
public:
fake_mutex() {}
~fake_mutex() {}
};
/// A fake lock guard implementation that does nothing
class fake_lock_guard {
public:
explicit fake_lock_guard(fake_mutex foo) {}
explicit fake_lock_guard(fake_mutex) {}
~fake_lock_guard() {}
};
} // namespace none_impl
/// Stub Concurrency policy to remove locking in single threaded projects
/// Stub concurrency policy that implements the interface using no-ops.
/**
* This policy documents the concurrency policy interface using no-ops. It can
* be used as a reference or base for building a new concurrency policy. It can
* also be used as is to disable all locking for endpoints used in purely single
* threaded programs.
*/
class none {
public:
/// The type of a mutex primitive
/**
* std::mutex is an example.
*/
typedef none_impl::fake_mutex mutex_type;
/// The type of a scoped/RAII lock primitive.
/**
* The scoped lock constructor should take a mutex_type as a parameter,
* acquire that lock, and release it in its destructor. std::lock_guard is
* an example.
*/
typedef none_impl::fake_lock_guard scoped_lock_type;
};
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -25,9 +25,9 @@
*
*/
// This header defines WebSocket++ macros for C++11 compatibility based on the Boost.Config library.
// This will correctly configure most target platforms simply by including this header before
// any other WebSocket++ header.
// This header defines WebSocket++ macros for C++11 compatibility based on the
// Boost.Config library. This will correctly configure most target platforms
// simply by including this header before any other WebSocket++ header.
#ifndef WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
#define WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
@@ -41,7 +41,7 @@
#define _WEBSOCKETPP_CPP11_FUNCTIONAL_
#endif
#ifndef BOOST_ASIO_HAS_STD_CHRONO
#ifdef BOOST_ASIO_HAS_STD_CHRONO
#define _WEBSOCKETPP_CPP11_CHRONO_
#endif
@@ -67,5 +67,6 @@
#define _WEBSOCKETPP_NOEXCEPT_TOKEN_ BOOST_NOEXCEPT
#define _WEBSOCKETPP_CONSTEXPR_TOKEN_ BOOST_CONSTEXPR
// TODO: nullptr support
#endif // WEBSOCKETPP_CONFIG_BOOST_CONFIG_HPP
+25 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -215,6 +215,30 @@ struct core {
*/
static const bool silent_close = false;
/// Default maximum message size
/**
* Default value for the processor's maximum message size. Maximum message size
* determines the point at which the library will fail a connection with the
* message_too_big protocol error.
*
* The default is 32MB
*
* @since 0.3.0
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -34,7 +34,11 @@
#include <websocketpp/common/stdint.hpp>
// Concurrency
#ifndef _WEBSOCKETPP_NO_THREADING_
#include <websocketpp/concurrency/basic.hpp>
#else
#include <websocketpp/concurrency/none.hpp>
#endif
// Transport
#include <websocketpp/transport/iostream/endpoint.hpp>
@@ -68,7 +72,11 @@ struct core_client {
typedef core_client type;
// Concurrency policy
#ifndef _WEBSOCKETPP_NO_THREADING_
typedef websocketpp::concurrency::basic concurrency_type;
#else
typedef websocketpp::concurrency::none concurrency_type;
#endif
// HTTP Parser Policies
typedef http::parser::request request_type;
@@ -216,6 +224,30 @@ struct core_client {
*/
static const bool silent_close = false;
/// Default maximum message size
/**
* Default value for the processor's maximum message size. Maximum message size
* determines the point at which the library will fail a connection with the
* message_too_big protocol error.
*
* The default is 32MB
*
* @since 0.3.0
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
+25 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -216,6 +216,30 @@ struct debug_core {
*/
static const bool silent_close = false;
/// Default maximum message size
/**
* Default value for the processor's maximum message size. Maximum message size
* determines the point at which the library will fail a connection with the
* message_too_big protocol error.
*
* The default is 32MB
*
* @since 0.3.0
*/
static const size_t max_message_size = 32000000;
/// Default maximum http body size
/**
* Default value for the http parser's maximum body size. Maximum body size
* determines the point at which the library will abort reading an HTTP
* connection with the 413/request entity too large error.
*
* The default is 32MB
*
* @since 0.5.0
*/
static const size_t max_http_body_size = 32000000;
/// Global flag for enabling/disabling extensions
static const bool enable_extensions = true;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+313 -93
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,19 +29,20 @@
#define WEBSOCKETPP_CONNECTION_HPP
#include <websocketpp/close.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/frame.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <algorithm>
#include <iostream>
#include <queue>
#include <sstream>
#include <string>
#include <vector>
@@ -118,7 +119,7 @@ typedef lib::function<void(connection_hdl,std::string)> pong_timeout_handler;
/**
* The validate handler is called after a WebSocket handshake has been received
* and processed but before it has been accepted. This gives the application a
* chance to impliment connection details specific policies for accepting
* chance to implement connection details specific policies for accepting
* connections and the ability to negotiate extensions and subprotocols.
*
* The validate handler return value indicates whether or not the connection
@@ -213,6 +214,20 @@ namespace internal_state {
PROCESS_CONNECTION = 7
};
} // namespace internal_state
namespace http_state {
// states to keep track of the progress of http connections
enum value {
init = 0,
deferred = 1,
headers_written = 2,
body_written = 3,
closed = 4
};
} // namespace http_state
} // namespace session
/// Represents an individual WebSocket connection
@@ -279,9 +294,9 @@ private:
};
public:
explicit connection(bool is_server, std::string const & ua, alog_type& alog,
explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
elog_type& elog, rng_type & rng)
: transport_con_type(is_server,alog,elog)
: transport_con_type(p_is_server, alog, elog)
, m_handle_read_frame(lib::bind(
&type::handle_read_frame,
this,
@@ -297,17 +312,21 @@ public:
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
, m_pong_timeout_dur(config::timeout_pong)
, m_max_message_size(config::max_message_size)
, m_state(session::state::connecting)
, m_internal_state(session::internal_state::USER_INIT)
, m_msg_manager(new con_msg_manager_type())
, m_send_buffer_size(0)
, m_write_flag(false)
, m_is_server(is_server)
, m_read_flag(true)
, m_is_server(p_is_server)
, m_alog(alog)
, m_elog(elog)
, m_rng(rng)
, m_local_close_code(close::status::abnormal_close)
, m_remote_close_code(close::status::abnormal_close)
, m_is_http(false)
, m_http_state(session::http_state::init)
, m_was_clean(false)
{
m_alog.write(log::alevel::devel,"connection constructor");
@@ -455,9 +474,9 @@ public:
m_message_handler = h;
}
/////////////////////////
// Connection timeouts //
/////////////////////////
//////////////////////////////////////////
// Connection timeouts and other limits //
//////////////////////////////////////////
/// Set open handshake timeout
/**
@@ -528,6 +547,70 @@ public:
m_pong_timeout_dur = dur;
}
/// Get maximum message size
/**
* Get maximum message size. Maximum message size determines the point at
* which the connection will fail with the message_too_big protocol error.
*
* The default is set by the endpoint that creates the connection.
*
* @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 connection will fail with the message_too_big protocol error.
* This value may be changed during the connection.
*
* The default is set by the endpoint that creates the connection.
*
* @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;
if (m_processor) {
m_processor->set_max_message_size(new_value);
}
}
/// Get maximum HTTP message body size
/**
* Get maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the endpoint that creates the connection.
*
* @since 0.5.0
*
* @return The maximum HTTP message body size
*/
size_t get_max_http_body_size() const {
return m_request.get_max_body_size();
}
/// Set maximum HTTP message body size
/**
* Set maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the endpoint that creates the connection.
*
* @since 0.5.0
*
* @param new_value The value to set as the maximum message size.
*/
void set_max_http_body_size(size_t new_value) {
m_request.set_max_body_size(new_value);
}
//////////////////////////////////
// Uncategorized public methods //
//////////////////////////////////
@@ -545,7 +628,10 @@ public:
*/
size_t get_buffered_amount() const;
/// DEPRECATED: use get_buffered_amount instead
/// Get the size of the outgoing write buffer (in payload bytes)
/**
* @deprecated use `get_buffered_amount` instead
*/
size_t buffered_amount() const {
return get_buffered_amount();
}
@@ -615,9 +701,46 @@ public:
* @return An error code
*/
lib::error_code interrupt();
/// Transport inturrupt callback
void handle_interrupt();
/// Pause reading of new data
/**
* Signals to the connection to halt reading of new data. While reading is paused,
* the connection will stop reading from its associated socket. In turn this will
* result in TCP based flow control kicking in and slowing data flow from the remote
* endpoint.
*
* This is useful for applications that push new requests to a queue to be processed
* by another thread and need a way to signal when their request queue is full without
* blocking the network processing thread.
*
* Use `resume_reading()` to resume.
*
* If supported by the transport this is done asynchronously. As such reading may not
* stop until the current read operation completes. Typically you can expect to
* receive no more bytes after initiating a read pause than the size of the read
* buffer.
*
* If reading is paused for this connection already nothing is changed.
*/
lib::error_code pause_reading();
/// Pause reading callback
void handle_pause_reading();
/// Resume reading of new data
/**
* Signals to the connection to resume reading of new data after it was paused by
* `pause_reading()`.
*
* If reading is not paused for this connection already nothing is changed.
*/
lib::error_code resume_reading();
/// Resume reading callback
void handle_resume_reading();
/// Send a ping
/**
@@ -827,7 +950,18 @@ public:
* @param key Name of the header to get
* @return The value of the header
*/
std::string const & get_request_header(std::string const & key);
std::string const & get_request_header(std::string const & key) const;
/// Retrieve a request body
/**
* Retrieve the value of the request body. This value is typically used with
* PUT and POST requests to upload files or other data. Only HTTP
* connections will ever have bodies. WebSocket connection's will always
* have blank bodies.
*
* @return The value of the request body.
*/
std::string const & get_request_body() const;
/// Retrieve a response header
/**
@@ -836,8 +970,32 @@ public:
* @param key Name of the header to get
* @return The value of the header
*/
std::string const & get_response_header(std::string const & key);
std::string const & get_response_header(std::string const & key) const;
/// Get response HTTP status code
/**
* Gets the response status code
*
* @since 0.7.0
*
* @return The response status code sent
*/
http::status_code::value get_response_code() const {
return m_response.get_status_code();
}
/// Get response HTTP status message
/**
* Gets the response status message
*
* @since 0.7.0
*
* @return The response status message sent
*/
std::string const & get_response_msg() const {
return m_response.get_status_msg();
}
/// Set response status code and message
/**
* Sets the response status code to `code` and looks up the corresponding
@@ -944,6 +1102,71 @@ public:
request_type const & get_request() const {
return m_request;
}
/// Get response object
/**
* Direct access to the HTTP response sent or received as a part of the
* opening handshake. This can be used to call methods of the response
* object that are not part of the standard request API that connection
* wraps.
*
* Note use of this method involves using behavior specific to the
* configured HTTP policy. Such behavior may not work with alternate HTTP
* policies.
*
* @since 0.7.0
*
* @return A const reference to the raw response object
*/
response_type const & get_response() const {
return m_response;
}
/// Defer HTTP Response until later (Exception free)
/**
* Used in the http handler to defer the HTTP response for this connection
* until later. Handshake timers will be canceled and the connection will be
* left open until `send_http_response` or an equivalent is called.
*
* Warning: deferred connections won't time out and as a result can tie up
* resources.
*
* @since 0.6.0
*
* @return A status code, zero on success, non-zero otherwise
*/
lib::error_code defer_http_response();
/// Send deferred HTTP Response (exception free)
/**
* Sends an http response to an HTTP connection that was deferred. This will
* send a complete response including all headers, status line, and body
* text. The connection will be closed afterwards.
*
* @since 0.6.0
*
* @param ec A status code, zero on success, non-zero otherwise
*/
void send_http_response(lib::error_code & ec);
/// Send deferred HTTP Response
void send_http_response();
// TODO HTTPNBIO: write_headers
// function that processes headers + status so far and writes it to the wire
// beginning the HTTP response body state. This method will ignore anything
// in the response body.
// TODO HTTPNBIO: write_body_message
// queues the specified message_buffer for async writing
// TODO HTTPNBIO: finish connection
//
// TODO HTTPNBIO: write_response
// Writes the whole response, headers + body and closes the connection
/////////////////////////////////////////////////////////////
// Pass-through access to the other connection information //
@@ -1032,26 +1255,6 @@ public:
return m_ec;
}
////////////////////////////////////////////////////////////////////////
// The remaining public member functions are for internal/policy use //
// only. Do not call from application code unless you understand what //
// you are doing. //
////////////////////////////////////////////////////////////////////////
/// Set Connection Handle
/**
* The connection handle is a token that can be shared outside the
* WebSocket++ core for the purposes of identifying a connection and
* sending it messages.
*
* @param hdl A connection_hdl that the connection will use to refer
* to itself.
*/
void set_handle(connection_hdl hdl) {
m_connection_hdl = hdl;
transport_con_type::set_handle(hdl);
}
/// Get a message buffer
/**
* Warning: The API related to directly sending message buffers may change
@@ -1077,7 +1280,13 @@ public:
return m_msg_manager->get_message(op, size);
}
void start();
////////////////////////////////////////////////////////////////////////
// The remaining public member functions are for internal/policy use //
// only. Do not call from application code unless you understand what //
// you are doing. //
////////////////////////////////////////////////////////////////////////
void read_handshake(size_t num_bytes);
@@ -1086,17 +1295,18 @@ public:
void handle_read_http_response(lib::error_code const & ec,
size_t bytes_transferred);
void handle_send_http_response(lib::error_code const & ec);
void handle_write_http_response(lib::error_code const & ec);
void handle_send_http_request(lib::error_code const & ec);
void handle_open_handshake_timeout(lib::error_code const & ec);
void handle_close_handshake_timeout(lib::error_code const & ec);
void handle_read_frame(lib::error_code const & ec,
size_t bytes_transferred);
void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred);
void read_frame();
/// Get array of WebSocket protocol versions that this connection supports.
const std::vector<int>& get_supported_versions() const;
std::vector<int> const & get_supported_versions() const;
/// Sets the handler for a terminating connection. Should only be used
/// internally by the endpoint class.
@@ -1126,64 +1336,48 @@ public:
* non-zero otherwise.
*/
void handle_write_frame(lib::error_code const & ec);
// protected:
// This set of methods would really like to be protected, but doing so
// requires that the endpoint be able to friend the connection. This is
// allowed with C++11, but not prior versions
/// Start the connection state machine
void start();
/// Set Connection Handle
/**
* The connection handle is a token that can be shared outside the
* WebSocket++ core for the purposes of identifying a connection and
* sending it messages.
*
* @param hdl A connection_hdl that the connection will use to refer
* to itself.
*/
void set_handle(connection_hdl hdl) {
m_connection_hdl = hdl;
transport_con_type::set_handle(hdl);
}
protected:
void handle_transport_init(lib::error_code const & ec);
/// Set m_processor based on information in m_request. Set m_response
/// status and return false on error.
bool initialize_processor();
/// status and return an error code indicating status.
lib::error_code initialize_processor();
/// Perform WebSocket handshake validation of m_request using m_processor.
/// set m_response and return false on error.
bool process_handshake_request();
/// Atomically change the internal connection state.
/**
* @param req The required starting state. If the internal state does not
* match req an exception is thrown.
*
* @param dest The state to change to.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_change(istate_type req, istate_type dest,
std::string msg);
/// Atomically change the internal and external connection state.
/**
* @param ireq The required starting internal state. If the internal state
* does not match ireq an exception is thrown.
*
* @param idest The internal state to change to.
*
* @param ereq The required starting external state. If the external state
* does not match ereq an exception is thrown.
*
* @param edest The external state to change to.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_change(istate_type ireq, istate_type idest,
session::state::value ereq, session::state::value edest,
std::string msg);
/// Atomically read and compared the internal state.
/**
* @param req The state to test against. If the internal state does not
* match req an exception is thrown.
*
* @param msg The message to include in the exception thrown
*/
void atomic_state_check(istate_type req, std::string msg);
/// set m_response and return an error code indicating status.
lib::error_code process_handshake_request();
private:
/// Completes m_response, serializes it, and sends it out on the wire.
void send_http_response();
void write_http_response(lib::error_code const & ec);
/// Sends an opening WebSocket connect request
void send_http_request();
/// Alternate path for send_http_response in error conditions
void send_http_response_error();
/// Alternate path for write_http_response in error conditions
void write_http_response_error(lib::error_code const & ec);
/// Process control message
/**
@@ -1203,7 +1397,7 @@ private:
* @return A status code, zero on success, non-zero otherwise
*/
lib::error_code send_close_ack(close::status::value code =
close::status::blank, std::string const & reason = "");
close::status::blank, std::string const & reason = std::string());
/// Send close frame
/**
@@ -1221,7 +1415,7 @@ private:
* @return A status code, zero on success, non-zero otherwise
*/
lib::error_code send_close_frame(close::status::value code =
close::status::blank, std::string const & reason = "", bool ack = false,
close::status::blank, std::string const & reason = std::string(), bool ack = false,
bool terminal = false);
/// Get a pointer to a new WebSocket protocol processor for a given version
@@ -1280,6 +1474,20 @@ private:
* Includes: error code and message for why it was failed
*/
void log_fail_result();
/// Prints information about HTTP connections
/**
* Includes: TODO
*/
void log_http_result();
/// Prints information about an arbitrary error code on the specified channel
template <typename error_type>
void log_err(log::level l, char const * msg, error_type const & ec) {
std::stringstream s;
s << msg << " error: " << ec << " (" << ec.message() << ")";
m_elog.write(l, s.str());
}
// internal handler functions
read_handler m_handle_read_frame;
@@ -1307,6 +1515,7 @@ private:
long m_open_handshake_timeout_dur;
long m_close_handshake_timeout_dur;
long m_pong_timeout_dur;
size_t m_max_message_size;
/// External connection state
/**
@@ -1370,9 +1579,9 @@ private:
*/
std::vector<transport::buffer> m_send_buffer;
/// a pointer to hold on to the current message being written to keep it
/// a list of pointers to hold on to the messages being written to keep them
/// from going out of scope before the write is complete.
message_ptr m_current_msg;
std::vector<message_ptr> m_current_msgs;
/// True if there is currently an outstanding transport write
/**
@@ -1380,6 +1589,9 @@ private:
*/
bool m_write_flag;
/// True if this connection is presently reading new data
bool m_read_flag;
// connection data
request_type m_request;
response_type m_response;
@@ -1411,6 +1623,14 @@ private:
/// Detailed internal error code
lib::error_code m_ec;
/// A flag that gets set once it is determined that the connection is an
/// HTTP connection and not a WebSocket one.
bool m_is_http;
/// A flag that gets set when the completion of an http connection is
/// deferred until later.
session::http_state::value m_http_state;
bool m_was_clean;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+201 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,11 +29,11 @@
#define WEBSOCKETPP_ENDPOINT_HPP
#include <websocketpp/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/version.hpp>
#include <iostream>
#include <set>
#include <string>
namespace websocketpp {
@@ -85,25 +85,78 @@ public:
// TODO: organize these
typedef typename connection_type::termination_handler termination_handler;
typedef lib::shared_ptr<connection_weak_ptr> hdl_type;
// This would be ideal. Requires C++11 though
//friend connection;
explicit endpoint(bool is_server)
: m_alog(config::alog_level, &std::cout)
, m_elog(config::elog_level, &std::cerr)
explicit endpoint(bool p_is_server)
: m_alog(config::alog_level, log::channel_type_hint::access)
, m_elog(config::elog_level, log::channel_type_hint::error)
, m_user_agent(::websocketpp::user_agent)
, m_open_handshake_timeout_dur(config::timeout_open_handshake)
, m_close_handshake_timeout_dur(config::timeout_close_handshake)
, m_pong_timeout_dur(config::timeout_pong)
, m_is_server(is_server)
, m_max_message_size(config::max_message_size)
, m_max_http_body_size(config::max_http_body_size)
, m_is_server(p_is_server)
{
m_alog.set_channels(config::alog_level);
m_elog.set_channels(config::elog_level);
m_alog.write(log::alevel::devel,"endpoint constructor");
m_alog.write(log::alevel::devel, "endpoint constructor");
transport_type::init_logging(&m_alog,&m_elog);
transport_type::init_logging(&m_alog, &m_elog);
}
/// Destructor
~endpoint<connection,config>() {}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no copy constructor because endpoints are not copyable
endpoint(endpoint &) = delete;
// no copy assignment operator because endpoints are not copyable
endpoint & operator=(endpoint const &) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
/// Move constructor
endpoint(endpoint && o)
: config::transport_type(std::move(o))
, config::endpoint_base(std::move(o))
, m_alog(std::move(o.m_alog))
, m_elog(std::move(o.m_elog))
, m_user_agent(std::move(o.m_user_agent))
, m_open_handler(std::move(o.m_open_handler))
, m_close_handler(std::move(o.m_close_handler))
, m_fail_handler(std::move(o.m_fail_handler))
, m_ping_handler(std::move(o.m_ping_handler))
, m_pong_handler(std::move(o.m_pong_handler))
, m_pong_timeout_handler(std::move(o.m_pong_timeout_handler))
, m_interrupt_handler(std::move(o.m_interrupt_handler))
, m_http_handler(std::move(o.m_http_handler))
, m_validate_handler(std::move(o.m_validate_handler))
, m_message_handler(std::move(o.m_message_handler))
, m_open_handshake_timeout_dur(o.m_open_handshake_timeout_dur)
, m_close_handshake_timeout_dur(o.m_close_handshake_timeout_dur)
, m_pong_timeout_dur(o.m_pong_timeout_dur)
, m_max_message_size(o.m_max_message_size)
, m_max_http_body_size(o.m_max_http_body_size)
, m_rng(std::move(o.m_rng))
, m_is_server(o.m_is_server)
{}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no move assignment operator because of const member variables
endpoint & operator=(endpoint &&) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
/// Returns the user agent string that this endpoint will use
/**
* Returns the user agent string that this endpoint will use when creating
@@ -272,9 +325,9 @@ public:
m_message_handler = h;
}
/////////////////////////
// Connection timeouts //
/////////////////////////
//////////////////////////////////////////
// Connection timeouts and other limits //
//////////////////////////////////////////
/// Set open handshake timeout
/**
@@ -348,6 +401,72 @@ public:
m_pong_timeout_dur = dur;
}
/// Get default maximum message size
/**
* Get the default maximum message size that will be used for new
* connections created by this endpoint. The maximum message size determines
* the point at which the connection will fail a connection with the
* message_too_big protocol error.
*
* The default is set by 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 default maximum message size
/**
* Set the default maximum message size that will be used for new
* connections created by this endpoint. Maximum message size determines the
* point at which the connection will fail a connection with the
* message_too_big protocol error.
*
* The default is set by 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;
}
/// Get maximum HTTP message body size
/**
* Get maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the max_http_body_size value from the template
* config
*
* @since 0.5.0
*
* @return The maximum HTTP message body size
*/
size_t get_max_http_body_size() const {
return m_max_http_body_size;
}
/// Set maximum HTTP message body size
/**
* Set maximum HTTP message body size. Maximum message body size determines
* the point at which the connection will stop reading an HTTP request whose
* body is too large.
*
* The default is set by the max_http_body_size value from the template
* config
*
* @since 0.5.1
*
* @param new_value The value to set as the maximum message size.
*/
void set_max_http_body_size(size_t new_value) {
m_max_http_body_size = new_value;
}
/*************************************/
/* Connection pass through functions */
/*************************************/
@@ -363,6 +482,72 @@ public:
void interrupt(connection_hdl hdl, lib::error_code & ec);
void interrupt(connection_hdl hdl);
/// Pause reading of new data (exception free)
/**
* Signals to the connection to halt reading of new data. While reading is
* paused, the connection will stop reading from its associated socket. In
* turn this will result in TCP based flow control kicking in and slowing
* data flow from the remote endpoint.
*
* This is useful for applications that push new requests to a queue to be
* processed by another thread and need a way to signal when their request
* queue is full without blocking the network processing thread.
*
* Use `resume_reading()` to resume.
*
* If supported by the transport this is done asynchronously. As such
* reading may not stop until the current read operation completes.
* Typically you can expect to receive no more bytes after initiating a read
* pause than the size of the read buffer.
*
* If reading is paused for this connection already nothing is changed.
*/
void pause_reading(connection_hdl hdl, lib::error_code & ec);
/// Pause reading of new data
void pause_reading(connection_hdl hdl);
/// Resume reading of new data (exception free)
/**
* Signals to the connection to resume reading of new data after it was
* paused by `pause_reading()`.
*
* If reading is not paused for this connection already nothing is changed.
*/
void resume_reading(connection_hdl hdl, lib::error_code & ec);
/// Resume reading of new data
void resume_reading(connection_hdl hdl);
/// Send deferred HTTP Response
/**
* Sends an http response to an HTTP connection that was deferred. This will
* send a complete response including all headers, status line, and body
* text. The connection will be closed afterwards.
*
* Exception free variant
*
* @since 0.6.0
*
* @param hdl The connection to send the response on
* @param ec A status code, zero on success, non-zero otherwise
*/
void send_http_response(connection_hdl hdl, lib::error_code & ec);
/// Send deferred HTTP Response (exception free)
/**
* Sends an http response to an HTTP connection that was deferred. This will
* send a complete response including all headers, status line, and body
* text. The connection will be closed afterwards.
*
* Exception variant
*
* @since 0.6.0
*
* @param hdl The connection to send the response on
*/
void send_http_response(connection_hdl hdl);
/// Create a message and add it to the outgoing send queue (exception free)
/**
* Convenience method to send a message given a payload string and an opcode
@@ -456,7 +641,6 @@ public:
* @return the connection_ptr. May be NULL if the handle was invalid.
*/
connection_ptr get_con_from_hdl(connection_hdl hdl, lib::error_code & ec) {
scoped_lock_type lock(m_mutex);
connection_ptr con = lib::static_pointer_cast<connection_type>(
hdl.lock());
if (!con) {
@@ -470,7 +654,7 @@ public:
lib::error_code ec;
connection_ptr con = this->get_con_from_hdl(hdl,ec);
if (ec) {
throw ec;
throw exception(ec);
}
return con;
}
@@ -497,6 +681,8 @@ private:
long m_open_handshake_timeout_dur;
long m_close_handshake_timeout_dur;
long m_pong_timeout_dur;
size_t m_max_message_size;
size_t m_max_http_body_size;
rng_type m_rng;
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+60 -10
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,7 +28,9 @@
#ifndef WEBSOCKETPP_ERROR_HPP
#define WEBSOCKETPP_ERROR_HPP
#include <exception>
#include <string>
#include <utility>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
@@ -38,7 +40,7 @@ namespace websocketpp {
/// Combination error code / string type for returning two values
typedef std::pair<lib::error_code,std::string> err_str_pair;
// setup for errors that should be propogated back to the user.
/// Library level error codes
namespace error {
enum value {
/// Catch-all library error
@@ -86,7 +88,8 @@ enum value {
/// Invalid subprotocol
invalid_subprotocol,
/// Bad or unknown connection
/// An operation was attempted on a connection that did not exist or was
/// already deleted.
bad_connection,
/// Unit testing utility error code
@@ -114,7 +117,33 @@ enum value {
close_handshake_timeout,
/// Invalid port in URI
invalid_port
invalid_port,
/// An async accept operation failed because the underlying transport has been
/// requested to not listen for new connections anymore.
async_accept_not_listening,
/// The requested operation was canceled
operation_canceled,
/// Connection rejected
rejected,
/// Upgrade Required. This happens if an HTTP request is made to a
/// WebSocket++ server that doesn't implement an http handler
upgrade_required,
/// Invalid WebSocket protocol version
invalid_version,
/// Unsupported WebSocket protocol version
unsupported_version,
/// HTTP parse error
http_parse_error,
/// Extension negotiation failed
extension_neg_failed
}; // enum value
@@ -176,6 +205,22 @@ public:
return "The closing handshake timed out";
case error::invalid_port:
return "Invalid URI port";
case error::async_accept_not_listening:
return "Async Accept not listening";
case error::operation_canceled:
return "Operation canceled";
case error::rejected:
return "Connection rejected";
case error::upgrade_required:
return "Upgrade required";
case error::invalid_version:
return "Invalid version";
case error::unsupported_version:
return "Unsupported version";
case error::http_parse_error:
return "HTTP parse error";
case error::extension_neg_failed:
return "Extension negotiation failed";
default:
return "Unknown";
}
@@ -205,21 +250,26 @@ namespace websocketpp {
class exception : public std::exception {
public:
exception(std::string const & msg,
error::value code = error::general)
: m_msg(msg),m_code(code) {}
exception(std::string const & msg, lib::error_code ec = make_error_code(error::general))
: m_msg(msg.empty() ? ec.message() : msg), m_code(ec)
{}
explicit exception(lib::error_code ec)
: m_msg(ec.message()), m_code(ec)
{}
~exception() throw() {}
virtual char const * what() const throw() {
return m_msg.c_str();
}
error::value code() const throw() {
lib::error_code code() const throw() {
return m_code;
}
std::string m_msg;
error::value m_code;
const std::string m_msg;
lib::error_code m_code;
};
} // namespace websocketpp
@@ -1,71 +0,0 @@
/*
* Copyright (c) 2012, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the WebSocket++ Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef WEBSOCKETPP_ERROR_MESSAGE_HPP
#define WEBSOCKETPP_ERROR_MESSAGE_HPP
namespace websocketpp {
/**
* The transport::security::* classes are a set of security/socket related
* policies and support code for the ASIO transport types.
*/
class error_msg {
public:
const std::string& get_msg() const {
return m_error_msg;
}
void set_msg(const std::string& msg) {
m_error_msg = msg;
}
void append_msg(const std::string& msg) {
m_error_msg.append(msg);
}
template <typename T>
void set_msg(const T& thing) {
std::stringsteam val;
val << thing;
this->set_msg(val.str());
}
template <typename T>
void append_msg(const T& thing) {
std::stringsteam val;
val << thing;
this->append_msg(val.str());
}
private:
// error resources
std::string m_error_msg;
};
} // namespace websocketpp
#endif // WEBSOCKETPP_ERROR_MESSAGE_HPP
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -78,7 +78,7 @@ public:
}
};
inline const lib::error_category& get_category() {
inline lib::error_category const & get_category() {
static category instance;
return instance;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -53,10 +53,29 @@ class disabled {
typedef std::pair<lib::error_code,std::string> err_str_pair;
public:
err_str_pair negotiate(http::attribute_list const & attributes) {
/// Negotiate extension
/**
* The disabled extension always fails the negotiation with a disabled
* error.
*
* @param offer Attribute from client's offer
* @return Status code and value to return to remote endpoint
*/
err_str_pair negotiate(http::attribute_list const &) {
return make_pair(make_error_code(error::disabled),std::string());
}
/// Initialize state
/**
* For the disabled extension state initialization is a no-op.
*
* @param is_server True to initialize as a server, false for a client.
* @return A code representing the error that occurred, if any
*/
lib::error_code init(bool) {
return lib::error_code();
}
/// Returns true if the extension is capable of providing
/// permessage_deflate functionality
bool is_implemented() const {
@@ -69,17 +88,35 @@ public:
return false;
}
lib::error_code compress(std::string const & in, std::string & out) {
/// Generate extension offer
/**
* Creates an offer string to include in the Sec-WebSocket-Extensions
* header of outgoing client requests.
*
* @return A WebSocket extension offer string for this extension
*/
std::string generate_offer() const {
return "";
}
/// Compress bytes
/**
* @param [in] in String to compress
* @param [out] out String to append compressed bytes to
* @return Error or status code
*/
lib::error_code compress(std::string const &, std::string &) {
return make_error_code(error::disabled);
}
lib::error_code decompress(uint8_t const * buf, size_t len,
std::string & out)
{
return make_error_code(error::disabled);
}
lib::error_code decompress(std::string const & in, std::string & out) {
/// Decompress bytes
/**
* @param buf Byte buffer to decompress
* @param len Length of buf
* @param out String to append decompressed bytes to
* @return Error or status code
*/
lib::error_code decompress(uint8_t const *, size_t, std::string &) {
return make_error_code(error::disabled);
}
};
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,8 +28,10 @@
#ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
#define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/error.hpp>
@@ -44,10 +46,14 @@
namespace websocketpp {
namespace extensions {
/// Implimentation of the draft permessage-deflate WebSocket extension
/// Implementation of the draft permessage-deflate WebSocket extension
/**
* ### permessage-deflate interface
*
* **init**\n
* `lib::error_code init(bool is_server)`\n
* Performs initialization
*
* **is_implimented**\n
* `bool is_implimented()`\n
* Returns whether or not the object impliments the extension or not
@@ -134,7 +140,7 @@ public:
case zlib_error:
return "A zlib function returned an error";
case uninitialized:
return "Object must be initialized before use";
return "Deflate extension must be initialized before use";
default:
return "Unknown permessage-compress error";
}
@@ -142,13 +148,13 @@ public:
};
/// Get a reference to a static copy of the permessage-deflate error category
lib::error_category const & get_category() {
inline lib::error_category const & get_category() {
static category instance;
return instance;
}
/// Create an error code in the permessage-deflate category
lib::error_code make_error_code(error::value e) {
inline lib::error_code make_error_code(error::value e) {
return lib::error_code(static_cast<int>(e), get_category());
}
@@ -168,19 +174,19 @@ namespace websocketpp {
namespace extensions {
namespace permessage_deflate {
/// Default value for s2c_max_window_bits as defined by RFC6455
static uint8_t const default_s2c_max_window_bits = 15;
/// Minimum value for s2c_max_window_bits as defined by RFC6455
static uint8_t const min_s2c_max_window_bits = 8;
/// Maximum value for s2c_max_window_bits as defined by RFC6455
static uint8_t const max_s2c_max_window_bits = 15;
/// Default value for server_max_window_bits as defined by draft 17
static uint8_t const default_server_max_window_bits = 15;
/// Minimum value for server_max_window_bits as defined by draft 17
static uint8_t const min_server_max_window_bits = 8;
/// Maximum value for server_max_window_bits as defined by draft 17
static uint8_t const max_server_max_window_bits = 15;
/// Default value for c2s_max_window_bits as defined by RFC6455
static uint8_t const default_c2s_max_window_bits = 15;
/// Minimum value for c2s_max_window_bits as defined by RFC6455
static uint8_t const min_c2s_max_window_bits = 8;
/// Maximum value for c2s_max_window_bits as defined by RFC6455
static uint8_t const max_c2s_max_window_bits = 15;
/// Default value for client_max_window_bits as defined by draft 17
static uint8_t const default_client_max_window_bits = 15;
/// Minimum value for client_max_window_bits as defined by draft 17
static uint8_t const min_client_max_window_bits = 8;
/// Maximum value for client_max_window_bits as defined by draft 17
static uint8_t const max_client_max_window_bits = 15;
namespace mode {
enum value {
@@ -200,12 +206,12 @@ class enabled {
public:
enabled()
: m_enabled(false)
, m_s2c_no_context_takeover(false)
, m_c2s_no_context_takeover(false)
, m_s2c_max_window_bits(15)
, m_c2s_max_window_bits(15)
, m_s2c_max_window_bits_mode(mode::accept)
, m_c2s_max_window_bits_mode(mode::accept)
, m_server_no_context_takeover(false)
, m_client_no_context_takeover(false)
, m_server_max_window_bits(15)
, m_client_max_window_bits(15)
, m_server_max_window_bits_mode(mode::accept)
, m_client_max_window_bits_mode(mode::accept)
, m_initialized(false)
, m_compress_buffer_size(16384)
{
@@ -242,20 +248,25 @@ public:
/// Initialize zlib state
/**
* Note: this should be called *after* the negotiation methods. It will use
* information from the negotiation to determine how to initialize the zlib
* data structures.
*
* @todo memory level, strategy, etc are hardcoded
* @todo server detection is hardcoded
*
* @param is_server True to initialize as a server, false for a client.
* @return A code representing the error that occurred, if any
*/
lib::error_code init() {
lib::error_code init(bool is_server) {
uint8_t deflate_bits;
uint8_t inflate_bits;
if (true /*is_server*/) {
deflate_bits = m_s2c_max_window_bits;
inflate_bits = m_c2s_max_window_bits;
if (is_server) {
deflate_bits = m_server_max_window_bits;
inflate_bits = m_client_max_window_bits;
} else {
deflate_bits = m_c2s_max_window_bits;
inflate_bits = m_s2c_max_window_bits;
deflate_bits = m_client_max_window_bits;
inflate_bits = m_server_max_window_bits;
}
int ret = deflateInit2(
@@ -263,8 +274,8 @@ public:
Z_DEFAULT_COMPRESSION,
Z_DEFLATED,
-1*deflate_bits,
8, // memory level 1-9
/*Z_DEFAULT_STRATEGY*/Z_FIXED
4, // memory level 1-9
Z_DEFAULT_STRATEGY
);
if (ret != Z_OK) {
@@ -281,15 +292,22 @@ public:
}
m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
if ((m_server_no_context_takeover && is_server) ||
(m_client_no_context_takeover && !is_server))
{
m_flush = Z_FULL_FLUSH;
} else {
m_flush = Z_SYNC_FLUSH;
}
m_initialized = true;
return lib::error_code();
}
/// Test if this object impliments the permessage-deflate specification
/// Test if this object implements the permessage-deflate specification
/**
* Because this object does impliment it, it will always return true.
* Because this object does implieent it, it will always return true.
*
* @return Whether or not this object impliments permessage-deflate
* @return Whether or not this object implements permessage-deflate
*/
bool is_implemented() const {
return true;
@@ -327,8 +345,8 @@ public:
* the option will be in use so they can optimize resource usage if they
* are able.
*/
void enable_s2c_no_context_takeover() {
m_s2c_no_context_takeover = true;
void enable_server_no_context_takeover() {
m_server_no_context_takeover = true;
}
/// Reset client's outgoing LZ77 sliding window for each new message
@@ -346,8 +364,8 @@ public:
* This option is supported by all compliant clients and servers. Enabling
* it via either endpoint should be sufficient to ensure it is used.
*/
void enable_c2s_no_context_takeover() {
m_c2s_no_context_takeover = true;
void enable_client_no_context_takeover() {
m_client_no_context_takeover = true;
}
/// Limit server LZ77 sliding window size
@@ -372,12 +390,12 @@ public:
* @param mode The mode to use for negotiating this parameter
* @return A status code
*/
lib::error_code set_s2c_max_window_bits(uint8_t bits, mode::value mode) {
if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) {
if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
return error::make_error_code(error::invalid_max_window_bits);
}
m_s2c_max_window_bits = bits;
m_s2c_max_window_bits_mode = mode;
m_server_max_window_bits = bits;
m_server_max_window_bits_mode = mode;
return lib::error_code();
}
@@ -403,12 +421,12 @@ public:
* @param mode The mode to use for negotiating this parameter
* @return A status code
*/
lib::error_code set_c2s_max_window_bits(uint8_t bits, mode::value mode) {
if (bits < min_c2s_max_window_bits || bits > max_c2s_max_window_bits) {
lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) {
if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
return error::make_error_code(error::invalid_max_window_bits);
}
m_c2s_max_window_bits = bits;
m_c2s_max_window_bits_mode = mode;
m_client_max_window_bits = bits;
m_client_max_window_bits_mode = mode;
return lib::error_code();
}
@@ -421,7 +439,8 @@ public:
* @return A WebSocket extension offer string for this extension
*/
std::string generate_offer() const {
return "";
// TODO: this should be dynamically generated based on user settings
return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
}
/// Validate extension response
@@ -432,8 +451,8 @@ public:
* @param response The server response attribute list to validate
* @return Validation error or 0 on success
*/
lib::error_code validate_offer(http::attribute_list const & response) {
lib::error_code validate_offer(http::attribute_list const &) {
return lib::error_code();
}
/// Negotiate extension
@@ -450,14 +469,14 @@ public:
http::attribute_list::const_iterator it;
for (it = offer.begin(); it != offer.end(); ++it) {
if (it->first == "s2c_no_context_takeover") {
negotiate_s2c_no_context_takeover(it->second,ret.first);
} else if (it->first == "c2s_no_context_takeover") {
negotiate_c2s_no_context_takeover(it->second,ret.first);
} else if (it->first == "s2c_max_window_bits") {
negotiate_s2c_max_window_bits(it->second,ret.first);
} else if (it->first == "c2s_max_window_bits") {
negotiate_c2s_max_window_bits(it->second,ret.first);
if (it->first == "server_no_context_takeover") {
negotiate_server_no_context_takeover(it->second,ret.first);
} else if (it->first == "client_no_context_takeover") {
negotiate_client_no_context_takeover(it->second,ret.first);
} else if (it->first == "server_max_window_bits") {
negotiate_server_max_window_bits(it->second,ret.first);
} else if (it->first == "client_max_window_bits") {
negotiate_client_max_window_bits(it->second,ret.first);
} else {
ret.first = make_error_code(error::invalid_attributes);
}
@@ -477,6 +496,9 @@ public:
/// Compress bytes
/**
* @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames
* on 64 bit machines.
*
* @param [in] in String to compress
* @param [out] out String to append compressed bytes to
* @return Error or status code
@@ -487,9 +509,14 @@ public:
}
size_t output;
int ret;
m_dstate.avail_out = m_compress_buffer_size;
if (in.empty()) {
uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
out.append((char *)(buf),6);
return lib::error_code();
}
m_dstate.avail_in = in.size();
m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
do {
@@ -497,7 +524,7 @@ public:
m_dstate.avail_out = m_compress_buffer_size;
m_dstate.next_out = m_compress_buffer.get();
ret = deflate(&m_dstate, Z_SYNC_FLUSH);
deflate(&m_dstate, m_flush);
output = m_compress_buffer_size - m_dstate.avail_out;
@@ -552,35 +579,35 @@ private:
std::string generate_response() {
std::string ret = "permessage-deflate";
if (m_s2c_no_context_takeover) {
ret += "; s2c_no_context_takeover";
if (m_server_no_context_takeover) {
ret += "; server_no_context_takeover";
}
if (m_c2s_no_context_takeover) {
ret += "; c2s_no_context_takeover";
if (m_client_no_context_takeover) {
ret += "; client_no_context_takeover";
}
if (m_s2c_max_window_bits < default_s2c_max_window_bits) {
if (m_server_max_window_bits < default_server_max_window_bits) {
std::stringstream s;
s << int(m_s2c_max_window_bits);
ret += "; s2c_max_window_bits="+s.str();
s << int(m_server_max_window_bits);
ret += "; server_max_window_bits="+s.str();
}
if (m_c2s_max_window_bits < default_c2s_max_window_bits) {
if (m_client_max_window_bits < default_client_max_window_bits) {
std::stringstream s;
s << int(m_c2s_max_window_bits);
ret += "; c2s_max_window_bits="+s.str();
s << int(m_client_max_window_bits);
ret += "; client_max_window_bits="+s.str();
}
return ret;
}
/// Negotiate s2c_no_context_takeover attribute
/// Negotiate server_no_context_takeover attribute
/**
* @param [in] value The value of the attribute from the offer
* @param [out] ec A reference to the error code to return errors via
*/
void negotiate_s2c_no_context_takeover(std::string const & value,
void negotiate_server_no_context_takeover(std::string const & value,
lib::error_code & ec)
{
if (!value.empty()) {
@@ -588,15 +615,15 @@ private:
return;
}
m_s2c_no_context_takeover = true;
m_server_no_context_takeover = true;
}
/// Negotiate c2s_no_context_takeover attribute
/// Negotiate client_no_context_takeover attribute
/**
* @param [in] value The value of the attribute from the offer
* @param [out] ec A reference to the error code to return errors via
*/
void negotiate_c2s_no_context_takeover(std::string const & value,
void negotiate_client_no_context_takeover(std::string const & value,
lib::error_code & ec)
{
if (!value.empty()) {
@@ -604,13 +631,13 @@ private:
return;
}
m_c2s_no_context_takeover = true;
m_client_no_context_takeover = true;
}
/// Negotiate s2c_max_window_bits attribute
/// Negotiate server_max_window_bits attribute
/**
* When this method starts, m_s2c_max_window_bits will contain the server's
* preferred value and m_s2c_max_window_bits_mode will contain the mode the
* When this method starts, m_server_max_window_bits will contain the server's
* preferred value and m_server_max_window_bits_mode will contain the mode the
* server wants to use to for negotiation. `value` contains the value the
* client requested that we use.
*
@@ -623,39 +650,39 @@ private:
* @param [in] value The value of the attribute from the offer
* @param [out] ec A reference to the error code to return errors via
*/
void negotiate_s2c_max_window_bits(std::string const & value,
void negotiate_server_max_window_bits(std::string const & value,
lib::error_code & ec)
{
uint8_t bits = uint8_t(atoi(value.c_str()));
if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
ec = make_error_code(error::invalid_attribute_value);
m_s2c_max_window_bits = default_s2c_max_window_bits;
m_server_max_window_bits = default_server_max_window_bits;
return;
}
switch (m_s2c_max_window_bits_mode) {
switch (m_server_max_window_bits_mode) {
case mode::decline:
m_s2c_max_window_bits = default_s2c_max_window_bits;
m_server_max_window_bits = default_server_max_window_bits;
break;
case mode::accept:
m_s2c_max_window_bits = bits;
m_server_max_window_bits = bits;
break;
case mode::largest:
m_s2c_max_window_bits = std::min(bits,m_s2c_max_window_bits);
m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
break;
case mode::smallest:
m_s2c_max_window_bits = min_s2c_max_window_bits;
m_server_max_window_bits = min_server_max_window_bits;
break;
default:
ec = make_error_code(error::invalid_mode);
m_s2c_max_window_bits = default_s2c_max_window_bits;
m_server_max_window_bits = default_server_max_window_bits;
}
}
/// Negotiate c2s_max_window_bits attribute
/// Negotiate client_max_window_bits attribute
/**
* When this method starts, m_c2s_max_window_bits and m_c2s_max_window_mode
* When this method starts, m_client_max_window_bits and m_c2s_max_window_mode
* will contain the server's preferred values for window size and
* negotiation mode.
*
@@ -668,49 +695,50 @@ private:
* @param [in] value The value of the attribute from the offer
* @param [out] ec A reference to the error code to return errors via
*/
void negotiate_c2s_max_window_bits(std::string const & value,
void negotiate_client_max_window_bits(std::string const & value,
lib::error_code & ec)
{
uint8_t bits = uint8_t(atoi(value.c_str()));
if (value.empty()) {
bits = default_c2s_max_window_bits;
} else if (bits < min_c2s_max_window_bits ||
bits > max_c2s_max_window_bits)
bits = default_client_max_window_bits;
} else if (bits < min_client_max_window_bits ||
bits > max_client_max_window_bits)
{
ec = make_error_code(error::invalid_attribute_value);
m_c2s_max_window_bits = default_c2s_max_window_bits;
m_client_max_window_bits = default_client_max_window_bits;
return;
}
switch (m_c2s_max_window_bits_mode) {
switch (m_client_max_window_bits_mode) {
case mode::decline:
m_c2s_max_window_bits = default_c2s_max_window_bits;
m_client_max_window_bits = default_client_max_window_bits;
break;
case mode::accept:
m_c2s_max_window_bits = bits;
m_client_max_window_bits = bits;
break;
case mode::largest:
m_c2s_max_window_bits = std::min(bits,m_c2s_max_window_bits);
m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
break;
case mode::smallest:
m_c2s_max_window_bits = min_c2s_max_window_bits;
m_client_max_window_bits = min_client_max_window_bits;
break;
default:
ec = make_error_code(error::invalid_mode);
m_c2s_max_window_bits = default_c2s_max_window_bits;
m_client_max_window_bits = default_client_max_window_bits;
}
}
bool m_enabled;
bool m_s2c_no_context_takeover;
bool m_c2s_no_context_takeover;
uint8_t m_s2c_max_window_bits;
uint8_t m_c2s_max_window_bits;
mode::value m_s2c_max_window_bits_mode;
mode::value m_c2s_max_window_bits_mode;
bool m_server_no_context_takeover;
bool m_client_no_context_takeover;
uint8_t m_server_max_window_bits;
uint8_t m_client_max_window_bits;
mode::value m_server_max_window_bits_mode;
mode::value m_client_max_window_bits_mode;
bool m_initialized;
int m_flush;
size_t m_compress_buffer_size;
lib::unique_ptr_uchar_array m_compress_buffer;
z_stream m_dstate;
+4 -32
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,6 +29,7 @@
#define WEBSOCKETPP_FRAME_HPP
#include <algorithm>
#include <string>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/network.hpp>
@@ -266,7 +267,7 @@ private:
}
uint64_converter temp64;
temp64.i = lib::net::htonll(payload_size);
temp64.i = lib::net::_htonll(payload_size);
std::copy(temp64.c+payload_offset,temp64.c+8,bytes);
return 8-payload_offset;
@@ -456,35 +457,6 @@ inline size_t get_header_len(basic_header const & h) {
return size;
}
/// Set the frame's size
/**
* @param [out] h The basic header to set.
* @param [out] eh The extended header to set.
* @param [in] The size to set.
* @return What error occurred, if any.
*/
inline lib::error_code set_size(basic_header & h, extended_header & eh, uint64_t
size)
{
// make sure value isn't too big
uint8_t basic_value;
if (size <= limits::payload_size_basic) {
basic_value = static_cast<uint8_t>(size);
} else if (size <= limits::payload_size_extended) {
basic_value = payload_size_code_16bit;
} else if (size <= limits::payload_size_jumbo) {
basic_value = payload_size_code_64bit;
} else {
// error
return lib::error_code();
}
h.b1 = (basic_value & BHB1_PAYLOAD) | (h.b1 & BHB1_MASK);
return lib::error_code();
}
/// Calculate the offset location of the masking key within the extended header
/**
* Calculate the offset location of the masking key within the extended header
@@ -583,7 +555,7 @@ inline uint16_t get_extended_size(const extended_header &e) {
inline uint64_t get_jumbo_size(const extended_header &e) {
uint64_converter temp64;
std::copy(e.bytes,e.bytes+8,temp64.c);
return lib::net::ntohll(temp64.i);
return lib::net::_ntohll(temp64.i);
}
/// Extract the full payload size field from a WebSocket header
+11 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,11 +28,14 @@
#ifndef HTTP_CONSTANTS_HPP
#define HTTP_CONSTANTS_HPP
#include <exception>
#include <map>
#include <string>
#include <vector>
#include <utility>
namespace websocketpp {
/// HTTP handling support
namespace http {
/// The type of an HTTP attribute list
/**
@@ -56,10 +59,13 @@ namespace http {
static char const header_separator[] = ":";
/// Literal value of an empty header
static std::string const empty_header = "";
static std::string const empty_header;
/// Maximum size in bytes before rejecting an HTTP header as too big.
size_t const max_header_size = 16000;
/// Default Maximum size in bytes for HTTP message bodies.
size_t const max_body_size = 32000000;
/// Number of bytes to use for temporary istream read buffers
size_t const istream_buffer = 512;
@@ -255,7 +261,7 @@ namespace http {
case internal_server_error:
return "Internal Server Error";
case not_implemented:
return "Not Implimented";
return "Not Implemented";
case bad_gateway:
return "Bad Gateway";
case service_unavailable:
@@ -278,8 +284,8 @@ namespace http {
public:
exception(const std::string& log_msg,
status_code::value error_code,
const std::string& error_msg = "",
const std::string& body = "")
const std::string& error_msg = std::string(),
const std::string& body = std::string())
: m_msg(log_msg)
, m_error_msg(error_msg)
, m_body(body)
+45 -20
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,6 +29,8 @@
#define HTTP_PARSER_IMPL_HPP
#include <algorithm>
#include <cstdlib>
#include <istream>
#include <sstream>
#include <string>
@@ -69,7 +71,7 @@ inline void parser::append_header(std::string const & key, std::string const &
throw exception("Invalid header name",status_code::bad_request);
}
if (this->get_header(key) == "") {
if (this->get_header(key).empty()) {
m_headers[key] = val;
} else {
m_headers[key] += ", " + val;
@@ -89,10 +91,13 @@ inline void parser::remove_header(std::string const & key) {
inline void parser::set_body(std::string const & value) {
if (value.size() == 0) {
remove_header("Content-Length");
m_body = "";
m_body.clear();
return;
}
// TODO: should this method respect the max size? If so how should errors
// be indicated?
std::stringstream len;
len << value.size();
replace_header("Content-Length", len.str());
@@ -111,26 +116,46 @@ inline bool parser::parse_parameter_list(std::string const & in,
return (it == in.begin());
}
inline bool parser::parse_headers(std::istream & s) {
std::string header;
std::string::size_type end;
// get headers
while (std::getline(s, header) && header != "\r") {
if (header[header.size()-1] != '\r') {
continue; // ignore malformed header lines?
} else {
header.erase(header.end()-1);
}
end = header.find(header_separator,0);
if (end != std::string::npos) {
append_header(header.substr(0,end),header.substr(end+2));
inline bool parser::prepare_body() {
if (!get_header("Content-Length").empty()) {
std::string const & cl_header = get_header("Content-Length");
char * end;
// TODO: not 100% sure what the compatibility of this method is. Also,
// I believe this will only work up to 32bit sizes. Is there a need for
// > 4GiB HTTP payloads?
m_body_bytes_needed = std::strtoul(cl_header.c_str(),&end,10);
if (m_body_bytes_needed > m_body_bytes_max) {
throw exception("HTTP message body too large",
status_code::request_entity_too_large);
}
m_body_encoding = body_encoding::plain;
return true;
} else if (get_header("Transfer-Encoding") == "chunked") {
// TODO
//m_body_encoding = body_encoding::chunked;
return false;
} else {
return false;
}
}
return true;
inline size_t parser::process_body(char const * buf, size_t len) {
if (m_body_encoding == body_encoding::plain) {
size_t processed = (std::min)(m_body_bytes_needed,len);
m_body.append(buf,processed);
m_body_bytes_needed -= processed;
return processed;
} else if (m_body_encoding == body_encoding::chunked) {
// TODO:
throw exception("Unexpected body encoding",
status_code::internal_server_error);
} else {
throw exception("Unexpected body encoding",
status_code::internal_server_error);
}
}
inline void parser::process_header(std::string::iterator begin,
+57 -57
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -30,6 +30,7 @@
#include <algorithm>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
@@ -37,40 +38,17 @@ namespace websocketpp {
namespace http {
namespace parser {
inline bool request::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);
}
inline size_t request::consume(const char *buf, size_t len) {
inline size_t request::consume(char const * buf, size_t len) {
size_t bytes_processed;
if (m_ready) {return 0;}
if (m_buf->size() + len > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
if (m_body_bytes_needed > 0) {
bytes_processed = process_body(buf,len);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
}
// copy new header bytes into buffer
@@ -88,15 +66,21 @@ inline size_t request::consume(const char *buf, size_t len) {
header_delimiter,
header_delimiter+sizeof(header_delimiter)-1
);
//std::cout << "mark5: " << end-begin << std::endl;
//std::cout << "mark6: " << sizeof(header_delimiter) << std::endl;
m_header_bytes += (end-begin+sizeof(header_delimiter));
if (m_header_bytes > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
}
if (end == m_buf->end()) {
// we are out of bytes. Discard the processed bytes and copy the
// remaining unprecessed bytes to the beginning of the buffer
std::copy(begin,end,m_buf->begin());
m_buf->resize(static_cast<std::string::size_type>(end-begin));
m_header_bytes -= m_buf->size();
return len;
}
@@ -104,12 +88,11 @@ inline size_t request::consume(const char *buf, size_t len) {
//the range [begin,end) now represents a line to be processed.
if (end-begin == 0) {
// we got a blank line
if (m_method.empty() || get_header("Host") == "") {
if (m_method.empty() || get_header("Host").empty()) {
throw exception("Incomplete Request",status_code::bad_request);
}
m_ready = true;
size_t bytes_processed = (
bytes_processed = (
len - static_cast<std::string::size_type>(m_buf->end()-end)
+ sizeof(header_delimiter) - 1
);
@@ -117,8 +100,22 @@ inline size_t request::consume(const char *buf, size_t len) {
// frees memory used temporarily during request parsing
m_buf.reset();
// return number of bytes processed (starting bytes - bytes left)
return bytes_processed;
// if this was not an upgrade request and has a content length
// continue capturing content-length bytes and expose them as a
// request body.
if (prepare_body()) {
bytes_processed += process_body(buf+bytes_processed,len-bytes_processed);
if (body_ready()) {
m_ready = true;
}
return bytes_processed;
} else {
m_ready = true;
// return number of bytes processed (starting bytes - bytes left)
return bytes_processed;
}
} else {
if (m_method.empty()) {
this->process(begin,end);
@@ -127,21 +124,31 @@ inline size_t request::consume(const char *buf, size_t len) {
}
}
begin = end+sizeof(header_delimiter)-1;
begin = end+(sizeof(header_delimiter)-1);
}
}
inline std::string request::raw() {
inline std::string request::raw() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream raw;
std::stringstream ret;
raw << m_method << " " << m_uri << " " << get_version() << "\r\n";
raw << raw_headers() << "\r\n" << m_body;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n" << m_body;
return raw.str();
return ret.str();
}
inline void request::set_method(const std::string& method) {
inline std::string request::raw_head() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream ret;
ret << m_method << " " << m_uri << " " << get_version() << "\r\n";
ret << raw_headers() << "\r\n";
return ret.str();
}
inline void request::set_method(std::string const & method) {
if (std::find_if(method.begin(),method.end(),is_not_token_char) != method.end()) {
throw exception("Invalid method token.",status_code::bad_request);
}
@@ -149,14 +156,7 @@ inline void request::set_method(const std::string& method) {
m_method = method;
}
/// Set HTTP body
/**
* Sets the body of the HTTP object and fills in the appropriate content length
* header
*
* @param value The value to set the body to.
*/
inline void request::set_uri(const std::string& uri) {
inline void request::set_uri(std::string const & uri) {
// TODO: validation?
m_uri = uri;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,7 +29,9 @@
#define HTTP_PARSER_RESPONSE_IMPL_HPP
#include <algorithm>
#include <istream>
#include <sstream>
#include <string>
#include <websocketpp/http/parser.hpp>
@@ -37,19 +39,13 @@ namespace websocketpp {
namespace http {
namespace parser {
inline size_t response::consume(const char *buf, size_t len) {
inline size_t response::consume(char const * buf, size_t len) {
if (m_state == DONE) {return 0;}
if (m_state == BODY) {
return this->process_body(buf,len);
}
if (m_read + len > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
}
// copy new header bytes into buffer
m_buf->append(buf,len);
@@ -67,13 +63,22 @@ inline size_t response::consume(const char *buf, size_t len) {
header_delimiter + sizeof(header_delimiter) - 1
);
m_header_bytes += (end-begin+sizeof(header_delimiter));
if (m_header_bytes > max_header_size) {
// exceeded max header size
throw exception("Maximum header size exceeded.",
status_code::request_header_fields_too_large);
}
if (end == m_buf->end()) {
// we are out of bytes. Discard the processed bytes and copy the
// remaining unprecessed bytes to the beginning of the buffer
std::copy(begin,end,m_buf->begin());
m_buf->resize(static_cast<std::string::size_type>(end-begin));
m_read +=len;
m_read += len;
m_header_bytes -= m_buf->size();
return len;
}
@@ -89,7 +94,7 @@ inline size_t response::consume(const char *buf, size_t len) {
// TODO: grab content-length
std::string length = get_header("Content-Length");
if (length == "") {
if (length.empty()) {
// no content length found, read indefinitely
m_read = 0;
} else {
@@ -170,45 +175,17 @@ inline size_t response::consume(std::istream & s) {
return total;
}
inline bool response::parse_complete(std::istream& s) {
// parse a complete header (ie \r\n\r\n MUST be in the input stream)
std::string response;
// get status line
std::getline(s, response);
if (response[response.size()-1] == '\r') {
response.erase(response.end()-1);
std::stringstream ss(response);
std::string str_val;
int int_val;
char char_val[256];
ss >> str_val;
set_version(str_val);
ss >> int_val;
ss.getline(char_val,256);
set_status(status_code::value(int_val),std::string(char_val));
} else {
return false;
}
return parse_headers(s);
}
inline std::string response::raw() const {
// TODO: validation. Make sure all required fields have been set?
std::stringstream raw;
std::stringstream ret;
raw << get_version() << " " << m_status_code << " " << m_status_msg;
raw << "\r\n" << raw_headers() << "\r\n";
ret << get_version() << " " << m_status_code << " " << m_status_msg;
ret << "\r\n" << raw_headers() << "\r\n";
raw << m_body;
ret << m_body;
return raw.str();
return ret.str();
}
inline void response::set_status(status_code::value code) {
@@ -217,7 +194,7 @@ inline void response::set_status(status_code::value code) {
m_status_msg = get_string(code);
}
inline void response::set_status(status_code::value code, const std::string&
inline void response::set_status(status_code::value code, std::string const &
msg)
{
// TODO: validation?
@@ -255,7 +232,7 @@ inline void response::process(std::string::iterator begin,
set_status(status_code::value(code),std::string(cursor_end+1,end));
}
inline size_t response::process_body(const char *buf, size_t len) {
inline size_t response::process_body(char const * buf, size_t len) {
// If no content length was set then we read forever and never set m_ready
if (m_read == 0) {
//m_body.append(buf,len);
+105 -21
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,8 +29,9 @@
#define HTTP_PARSER_HPP
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <websocketpp/utilities.hpp>
#include <websocketpp/http/constants.hpp>
@@ -48,6 +49,14 @@ namespace state {
};
}
namespace body_encoding {
enum value {
unknown,
plain,
chunked
};
}
typedef std::map<std::string, std::string, utility::ci_less > header_list;
/// Read and return the next token in the stream
@@ -221,7 +230,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
cursor = http::parser::extract_all_lws(cursor,end);
ret = http::parser::extract_token(cursor,end);
if (ret.first == "") {
if (ret.first.empty()) {
// error: expected a token
return begin;
} else {
@@ -233,7 +242,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
if (cursor == end || *cursor != '=') {
// if there is an equals sign, read the attribute value. Otherwise
// record a blank value and continue
attributes[name] = "";
attributes[name].clear();
continue;
}
@@ -254,7 +263,7 @@ InputIterator extract_attributes(InputIterator begin, InputIterator end,
}
ret = http::parser::extract_token(cursor,end);
if (ret.first == "") {
if (ret.first.empty()) {
// error : expected token or quoted string
return begin;
} else {
@@ -312,7 +321,7 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
ret = http::parser::extract_token(cursor,end);
if (ret.first == "") {
if (ret.first.empty()) {
// error: expected a token
return begin;
} else {
@@ -369,9 +378,16 @@ InputIterator extract_parameters(InputIterator begin, InputIterator end,
inline std::string strip_lws(std::string const & input) {
std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
std::string::const_reverse_iterator end = extract_all_lws(input.rbegin(),input.rend());
if (begin == input.end()) {
return std::string();
}
return std::string(begin,end.base());
std::string::const_reverse_iterator rbegin = extract_all_lws(input.rbegin(),input.rend());
if (rbegin == input.rend()) {
return std::string();
}
return std::string(begin,rbegin.base());
}
/// Base HTTP parser
@@ -381,6 +397,12 @@ inline std::string strip_lws(std::string const & input) {
*/
class parser {
public:
parser()
: m_header_bytes(0)
, m_body_bytes_needed(0)
, m_body_bytes_max(max_body_size)
, m_body_encoding(body_encoding::unknown) {}
/// Get the HTTP version string
/**
* @return The version string for this parser
@@ -464,12 +486,11 @@ public:
*/
void remove_header(std::string const & key);
/// Set HTTP body
/// Get HTTP body
/**
* Sets the body of the HTTP object and fills in the appropriate content
* length header.
* Gets the body of the HTTP object
*
* @param [in] value The value to set the body to.
* @return The body of the HTTP message.
*/
std::string const & get_body() const {
return m_body;
@@ -486,6 +507,32 @@ public:
*/
void set_body(std::string const & value);
/// Get body size limit
/**
* Retrieves the maximum number of bytes to parse & buffer before canceling
* a request.
*
* @since 0.5.0
*
* @return The maximum length of a message body.
*/
size_t get_max_body_size() const {
return m_body_bytes_max;
}
/// Set body size limit
/**
* Set the maximum number of bytes to parse and buffer before canceling a
* request.
*
* @since 0.5.0
*
* @param value The size to set the max body length to.
*/
void set_max_body_size(size_t value) {
m_body_bytes_max = value;
}
/// Extract an HTTP parameter list from a string.
/**
* @param [in] in The input string.
@@ -495,14 +542,6 @@ public:
bool parse_parameter_list(std::string const & in, parameter_list & out)
const;
protected:
/// Parse headers from an istream
/**
* @deprecated Use process_header instead.
*
* @param [in] s The istream to extract headers from.
*/
bool parse_headers(std::istream & s);
/// Process a header line
/**
* @todo Update this method to be exception free.
@@ -512,6 +551,45 @@ protected:
*/
void process_header(std::string::iterator begin, std::string::iterator end);
/// Prepare the parser to begin parsing body data
/**
* Inspects headers to determine if the message has a body that needs to be
* read. If so, sets up the necessary state, otherwise returns false. If
* this method returns true and loading the message body is desired call
* `process_body` until it returns zero bytes or an error.
*
* Must not be called until after all headers have been processed.
*
* @since 0.5.0
*
* @return True if more bytes are needed to load the body, false otherwise.
*/
bool prepare_body();
/// Process body data
/**
* Parses body data.
*
* @since 0.5.0
*
* @param [in] begin An iterator to the beginning of the sequence.
* @param [in] end An iterator to the end of the sequence.
* @return The number of bytes processed
*/
size_t process_body(char const * buf, size_t len);
/// Check if the parser is done parsing the body
/**
* Behavior before a call to `prepare_body` is undefined.
*
* @since 0.5.0
*
* @return True if the message body has been completed loaded.
*/
bool body_ready() const {
return (m_body_bytes_needed == 0);
}
/// Generate and return the HTTP headers as a string
/**
* Each headers will be followed by the \r\n sequence including the last one.
@@ -523,7 +601,13 @@ protected:
std::string m_version;
header_list m_headers;
std::string m_body;
size_t m_header_bytes;
std::string m_body;
size_t m_body_bytes_needed;
size_t m_body_bytes_max;
body_encoding::value m_body_encoding;
};
} // namespace parser
+14 -12
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,6 +28,8 @@
#ifndef HTTP_PARSER_REQUEST_HPP
#define HTTP_PARSER_REQUEST_HPP
#include <string>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/http/parser.hpp>
@@ -51,12 +53,9 @@ public:
typedef lib::shared_ptr<type> ptr;
request()
: m_buf(new std::string())
: m_buf(lib::make_shared<std::string>())
, m_ready(false) {}
/// DEPRECATED parse a complete header (\r\n\r\n MUST be in the istream)
bool parse_complete(std::istream& s);
/// Process bytes in the input buffer
/**
* Process up to len bytes from input buffer buf. Returns the number of
@@ -77,29 +76,32 @@ public:
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(const char *buf, size_t len);
size_t consume(char const * buf, size_t len);
/// Returns whether or not the request is ready for reading.
bool ready() const {
return m_ready;
}
/// Returns the full raw request
std::string raw();
/// Returns the full raw request (including the body)
std::string raw() const;
/// Returns the raw request headers only (similar to an HTTP HEAD request)
std::string raw_head() const;
/// Set the HTTP method. Must be a valid HTTP token
void set_method(const std::string& method);
void set_method(std::string const & method);
/// Return the request method
const std::string& get_method() const {
std::string const & get_method() const {
return m_method;
}
/// Set the HTTP uri. Must be a valid HTTP uri
void set_uri(const std::string& uri);
void set_uri(std::string const & uri);
/// Return the requested URI
const std::string& get_uri() const {
std::string const & get_uri() const {
return m_uri;
}
+28 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,6 +28,9 @@
#ifndef HTTP_PARSER_RESPONSE_HPP
#define HTTP_PARSER_RESPONSE_HPP
#include <iostream>
#include <string>
#include <websocketpp/http/parser.hpp>
namespace websocketpp {
@@ -58,7 +61,7 @@ public:
response()
: m_read(0)
, m_buf(new std::string())
, m_buf(lib::make_shared<std::string>())
, m_status_code(status_code::uninitialized)
, m_state(RESPONSE_LINE) {}
@@ -82,8 +85,28 @@ public:
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(const char *buf, size_t len);
size_t consume(char const * buf, size_t len);
/// Process bytes in the input buffer (istream version)
/**
* Process bytes from istream s. Returns the number of bytes processed.
* Bytes left unprocessed means bytes left over after the final header
* delimiters.
*
* Consume is a streaming processor. It may be called multiple times on one
* response and the full headers need not be available before processing can
* begin. If the end of the response was reached during this call to consume
* the ready flag will be set. Further calls to consume once ready will be
* ignored.
*
* Consume will throw an http::exception in the case of an error. Typical
* error reasons include malformed responses, incomplete responses, and max
* header size being reached.
*
* @param buf Pointer to byte buffer
* @param len Size of byte buffer
* @return Number of bytes processed.
*/
size_t consume(std::istream & s);
/// Returns true if the response is ready.
@@ -99,9 +122,6 @@ public:
return (m_state == BODY || m_state == DONE);
}
/// DEPRECATED parse a complete response from a pre-delimited istream
bool parse_complete(std::istream& s);
/// Returns the full raw response
std::string raw() const;
@@ -126,7 +146,7 @@ public:
* @param code Code to set
* @param msg Message to set
*/
void set_status(status_code::value code, const std::string& msg);
void set_status(status_code::value code, std::string const & msg);
/// Return the response status code
status_code::value get_status_code() const {
@@ -142,7 +162,7 @@ private:
void process(std::string::iterator begin, std::string::iterator end);
/// Helper function for processing body bytes
size_t process_body(const char *buf, size_t len);
size_t process_body(char const * buf, size_t len);
enum state {
RESPONSE_LINE = 0,
File diff suppressed because it is too large. Load diff
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,6 +28,8 @@
#ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP
#define WEBSOCKETPP_ENDPOINT_IMPL_HPP
#include <string>
namespace websocketpp {
template <typename connection, typename config>
@@ -42,8 +44,8 @@ endpoint<connection,config>::create_connection() {
//scoped_lock_type guard(m_mutex);
// Create a connection on the heap and manage it using a shared pointer
connection_ptr con(new connection_type(m_is_server,m_user_agent,m_alog,
m_elog, m_rng));
connection_ptr con = lib::make_shared<connection_type>(m_is_server,
m_user_agent, lib::ref(m_alog), lib::ref(m_elog), lib::ref(m_rng));
connection_weak_ptr w(con);
@@ -64,16 +66,20 @@ endpoint<connection,config>::create_connection() {
con->set_http_handler(m_http_handler);
con->set_validate_handler(m_validate_handler);
con->set_message_handler(m_message_handler);
if (m_open_handshake_timeout_dur == config::timeout_open_handshake) {
if (m_open_handshake_timeout_dur != config::timeout_open_handshake) {
con->set_open_handshake_timeout(m_open_handshake_timeout_dur);
}
if (m_close_handshake_timeout_dur == config::timeout_close_handshake) {
if (m_close_handshake_timeout_dur != config::timeout_close_handshake) {
con->set_close_handshake_timeout(m_close_handshake_timeout_dur);
}
if (m_pong_timeout_dur == config::timeout_pong) {
if (m_pong_timeout_dur != config::timeout_pong) {
con->set_pong_timeout(m_pong_timeout_dur);
}
if (m_max_message_size != config::max_message_size) {
con->set_max_message_size(m_max_message_size);
}
con->set_max_http_body_size(m_max_http_body_size);
lib::error_code ec;
@@ -87,8 +93,7 @@ endpoint<connection,config>::create_connection() {
}
template <typename connection, typename config>
void endpoint<connection,config>::interrupt(connection_hdl hdl,
lib::error_code & ec)
void endpoint<connection,config>::interrupt(connection_hdl hdl, lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
@@ -102,12 +107,60 @@ template <typename connection, typename config>
void endpoint<connection,config>::interrupt(connection_hdl hdl) {
lib::error_code ec;
interrupt(hdl,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
payload, frame::opcode::value op, lib::error_code & ec)
void endpoint<connection,config>::pause_reading(connection_hdl hdl, lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
ec = con->pause_reading();
}
template <typename connection, typename config>
void endpoint<connection,config>::pause_reading(connection_hdl hdl) {
lib::error_code ec;
pause_reading(hdl,ec);
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
void endpoint<connection,config>::resume_reading(connection_hdl hdl, lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
ec = con->resume_reading();
}
template <typename connection, typename config>
void endpoint<connection,config>::resume_reading(connection_hdl hdl) {
lib::error_code ec;
resume_reading(hdl,ec);
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
void endpoint<connection,config>::send_http_response(connection_hdl hdl,
lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
con->send_http_response(ec);
}
template <typename connection, typename config>
void endpoint<connection,config>::send_http_response(connection_hdl hdl) {
lib::error_code ec;
send_http_response(hdl,ec);
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
frame::opcode::value op, lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
@@ -116,12 +169,12 @@ void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
}
template <typename connection, typename config>
void endpoint<connection,config>::send(connection_hdl hdl, std::string const &
payload, frame::opcode::value op)
void endpoint<connection,config>::send(connection_hdl hdl, std::string const & payload,
frame::opcode::value op)
{
lib::error_code ec;
send(hdl,payload,op,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
@@ -139,7 +192,7 @@ void endpoint<connection,config>::send(connection_hdl hdl, void const * payload,
{
lib::error_code ec;
send(hdl,payload,len,op,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
@@ -155,7 +208,7 @@ template <typename connection, typename config>
void endpoint<connection,config>::send(connection_hdl hdl, message_ptr msg) {
lib::error_code ec;
send(hdl,msg,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
@@ -174,7 +227,7 @@ void endpoint<connection,config>::close(connection_hdl hdl, close::status::value
{
lib::error_code ec;
close(hdl,code,reason,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
@@ -187,17 +240,16 @@ void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
}
template <typename connection, typename config>
void endpoint<connection,config>::ping(connection_hdl hdl, std::string const &
payload)
void endpoint<connection,config>::ping(connection_hdl hdl, std::string const & payload)
{
lib::error_code ec;
ping(hdl,payload,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
template <typename connection, typename config>
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
payload, lib::error_code & ec)
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload,
lib::error_code & ec)
{
connection_ptr con = get_con_from_hdl(hdl,ec);
if (ec) {return;}
@@ -205,12 +257,11 @@ void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
}
template <typename connection, typename config>
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const &
payload)
void endpoint<connection,config>::pong(connection_hdl hdl, std::string const & payload)
{
lib::error_code ec;
pong(hdl,payload,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
} // namespace websocketpp
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,6 +28,9 @@
#ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP
#define WEBSOCKETPP_UTILITIES_IMPL_HPP
#include <algorithm>
#include <string>
namespace websocketpp {
namespace utility {
@@ -37,7 +40,7 @@ inline std::string to_lower(std::string const & in) {
return out;
}
inline std::string to_hex(const std::string& input) {
inline std::string to_hex(std::string const & input) {
std::string output;
std::string hex = "0123456789ABCDEF";
@@ -50,7 +53,7 @@ inline std::string to_hex(const std::string& input) {
return output;
}
inline std::string to_hex(const uint8_t* input,size_t length) {
inline std::string to_hex(uint8_t const * input, size_t length) {
std::string output;
std::string hex = "0123456789ABCDEF";
@@ -67,8 +70,8 @@ inline std::string to_hex(const char* input,size_t length) {
return to_hex(reinterpret_cast<const uint8_t*>(input),length);
}
inline std::string string_replace_all(std::string subject, const std::string&
search, const std::string& replace)
inline std::string string_replace_all(std::string subject, std::string const &
search, std::string const & replace)
{
size_t pos = 0;
while((pos = subject.find(search, pos)) != std::string::npos) {
+71 -15
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -40,13 +40,16 @@
*
*/
#include <ctime>
#include <iostream>
#include <iomanip>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/stdint.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/time.hpp>
#include <ctime>
#include <iostream>
#include <iomanip>
#include <string>
namespace websocketpp {
namespace log {
@@ -55,16 +58,58 @@ namespace log {
template <typename concurrency, typename names>
class basic {
public:
basic<concurrency,names>(std::ostream * out = &std::cout)
basic<concurrency,names>(channel_type_hint::value h =
channel_type_hint::access)
: m_static_channels(0xffffffff)
, m_dynamic_channels(0)
, m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
basic<concurrency,names>(std::ostream * out)
: m_static_channels(0xffffffff)
, m_dynamic_channels(0)
, m_out(out) {}
basic<concurrency,names>(level c, std::ostream * out = &std::cout)
basic<concurrency,names>(level c, channel_type_hint::value h =
channel_type_hint::access)
: m_static_channels(c)
, m_dynamic_channels(0)
, m_out(h == channel_type_hint::error ? &std::cerr : &std::cout) {}
basic<concurrency,names>(level c, std::ostream * out)
: m_static_channels(c)
, m_dynamic_channels(0)
, m_out(out) {}
/// Destructor
~basic<concurrency,names>() {}
/// Copy constructor
basic<concurrency,names>(basic<concurrency,names> const & other)
: m_static_channels(other.m_static_channels)
, m_dynamic_channels(other.m_dynamic_channels)
, m_out(other.m_out)
{}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no copy assignment operator because of const member variables
basic<concurrency,names> & operator=(basic<concurrency,names> const &) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
/// Move constructor
basic<concurrency,names>(basic<concurrency,names> && other)
: m_static_channels(other.m_static_channels)
, m_dynamic_channels(other.m_dynamic_channels)
, m_out(other.m_out)
{}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no move assignment operator because of const member variables
basic<concurrency,names> & operator=(basic<concurrency,names> &&) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
void set_ostream(std::ostream * out = &std::cout) {
m_out = out;
}
@@ -84,6 +129,11 @@ public:
m_dynamic_channels &= ~channels;
}
/// Write a string message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, std::string const & msg) {
scoped_lock_type lock(m_lock);
if (!this->dynamic_test(channel)) { return; }
@@ -93,6 +143,11 @@ public:
m_out->flush();
}
/// Write a cstring message to the given channel
/**
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level channel, char const * msg) {
scoped_lock_type lock(m_lock);
if (!this->dynamic_test(channel)) { return; }
@@ -109,10 +164,13 @@ public:
bool dynamic_test(level channel) {
return ((channel & m_dynamic_channels) != 0);
}
private:
protected:
typedef typename concurrency::scoped_lock_type scoped_lock_type;
typedef typename concurrency::mutex_type mutex_type;
mutex_type m_lock;
private:
// The timestamp does not include the time zone, because on Windows with the
// default registry settings, the time zone would be written out in full,
// which would be obnoxiously verbose.
@@ -120,18 +178,16 @@ private:
// TODO: find a workaround for this or make this format user settable
static std::ostream & timestamp(std::ostream & os) {
std::time_t t = std::time(NULL);
std::tm* lt = std::localtime(&t);
#ifdef _WEBSOCKETPP_CPP11_CHRONO_
return os << std::put_time(lt,"%Y-%m-%d %H:%M:%S");
std::tm lt = lib::localtime(t);
#ifdef _WEBSOCKETPP_PUTTIME_
return os << std::put_time(&lt,"%Y-%m-%d %H:%M:%S");
#else // Falls back to strftime, which requires a temporary copy of the string.
char buffer[20];
std::strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",lt);
return os << buffer;
size_t result = std::strftime(buffer,sizeof(buffer),"%Y-%m-%d %H:%M:%S",&lt);
return os << (result == 0 ? "Unknown" : buffer);
#endif
}
mutex_type m_lock;
level const m_static_channels;
level m_dynamic_channels;
std::ostream * m_out;
+31 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -36,6 +36,25 @@ namespace log {
/// Type of a channel package
typedef uint32_t level;
/// Package of values for hinting at the nature of a given logger.
/**
* Used by the library to signal to the logging class a hint that it can use to
* set itself up. For example, the `access` hint indicates that it is an access
* log that might be suitable for being printed to an access log file or to cout
* whereas `error` might be suitable for an error log file or cerr.
*/
struct channel_type_hint {
/// Type of a channel type hint value
typedef uint32_t value;
/// No information
static value const none = 0;
/// Access log
static value const access = 1;
/// Error log
static value const error = 2;
};
/// Package of log levels for logging errors
struct elevel {
/// Special aggregate value representing "no levels"
@@ -122,6 +141,13 @@ struct alevel {
static level const devel = 0x400;
/// Special channel for application specific logs. Not used by the library.
static level const app = 0x800;
/// Access related to HTTP requests
static level const http = 0x1000;
/// One line for each failed WebSocket connection with details
static level const fail = 0x2000;
/// Aggregate package representing the commonly used core access channels
/// Connect, Disconnect, Fail, and HTTP
static level const access_core = 0x00003003;
/// Special aggregate value representing "all levels"
static level const all = 0xffffffff;
@@ -161,6 +187,10 @@ struct alevel {
return "devel";
case app:
return "application";
case http:
return "http";
case fail:
return "fail";
default:
return "unknown";
}
+67 -11
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,10 +28,11 @@
#ifndef WEBSOCKETPP_LOGGER_STUB_HPP
#define WEBSOCKETPP_LOGGER_STUB_HPP
#include <iostream>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/logger/levels.hpp>
#include <string>
namespace websocketpp {
namespace log {
@@ -39,20 +40,75 @@ namespace log {
/// Stub logger that ignores all input
class stub {
public:
explicit stub(std::ostream * out) {}
stub(level c, std::ostream * out) {}
/// Construct the logger
/**
* @param hint A channel type specific hint for how to construct the logger
*/
explicit stub(channel_type_hint::value) {}
/// Construct the logger
/**
* @param default_channels A set of channels to statically enable
* @param hint A channel type specific hint for how to construct the logger
*/
stub(level, channel_type_hint::value) {}
_WEBSOCKETPP_CONSTEXPR_TOKEN_ stub() {}
void set_channels(level channels) {}
void clear_channels(level channels) {}
/// Dynamically enable the given list of channels
/**
* All operations on the stub logger are no-ops and all arguments are
* ignored
*
* @param channels The package of channels to enable
*/
void set_channels(level) {}
void write(level channel, std::string const & msg) {}
void write(level channel, char const * msg) {}
/// Dynamically disable the given list of channels
/**
* All operations on the stub logger are no-ops and all arguments are
* ignored
*
* @param channels The package of channels to disable
*/
void clear_channels(level) {}
_WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level channel) const {
/// Write a string message to the given channel
/**
* Writing on the stub logger is a no-op and all arguments are ignored
*
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level, std::string const &) {}
/// Write a cstring message to the given channel
/**
* Writing on the stub logger is a no-op and all arguments are ignored
*
* @param channel The channel to write to
* @param msg The message to write
*/
void write(level, char const *) {}
/// Test whether a channel is statically enabled
/**
* The stub logger has no channels so all arguments are ignored and
* `static_test` always returns false.
*
* @param channel The package of channels to test
*/
_WEBSOCKETPP_CONSTEXPR_TOKEN_ bool static_test(level) const {
return false;
}
bool dynamic_test(level channel) {
/// Test whether a channel is dynamically enabled
/**
* The stub logger has no channels so all arguments are ignored and
* `dynamic_test` always returns false.
*
* @param channel The package of channels to test
*/
bool dynamic_test(level) {
return false;
}
};
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -53,7 +53,7 @@ public:
* @return A shared pointer to an empty new message
*/
message_ptr get_message() {
return message_ptr(new message(type::shared_from_this()));
return message_ptr(lib::make_shared<message>(type::shared_from_this()));
}
/// Get a message buffer with specified size and opcode
@@ -64,7 +64,7 @@ public:
* @return A shared pointer to a new message with specified size.
*/
message_ptr get_message(frame::opcode::value op,size_t size) {
return message_ptr(new message(type::shared_from_this(),op,size));
return message_ptr(lib::make_shared<message>(type::shared_from_this(),op,size));
}
/// Recycle a message
@@ -77,7 +77,7 @@ public:
*
* @return true if the message was successfully recycled, false otherwse.
*/
bool recycle(message * msg) {
bool recycle(message *) {
return false;
}
};
@@ -94,7 +94,7 @@ public:
* @return A pointer to the requested connection message manager.
*/
con_msg_man_ptr get_manager() const {
return con_msg_man_ptr(new con_msg_manager());
return con_msg_man_ptr(lib::make_shared<con_msg_manager>());
}
};
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -43,7 +43,7 @@ namespace message_buffer {
*
* # connection_message_manager:
* An object that manages all of the message_buffers associated with a given
* connection. Impliments the get_message_buffer(size) method that returns
* connection. Implements the get_message_buffer(size) method that returns
* a message buffer at least size bytes long.
*
* Message buffers are reference counted with shared ownership semantics. Once
@@ -54,7 +54,7 @@ namespace message_buffer {
* in the manager.
*
* # endpoint_message_manager:
* An object that manages connection_message_managers. Impliments the
* An object that manages connection_message_managers. Implements the
* get_message_manager() method. This is used once by each connection to
* request the message manager that they are supposed to use to manage message
* buffers for their own use.
@@ -146,10 +146,10 @@ public:
/// Set or clear the compression flag
/**
* The compression flag is used to indicate whether or not the message is
* or should be compressed. Compression is not guaranteed. Both endpoints
* must support a compression extension and the connection must have had
* that extension negotiated in its handshake.
* Setting the compression flag indicates that the data in this message
* would benefit from compression. If both endpoints negotiate a compression
* extension WebSocket++ will attempt to compress messages with this flag.
* Setting this flag does not guarantee that the message will be compressed.
*
* @param value The value to set the compressed flag to
*/
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -42,7 +42,7 @@ namespace message_buffer {
*
* # connection_message_manager:
* An object that manages all of the message_buffers associated with a given
* connection. Impliments the get_message_buffer(size) method that returns
* connection. Implements the get_message_buffer(size) method that returns
* a message buffer at least size bytes long.
*
* Message buffers are reference counted with shared ownership semantics. Once
@@ -53,7 +53,7 @@ namespace message_buffer {
* in the manager.
*
* # endpoint_message_manager:
* An object that manages connection_message_managers. Impliments the
* An object that manages connection_message_managers. Implements the
* get_message_manager() method. This is used once by each connection to
* request the message manager that they are supposed to use to manage message
* buffers for their own use.
@@ -171,7 +171,7 @@ public:
* @return A shared pointer to a new message with specified size.
*/
message_ptr get_message(size_t size) const {
return message_ptr(new message(size));
return lib::make_shared<message>(size);
}
/// Recycle a message
@@ -201,7 +201,7 @@ public:
* @return A pointer to the requested connection message manager.
*/
con_msg_man_ptr get_manager() const {
return con_msg_man_ptr(new con_msg_manager());
return lib::make_shared<con_msg_manager>();
}
};
+11 -6
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,14 +28,13 @@
#ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP
#define WEBSOCKETPP_PROCESSOR_BASE_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/close.hpp>
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <map>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
@@ -151,7 +150,11 @@ enum processor_errors {
extension_parse_error,
/// Extension related operation was ignored because extensions are disabled
extensions_disabled
extensions_disabled,
/// Short Ke3 read. Hybi00 requires a third key to be read from the 8 bytes
/// after the handshake. Less than 8 bytes were read.
short_key3
};
/// Category for processor errors
@@ -223,6 +226,8 @@ public:
return "Error parsing extension header";
case error::extensions_disabled:
return "Extensions are disabled";
case error::short_key3:
return "Short Hybi00 Key 3 read";
default:
return "Unknown";
}
+97 -27
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,15 +28,21 @@
#ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI00_HPP
#include <cstdlib>
#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 {
@@ -57,8 +63,8 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
explicit hybi00(bool secure, bool server, msg_manager_ptr manager)
: processor<config>(secure, server)
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)
@@ -81,9 +87,9 @@ public:
// 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") == "" ||
r.get_header("Sec-WebSocket-Key2") == "" ||
r.get_header("Sec-WebSocket-Key3") == "")
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);
}
@@ -107,9 +113,9 @@ public:
// if it is less the final key will almost certainly be wrong.
// TODO: decide if it is best to silently fail here or produce some sort
// of warning or exception.
const std::string& key3 = req.get_header("Sec-WebSocket-Key3");
std::string const & key3 = req.get_header("Sec-WebSocket-Key3");
std::copy(key3.c_str(),
key3.c_str()+std::min(static_cast<size_t>(8), key3.size()),
key3.c_str()+(std::min)(static_cast<size_t>(8), key3.size()),
&key_final[8]);
res.append_header(
@@ -122,33 +128,50 @@ public:
// Echo back client's origin unless our local application set a
// more restrictive one.
if (res.get_header("Sec-WebSocket-Origin") == "") {
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") == "") {
if (res.get_header("Sec-WebSocket-Location").empty()) {
uri_ptr uri = get_uri(req);
res.append_header("Sec-WebSocket-Location",uri->str());
}
if (subprotocol != "") {
if (!subprotocol.empty()) {
res.replace_header("Sec-WebSocket-Protocol",subprotocol);
}
return lib::error_code();
}
// outgoing client connection processing is not supported for this version
lib::error_code client_handshake_request(request_type& req, uri_ptr uri,
std::vector<std::string> const & subprotocols) const
/// 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);
}
lib::error_code validate_server_handshake_response(request_type const & req,
response_type & res) const
/// 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);
}
@@ -163,10 +186,31 @@ public:
return r.get_header("Origin");
}
// hybi00 doesn't support subprotocols so there never will be any requested
/// 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();
}
@@ -184,12 +228,12 @@ public:
if (last_colon == std::string::npos ||
(last_sbrace != std::string::npos && last_sbrace > last_colon))
{
return uri_ptr(new uri(base::m_secure, h, request.get_uri()));
return lib::make_shared<uri>(base::m_secure, h, request.get_uri());
} else {
return uri_ptr(new uri(base::m_secure,
return lib::make_shared<uri>(base::m_secure,
h.substr(0,last_colon),
h.substr(last_colon+1),
request.get_uri()));
request.get_uri());
}
// TODO: check if get_uri is a full uri
@@ -318,18 +362,44 @@ public:
return lib::error_code();
}
lib::error_code prepare_ping(std::string const & in, message_ptr out) const
/// 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);
}
lib::error_code prepare_pong(const std::string & in, message_ptr out) const
/// 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);
}
lib::error_code prepare_close(close::status::value code,
std::string const & reason, message_ptr out) const
/// 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);
@@ -346,7 +416,7 @@ public:
private:
void decode_client_key(std::string const & key, char * result) const {
unsigned int spaces = 0;
std::string digits = "";
std::string digits;
uint32_t num;
// key2
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -30,6 +30,9 @@
#include <websocketpp/processors/hybi08.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
@@ -45,13 +48,20 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi07(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: hybi08<config>(secure, server, manager, rng) {}
explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi08<config>(secure, p_is_server, manager, rng) {}
// outgoing client connection processing is not supported for this version
lib::error_code client_handshake_request(request_type & req, uri_ptr uri,
std::vector<std::string> const & subprotocols) const
/// 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);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -30,6 +30,9 @@
#include <websocketpp/processors/hybi13.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace processor {
@@ -46,13 +49,20 @@ public:
typedef typename config::con_msg_manager_type::ptr msg_manager_ptr;
typedef typename config::rng_type rng_type;
explicit hybi08(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: hybi13<config>(secure, server, manager, rng) {}
explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: hybi13<config>(secure, p_is_server, manager, rng) {}
// outgoing client connection processing is not supported for this version
lib::error_code client_handshake_request(request_type& req, uri_ptr uri,
std::vector<std::string> const & subprotocols) const
/// 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);
}
@@ -61,7 +71,7 @@ public:
return 8;
}
const std::string& get_origin(request_type const & r) const {
std::string const & get_origin(request_type const & r) const {
return r.get_header("Sec-WebSocket-Origin");
}
private:
+160 -80
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,18 +28,20 @@
#ifndef WEBSOCKETPP_PROCESSOR_HYBI13_HPP
#define WEBSOCKETPP_PROCESSOR_HYBI13_HPP
#include <cassert>
#include <websocketpp/frame.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/frame.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/utf8_validator.hpp>
#include <websocketpp/sha1/sha1.hpp>
#include <websocketpp/base64/base64.hpp>
#include <websocketpp/common/network.hpp>
#include <websocketpp/common/platforms.hpp>
#include <algorithm>
#include <cassert>
#include <string>
#include <vector>
#include <utility>
@@ -67,9 +69,8 @@ public:
typedef std::pair<lib::error_code,std::string> err_str_pair;
explicit hybi13(bool secure, bool server, msg_manager_ptr manager,
rng_type& rng)
: processor<config>(secure,server)
explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng)
: processor<config>(secure, p_is_server)
, m_msg_manager(manager)
, m_rng(rng)
{
@@ -84,7 +85,21 @@ public:
return m_permessage_deflate.is_implemented();
}
err_str_pair negotiate_extensions(request_type const & req) {
err_str_pair negotiate_extensions(request_type const & request) {
return negotiate_extensions_helper(request);
}
err_str_pair negotiate_extensions(response_type const & response) {
return negotiate_extensions_helper(response);
}
/// Extension negotiation helper function
/**
* This exists mostly because the code for requests and responses is
* identical and I can't have virtual template methods.
*/
template <typename header_type>
err_str_pair negotiate_extensions_helper(header_type const & header) {
err_str_pair ret;
// Respect blanket disabling of all extensions and don't even parse
@@ -96,7 +111,7 @@ public:
http::parameter_list p;
bool error = req.get_header_as_plist("Sec-WebSocket-Extensions",p);
bool error = header.get_header_as_plist("Sec-WebSocket-Extensions",p);
if (error) {
ret.first = make_error_code(error::extension_parse_error);
@@ -115,18 +130,27 @@ public:
for (it = p.begin(); it != p.end(); ++it) {
// look through each extension, if the key is permessage-deflate
if (it->first == "permessage-deflate") {
// if we have already successfully negotiated this extension
// then skip any other requests to negotiate the same one
// with different parameters
if (m_permessage_deflate.is_enabled()) {
continue;
}
neg_ret = m_permessage_deflate.negotiate(it->second);
if (neg_ret.first) {
// Figure out if this is an error that should halt all
// extension negotiations or simply cause negotiation of
// this specific extension to fail.
std::cout << "permessage-compress negotiation failed: "
<< neg_ret.first.message() << std::endl;
//std::cout << "permessage-compress negotiation failed: "
// << neg_ret.first.message() << std::endl;
} else {
// Note: this list will need commas if WebSocket++ ever
// supports more than one extension
ret.second += neg_ret.second;
m_permessage_deflate.init(base::m_server);
continue;
}
}
@@ -149,7 +173,7 @@ public:
// 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-Key") == "") {
if (r.get_header("Sec-WebSocket-Key").empty()) {
return make_error_code(error::missing_required_header);
}
@@ -160,8 +184,8 @@ public:
* generic struct if other user input parameters to the processed handshake
* are found.
*/
lib::error_code process_handshake(request_type const & request, const
std::string & subprotocol, response_type& response) const
lib::error_code process_handshake(request_type const & request,
std::string const & subprotocol, response_type & response) const
{
std::string server_key = request.get_header("Sec-WebSocket-Key");
@@ -182,7 +206,13 @@ public:
return lib::error_code();
}
lib::error_code client_handshake_request(request_type& req, uri_ptr
/// Fill in a set of request headers for a client connection request
/**
* @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 & req, uri_ptr
uri, std::vector<std::string> const & subprotocols) const
{
req.set_method("GET");
@@ -216,9 +246,22 @@ public:
req.replace_header("Sec-WebSocket-Key",base64_encode(raw_key, 16));
if (m_permessage_deflate.is_implemented()) {
std::string offer = m_permessage_deflate.generate_offer();
if (!offer.empty()) {
req.replace_header("Sec-WebSocket-Extensions",offer);
}
}
return lib::error_code();
}
/// 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
*/
lib::error_code validate_server_handshake_response(request_type const & req,
response_type& res) const
{
@@ -251,6 +294,8 @@ public:
return error::make_error_code(error::missing_required_header);
}
// check extensions
return lib::error_code();
}
@@ -369,11 +414,29 @@ public:
m_current_msg = &m_control_msg;
} else {
if (!m_data_msg.msg_ptr) {
if (m_bytes_needed > base::m_max_message_size) {
ec = make_error_code(error::message_too_big);
break;
}
m_data_msg = msg_metadata(
m_msg_manager->get_message(op,m_bytes_needed),
frame::get_masking_key(m_basic_header,m_extended_header)
);
if (m_permessage_deflate.is_enabled()) {
m_data_msg.msg_ptr->set_compressed(frame::get_rsv1(m_basic_header));
}
} else {
// Fetch the underlying payload buffer from the data message we
// are writing into.
std::string & out = m_data_msg.msg_ptr->get_raw_payload();
if (out.size() + m_bytes_needed > base::m_max_message_size) {
ec = make_error_code(error::message_too_big);
break;
}
// Each frame starts a new masking key. All other state
// remains between frames.
m_data_msg.prepared_key = prepare_masking_key(
@@ -382,14 +445,15 @@ public:
m_extended_header
)
);
// TODO: reserve space in the existing message for the new bytes
out.reserve(out.size() + m_bytes_needed);
}
m_current_msg = &m_data_msg;
}
} else if (m_state == EXTENSION) {
m_state = APPLICATION;
} else if (m_state == APPLICATION) {
size_t bytes_to_process = std::min(m_bytes_needed,len-p);
size_t bytes_to_process = (std::min)(m_bytes_needed,len-p);
if (bytes_to_process > 0) {
p += this->process_payload_bytes(buf+p,bytes_to_process,ec);
@@ -404,15 +468,10 @@ public:
// If this was the last frame in the message set the ready flag.
// Otherwise, reset processor state to read additional frames.
if (frame::get_fin(m_basic_header)) {
// ensure that text messages end on a valid UTF8 code point
if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) {
if (!m_current_msg->validator.complete()) {
ec = make_error_code(error::invalid_utf8);
break;
}
ec = finalize_message();
if (ec) {
break;
}
m_state = READY;
} else {
this->reset_headers();
}
@@ -426,6 +485,44 @@ public:
return p;
}
/// Perform any finalization actions on an incoming message
/**
* Called after the full message is received. Provides the opportunity for
* extensions to complete any data post processing as well as final UTF8
* validation checks for text messages.
*
* @return A code indicating errors, if any
*/
lib::error_code finalize_message() {
std::string & out = m_current_msg->msg_ptr->get_raw_payload();
// if the frame is compressed, append the compression
// trailer and flush the compression buffer.
if (m_permessage_deflate.is_enabled()
&& m_current_msg->msg_ptr->get_compressed())
{
uint8_t trailer[4] = {0x00, 0x00, 0xff, 0xff};
// Decompress current buffer into the message buffer
lib::error_code ec;
ec = m_permessage_deflate.decompress(trailer,4,out);
if (ec) {
return ec;
}
}
// ensure that text messages end on a valid UTF8 code point
if (frame::get_opcode(m_basic_header) == frame::opcode::TEXT) {
if (!m_current_msg->validator.complete()) {
return make_error_code(error::invalid_utf8);
}
}
m_state = READY;
return lib::error_code();
}
void reset_headers() {
m_state = HEADER_BASIC;
m_bytes_needed = frame::BASIC_HEADER_LENGTH;
@@ -477,19 +574,6 @@ public:
* Performs validation, masking, compression, etc. will return an error if
* there was an error, otherwise msg will be ready to be written
*
* By default WebSocket++ performs block masking/unmasking in a manner that
* makes assumptions about the nature of the machine and STL library used.
* In particular the assumption is either a 32 or 64 bit word size and an
* STL with std::string::data returning a contiguous char array.
*
* This method improves masking performance by 3-8x depending on the ratio
* of small to large messages and the availability of a 64 bit processor.
*
* To disable this optimization (for use with alternative STL
* implementations or processors) define WEBSOCKETPP_STRICT_MASKING when
* compiling the library. This will force the library to perform masking in
* single byte chunks.
*
* TODO: tests
*
* @param in An unprepared message to prepare
@@ -523,18 +607,11 @@ public:
&& in->get_compressed();
bool fin = in->get_fin();
// generate header
frame::basic_header h(op,i.size(),fin,masked,compressed);
if (masked) {
// Generate masking key.
key.i = m_rng();
frame::extended_header e(i.size(),key.i);
out->set_header(frame::prepare_header(h,e));
} else {
frame::extended_header e(i.size());
out->set_header(frame::prepare_header(h,e));
key.i = 0;
}
// prepare payload
@@ -542,6 +619,14 @@ public:
// compress and store in o after header.
m_permessage_deflate.compress(i,o);
if (o.size() < 4) {
return make_error_code(error::general);
}
// Strip trailing 4 0x00 0x00 0xff 0xff bytes before writing to the
// wire
o.resize(o.size()-4);
// mask in place if necessary
if (masked) {
this->masked_copy(o,o,key);
@@ -560,7 +645,19 @@ public:
}
}
// generate header
frame::basic_header h(op,o.size(),fin,masked,compressed);
if (masked) {
frame::extended_header e(o.size(),key.i);
out->set_header(frame::prepare_header(h,e));
} else {
frame::extended_header e(o.size());
out->set_header(frame::prepare_header(h,e));
}
out->set_prepared(true);
out->set_opcode(op);
return lib::error_code();
}
@@ -655,7 +752,7 @@ protected:
/// Reads bytes from buf into m_extended_header
size_t copy_extended_header_bytes(uint8_t const * buf, size_t len) {
size_t bytes_to_read = std::min(m_bytes_needed,len);
size_t bytes_to_read = (std::min)(m_bytes_needed,len);
std::copy(buf,buf+bytes_to_read,m_extended_header.bytes+m_cursor);
m_cursor += bytes_to_read;
@@ -681,19 +778,9 @@ protected:
{
// unmask if masked
if (frame::get_masked(m_basic_header)) {
#ifdef WEBSOCKETPP_STRICT_MASKING
m_current_msg->prepared_key = frame::byte_mask_circ(
buf,
len,
m_current_msg->prepared_key
);
#else
m_current_msg->prepared_key = frame::word_mask_circ(
buf,
len,
m_current_msg->prepared_key
);
#endif
m_current_msg->prepared_key = frame::byte_mask_circ(
buf, len, m_current_msg->prepared_key);
// TODO: SIMD masking
}
std::string & out = m_current_msg->msg_ptr->get_raw_payload();
@@ -701,13 +788,13 @@ protected:
// decompress message if needed.
if (m_permessage_deflate.is_enabled()
&& frame::get_rsv1(m_basic_header))
&& m_current_msg->msg_ptr->get_compressed())
{
// Decompress current buffer into the message buffer
m_permessage_deflate.decompress(buf,len,out);
// get the length of the newly uncompressed output
offset = out.size() - offset;
ec = m_permessage_deflate.decompress(buf,len,out);
if (ec) {
return 0;
}
} else {
// No compression, straight copy
out.append(reinterpret_cast<char *>(buf),len);
@@ -852,16 +939,8 @@ protected:
void masked_copy (std::string const & i, std::string & o,
frame::masking_key_type key) const
{
#ifdef WEBSOCKETPP_STRICT_MASKING
frame::byte_mask(i.begin(),i.end(),o.begin(),key);
#else
websocketpp::frame::word_mask_exact(
reinterpret_cast<uint8_t *>(const_cast<char *>(i.data())),
reinterpret_cast<uint8_t *>(const_cast<char *>(o.data())),
i.size(),
key
);
#endif
frame::byte_mask(i.begin(),i.end(),o.begin(),key);
// TODO: SIMD masking
}
/// Generic prepare control frame with opcode and payload.
@@ -908,7 +987,8 @@ protected:
out->set_header(frame::prepare_header(h,e));
std::copy(payload.begin(),payload.end(),o.begin());
}
out->set_opcode(op);
out->set_prepared(true);
return lib::error_code();
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -35,8 +35,10 @@
#include <websocketpp/utilities.hpp>
#include <websocketpp/uri.hpp>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
namespace websocketpp {
/// Processors encapsulate the protocol rules specific to each WebSocket version
@@ -103,7 +105,11 @@ bool is_websocket_handshake(request_type& r) {
*/
template <typename request_type>
int get_websocket_version(request_type& r) {
if (r.get_header("Sec-WebSocket-Version") == "") {
if (!r.ready()) {
return -2;
}
if (r.get_header("Sec-WebSocket-Version").empty()) {
return 0;
}
@@ -140,12 +146,12 @@ uri_ptr get_uri_from_host(request_type & request, std::string scheme) {
if (last_colon == std::string::npos ||
(last_sbrace != std::string::npos && last_sbrace > last_colon))
{
return uri_ptr(new uri(scheme, h, request.get_uri()));
return lib::make_shared<uri>(scheme, h, request.get_uri());
} else {
return uri_ptr(new uri(scheme,
return lib::make_shared<uri>(scheme,
h.substr(0,last_colon),
h.substr(last_colon+1),
request.get_uri()));
request.get_uri());
}
}
@@ -159,15 +165,45 @@ public:
typedef typename config::message_type::ptr message_ptr;
typedef std::pair<lib::error_code,std::string> err_str_pair;
explicit processor(bool secure, bool server)
explicit processor(bool secure, bool p_is_server)
: m_secure(secure)
, m_server(server) {}
, 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
@@ -181,9 +217,26 @@ public:
/**
* 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.
* 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 & request) {
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();
}
@@ -196,8 +249,7 @@ public:
* @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;
virtual lib::error_code validate_handshake(request_type const & request) const = 0;
/// Calculate the appropriate response for this websocket request
/**
@@ -224,9 +276,7 @@ public:
/// 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
@@ -236,18 +286,16 @@ public:
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;
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 req The request to extract from
*
* @param subprotocol_list A reference to a vector of strings to store the
* results in.
* @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;
@@ -261,11 +309,8 @@ public:
* 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;
@@ -310,8 +355,7 @@ public:
* 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;
virtual lib::error_code prepare_data_frame(message_ptr in, message_ptr out) = 0;
/// Prepare a ping frame
/**
@@ -319,13 +363,11 @@ public:
* 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;
virtual lib::error_code prepare_ping(std::string const & in, message_ptr out) const
= 0;
/// Prepare a pong frame
/**
@@ -333,13 +375,11 @@ public:
* 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;
virtual lib::error_code prepare_pong(std::string const & in, message_ptr out) const
= 0;
/// Prepare a close frame
/**
@@ -349,11 +389,8 @@ public:
* 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,
@@ -361,6 +398,7 @@ public:
protected:
bool const m_secure;
bool const m_server;
size_t m_max_message_size;
};
} // namespace processor
+2 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,6 +29,7 @@
#define WEBSOCKETPP_RANDOM_NONE_HPP
namespace websocketpp {
/// Random number generation policies
namespace random {
/// Stub RNG policy that always returns 0
namespace none {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,13 +29,16 @@
#define WEBSOCKETPP_CLIENT_ENDPOINT_HPP
#include <websocketpp/endpoint.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <iostream>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
/// Client endpoint role based on the given config
/**
*
@@ -64,6 +67,8 @@ public:
/// Type of the endpoint component of this server
typedef endpoint<connection_type,config> endpoint_type;
friend class connection<config>;
explicit client() : endpoint_type(false)
{
endpoint_type::m_alog.write(log::alevel::devel, "client constructor");
@@ -112,7 +117,7 @@ public:
* @return A connection_ptr to the new connection
*/
connection_ptr get_connection(std::string const & u, lib::error_code & ec) {
uri_ptr location(new uri(u));
uri_ptr location = lib::make_shared<uri>(u);
if (!location->get_valid()) {
ec = error::make_error_code(error::invalid_uri);
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -29,13 +29,13 @@
#define WEBSOCKETPP_SERVER_ENDPOINT_HPP
#include <websocketpp/endpoint.hpp>
#include <websocketpp/logger/levels.hpp>
#include <iostream>
#include <websocketpp/common/system_error.hpp>
namespace websocketpp {
/// Server endpoint role based on the given config
/**
*
@@ -64,52 +64,125 @@ public:
/// Type of the endpoint component of this server
typedef endpoint<connection_type,config> endpoint_type;
// TODO: clean up these types
friend class connection<config>;
explicit server() : endpoint_type(true)
{
endpoint_type::m_alog.write(log::alevel::devel,
"server constructor");
endpoint_type::m_alog.write(log::alevel::devel, "server constructor");
}
// return an initialized connection_ptr. Call start() on this object to
// begin the processing loop.
/// Destructor
~server<config>() {}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no copy constructor because endpoints are not copyable
server<config>(server<config> &) = delete;
// no copy assignment operator because endpoints are not copyable
server<config> & operator=(server<config> const &) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
/// Move constructor
server<config>(server<config> && o) : endpoint<connection<config>,config>(std::move(o)) {}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
// no move assignment operator because of const member variables
server<config> & operator=(server<config> &&) = delete;
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
/// Create and initialize a new connection
/**
* The connection will be initialized and ready to begin. Call its start()
* method to begin the processing loop.
*
* Note: The connection must either be started or terminated using
* connection::terminate in order to avoid memory leaks.
*
* @return A pointer to the new connection.
*/
connection_ptr get_connection() {
connection_ptr con = endpoint_type::create_connection();
return con;
return endpoint_type::create_connection();
}
// Starts the server's async connection acceptance loop.
void start_accept() {
/// Starts the server's async connection acceptance loop (exception free)
/**
* Initiates the server connection acceptance loop. Must be called after
* listen. This method will have no effect until the underlying io_service
* starts running. It may be called after the io_service is already running.
*
* Refer to documentation for the transport policy you are using for
* instructions on how to stop this acceptance loop.
*
* @param [out] ec A status code indicating an error, if any.
*/
void start_accept(lib::error_code & ec) {
if (!transport_type::is_listening()) {
ec = error::make_error_code(error::async_accept_not_listening);
return;
}
ec = lib::error_code();
connection_ptr con = get_connection();
transport_type::async_accept(
lib::static_pointer_cast<transport_con_type>(con),
lib::bind(
&type::handle_accept,
this,
con,
lib::placeholders::_1
)
lib::bind(&type::handle_accept,this,con,lib::placeholders::_1),
ec
);
if (ec && con) {
// If the connection was constructed but the accept failed,
// terminate the connection to prevent memory leaks
con->terminate(lib::error_code());
}
}
void handle_accept(connection_ptr con, const lib::error_code& ec) {
/// Starts the server's async connection acceptance loop
/**
* Initiates the server connection acceptance loop. Must be called after
* listen. This method will have no effect until the underlying io_service
* starts running. It may be called after the io_service is already running.
*
* Refer to documentation for the transport policy you are using for
* instructions on how to stop this acceptance loop.
*/
void start_accept() {
lib::error_code ec;
start_accept(ec);
if (ec) {
throw exception(ec);
}
}
/// Handler callback for start_accept
void handle_accept(connection_ptr con, lib::error_code const & ec) {
if (ec) {
con->terminate(ec);
endpoint_type::m_elog.write(log::elevel::rerror,
"handle_accept error: "+ec.message());
if (ec == error::operation_canceled) {
endpoint_type::m_elog.write(log::elevel::info,
"handle_accept error: "+ec.message());
} else {
endpoint_type::m_elog.write(log::elevel::rerror,
"handle_accept error: "+ec.message());
}
} else {
con->start();
}
// TODO: are there cases where we should terminate this loop?
start_accept();
lib::error_code start_ec;
start_accept(start_ec);
if (start_ec == error::async_accept_not_listening) {
endpoint_type::m_elog.write(log::elevel::info,
"Stopping acceptance of new connections because the underlying transport is no longer listening.");
} else if (start_ec) {
endpoint_type::m_elog.write(log::elevel::rerror,
"Restarting async_accept loop failed: "+ec.message());
}
}
private:
};
} // namespace websocketpp
+1 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
+189 -184
View File
@@ -1,184 +1,189 @@
/*
*****
sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the shallsha1
library (http://code.google.com/p/smallsha1/) into a single header suitable for
use as a header only library. This conversion was done by Peter Thorson
(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed
under the same license as the original, which is listed below.
*****
Copyright (c) 2011, Micael Hildenborg
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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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 SHA1_DEFINED
#define SHA1_DEFINED
namespace websocketpp {
namespace sha1 {
namespace // local
{
// Rotate an integer value to left.
inline const unsigned int rol(const unsigned int value,
const unsigned int steps)
{
return ((value << steps) | (value >> (32 - steps)));
}
// Sets the first 16 integers in the buffert to zero.
// Used for clearing the W buffert.
inline void clearWBuffert(unsigned int* buffert)
{
for (int pos = 16; --pos >= 0;)
{
buffert[pos] = 0;
}
}
inline void innerHash(unsigned int* result, unsigned int* w)
{
unsigned int a = result[0];
unsigned int b = result[1];
unsigned int c = result[2];
unsigned int d = result[3];
unsigned int e = result[4];
int round = 0;
#define sha1macro(func,val) \
{ \
const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
e = d; \
d = c; \
c = rol(b, 30); \
b = a; \
a = t; \
}
while (round < 16)
{
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 20)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 40)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0x6ed9eba1)
++round;
}
while (round < 60)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
++round;
}
while (round < 80)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0xca62c1d6)
++round;
}
#undef sha1macro
result[0] += a;
result[1] += b;
result[2] += c;
result[3] += d;
result[4] += e;
}
} // namespace
/**
@param src points to any kind of data to be hashed.
@param bytelength the number of bytes to hash from the src pointer.
@param hash should point to a buffer of at least 20 bytes of size for storing the sha1 result in.
*/
inline void calc(const void* src, const int bytelength, unsigned char* hash) {
// Init the result array.
unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
// Cast the void src pointer to be the byte array we can work with.
const unsigned char* sarray = (const unsigned char*) src;
// The reusable round buffer
unsigned int w[80];
// Loop through all complete 64byte blocks.
const int endOfFullBlocks = bytelength - 64;
int endCurrentBlock;
int currentBlock = 0;
while (currentBlock <= endOfFullBlocks)
{
endCurrentBlock = currentBlock + 64;
// Init the round buffer with the 64 byte block data.
for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
{
// This line will swap endian on big endian and keep endian on little endian.
w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
| (((unsigned int) sarray[currentBlock + 2]) << 8)
| (((unsigned int) sarray[currentBlock + 1]) << 16)
| (((unsigned int) sarray[currentBlock]) << 24);
}
innerHash(result, w);
}
// Handle the last and not full 64 byte block if existing.
endCurrentBlock = bytelength - currentBlock;
clearWBuffert(w);
int lastBlockBytes = 0;
for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes)
{
w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
}
w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
if (endCurrentBlock >= 56)
{
innerHash(result, w);
clearWBuffert(w);
}
w[15] = bytelength << 3;
innerHash(result, w);
// Store hash in result pointer, and make sure we get in in the correct order on both endian models.
for (int hashByte = 20; --hashByte >= 0;)
{
hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
}
}
} // namespace sha1
} // namespace websocketpp
#endif // SHA1_DEFINED
/*
*****
sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the smallsha1
library (http://code.google.com/p/smallsha1/) into a single header suitable for
use as a header only library. This conversion was done by Peter Thorson
(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed
under the same license as the original, which is listed below.
*****
Copyright (c) 2011, Micael Hildenborg
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 Micael Hildenborg 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 Micael Hildenborg ''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 Micael Hildenborg 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 SHA1_DEFINED
#define SHA1_DEFINED
namespace websocketpp {
namespace sha1 {
namespace { // local
// Rotate an integer value to left.
inline unsigned int rol(unsigned int value, unsigned int steps) {
return ((value << steps) | (value >> (32 - steps)));
}
// Sets the first 16 integers in the buffert to zero.
// Used for clearing the W buffert.
inline void clearWBuffert(unsigned int * buffert)
{
for (int pos = 16; --pos >= 0;)
{
buffert[pos] = 0;
}
}
inline void innerHash(unsigned int * result, unsigned int * w)
{
unsigned int a = result[0];
unsigned int b = result[1];
unsigned int c = result[2];
unsigned int d = result[3];
unsigned int e = result[4];
int round = 0;
#define sha1macro(func,val) \
{ \
const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
e = d; \
d = c; \
c = rol(b, 30); \
b = a; \
a = t; \
}
while (round < 16)
{
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 20)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (~b & d), 0x5a827999)
++round;
}
while (round < 40)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0x6ed9eba1)
++round;
}
while (round < 60)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
++round;
}
while (round < 80)
{
w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
sha1macro(b ^ c ^ d, 0xca62c1d6)
++round;
}
#undef sha1macro
result[0] += a;
result[1] += b;
result[2] += c;
result[3] += d;
result[4] += e;
}
} // namespace
/// Calculate a SHA1 hash
/**
* @param src points to any kind of data to be hashed.
* @param bytelength the number of bytes to hash from the src pointer.
* @param hash should point to a buffer of at least 20 bytes of size for storing
* the sha1 result in.
*/
inline void calc(void const * src, size_t bytelength, unsigned char * hash) {
// Init the result array.
unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe,
0x10325476, 0xc3d2e1f0 };
// Cast the void src pointer to be the byte array we can work with.
unsigned char const * sarray = (unsigned char const *) src;
// The reusable round buffer
unsigned int w[80];
// Loop through all complete 64byte blocks.
size_t endCurrentBlock;
size_t currentBlock = 0;
if (bytelength >= 64) {
size_t const endOfFullBlocks = bytelength - 64;
while (currentBlock <= endOfFullBlocks) {
endCurrentBlock = currentBlock + 64;
// Init the round buffer with the 64 byte block data.
for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
{
// This line will swap endian on big endian and keep endian on
// little endian.
w[roundPos++] = (unsigned int) sarray[currentBlock + 3]
| (((unsigned int) sarray[currentBlock + 2]) << 8)
| (((unsigned int) sarray[currentBlock + 1]) << 16)
| (((unsigned int) sarray[currentBlock]) << 24);
}
innerHash(result, w);
}
}
// Handle the last and not full 64 byte block if existing.
endCurrentBlock = bytelength - currentBlock;
clearWBuffert(w);
size_t lastBlockBytes = 0;
for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) {
w[lastBlockBytes >> 2] |= (unsigned int) sarray[lastBlockBytes + currentBlock] << ((3 - (lastBlockBytes & 3)) << 3);
}
w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
if (endCurrentBlock >= 56) {
innerHash(result, w);
clearWBuffert(w);
}
w[15] = bytelength << 3;
innerHash(result, w);
// Store hash in result pointer, and make sure we get in in the correct
// order on both endian models.
for (int hashByte = 20; --hashByte >= 0;) {
hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
}
}
} // namespace sha1
} // namespace websocketpp
#endif // SHA1_DEFINED
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,123 +28,107 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_BASE_HPP
#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/system_error.hpp>
#include <boost/system/error_code.hpp>
#include <boost/aligned_storage.hpp>
#include <boost/noncopyable.hpp>
#include <boost/array.hpp>
#include <websocketpp/common/type_traits.hpp>
#include <string>
namespace websocketpp {
namespace transport {
/// Transport policy that uses boost::asio
/// Transport policy that uses asio
/**
* This policy uses a single boost::asio io_service to provide transport
* This policy uses a single asio io_service to provide transport
* services to a WebSocket++ endpoint.
*/
namespace asio {
//
// Class to manage the memory to be used for handler-based custom allocation.
// It contains a single block of memory which may be returned for allocation
// requests. If the memory is in use when an allocation request is made, the
// allocator delegates allocation to the global heap.
class handler_allocator
: private boost::noncopyable
{
class handler_allocator {
public:
handler_allocator()
: in_use_(false)
{
}
static const size_t size = 1024;
handler_allocator() : m_in_use(false) {}
void* allocate(std::size_t size)
{
if (!in_use_ && size < storage_.size)
{
in_use_ = true;
return storage_.address();
}
else
{
return ::operator new(size);
}
}
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
handler_allocator(handler_allocator const & cpy) = delete;
handler_allocator & operator =(handler_allocator const &) = delete;
#endif
void deallocate(void* pointer)
{
if (pointer == storage_.address())
{
in_use_ = false;
void * allocate(std::size_t memsize) {
if (!m_in_use && memsize < size) {
m_in_use = true;
return static_cast<void*>(&m_storage);
} else {
return ::operator new(memsize);
}
}
else
{
::operator delete(pointer);
void deallocate(void * pointer) {
if (pointer == &m_storage) {
m_in_use = false;
} else {
::operator delete(pointer);
}
}
}
private:
// Storage space used for handler-based custom memory allocation.
boost::aligned_storage<1024> storage_;
// Storage space used for handler-based custom memory allocation.
lib::aligned_storage<size>::type m_storage;
// Whether the handler-based custom allocation storage has been used.
bool in_use_;
// Whether the handler-based custom allocation storage has been used.
bool m_in_use;
};
// Wrapper class template for handler objects to allow handler memory
// allocation to be customised. Calls to operator() are forwarded to the
// encapsulated handler.
template <typename Handler>
class custom_alloc_handler
{
class custom_alloc_handler {
public:
custom_alloc_handler(handler_allocator& a, Handler h)
: allocator_(a),
handler_(h)
{
}
custom_alloc_handler(handler_allocator& a, Handler h)
: allocator_(a),
handler_(h)
{}
template <typename Arg1>
void operator()(Arg1 arg1)
{
handler_(arg1);
}
template <typename Arg1>
void operator()(Arg1 arg1) {
handler_(arg1);
}
template <typename Arg1, typename Arg2>
void operator()(Arg1 arg1, Arg2 arg2)
{
handler_(arg1, arg2);
}
template <typename Arg1, typename Arg2>
void operator()(Arg1 arg1, Arg2 arg2) {
handler_(arg1, arg2);
}
friend void* asio_handler_allocate(std::size_t size,
custom_alloc_handler<Handler>* this_handler)
{
return this_handler->allocator_.allocate(size);
}
friend void* asio_handler_allocate(std::size_t size,
custom_alloc_handler<Handler> * this_handler)
{
return this_handler->allocator_.allocate(size);
}
friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
custom_alloc_handler<Handler>* this_handler)
{
this_handler->allocator_.deallocate(pointer);
}
friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/,
custom_alloc_handler<Handler> * this_handler)
{
this_handler->allocator_.deallocate(pointer);
}
private:
handler_allocator& allocator_;
Handler handler_;
handler_allocator & allocator_;
Handler handler_;
};
// Helper function to wrap a handler object to add custom allocation.
template <typename Handler>
inline custom_alloc_handler<Handler> make_custom_alloc_handler(
handler_allocator& a, Handler h)
handler_allocator & a, Handler h)
{
return custom_alloc_handler<Handler>(a, h);
return custom_alloc_handler<Handler>(a, h);
}
@@ -158,13 +142,10 @@ inline custom_alloc_handler<Handler> make_custom_alloc_handler(
template <typename config>
class endpoint;
typedef lib::function<void(boost::system::error_code const &)>
socket_shutdown_handler;
typedef lib::function<void (boost::system::error_code const & ec,
typedef lib::function<void (lib::asio::error_code const & ec,
size_t bytes_transferred)> async_read_handler;
typedef lib::function<void (boost::system::error_code const & ec,
typedef lib::function<void (lib::asio::error_code const & ec,
size_t bytes_transferred)> async_write_handler;
typedef lib::function<void (lib::error_code const & ec)> pre_init_handler;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,21 +28,27 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_CON_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/transport/asio/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/http/constants.hpp>
#include <websocketpp/transport/asio/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/base64/base64.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/uri.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/chrono.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <istream>
#include <sstream>
#include <string>
#include <vector>
namespace websocketpp {
@@ -51,10 +57,10 @@ namespace asio {
typedef lib::function<void(connection_hdl)> tcp_init_handler;
/// Boost Asio based connection transport component
/// Asio based connection transport component
/**
* transport::asio::connection implements a connection transport component using
* Boost ASIO that works with the transport::asio::endpoint endpoint transport
* Asio that works with the transport::asio::endpoint endpoint transport
* component.
*/
template <typename config>
@@ -79,12 +85,12 @@ public:
typedef typename config::response_type response_type;
typedef typename response_type::ptr response_ptr;
/// Type of a pointer to the ASIO io_service being used
typedef boost::asio::io_service* io_service_ptr;
/// Type of a pointer to the ASIO io_service::strand being used
typedef lib::shared_ptr<boost::asio::io_service::strand> strand_ptr;
/// Type of a pointer to the ASIO timer class
typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
/// Type of a pointer to the Asio io_service being used
typedef lib::asio::io_service * io_service_ptr;
/// Type of a pointer to the Asio io_service::strand being used
typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
/// Type of a pointer to the Asio timer class
typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
// connection is friends with its associated endpoint to allow the endpoint
// to call private/protected utility methods that we don't want to expose
@@ -92,7 +98,7 @@ public:
friend class endpoint<config>;
// generate and manage our own io_service
explicit connection(bool is_server, alog_type& alog, elog_type& elog)
explicit connection(bool is_server, alog_type & alog, elog_type & elog)
: m_is_server(is_server)
, m_alog(alog)
, m_elog(elog)
@@ -109,13 +115,29 @@ public:
return socket_con_type::is_secure();
}
/// Set uri hook
/**
* Called by the endpoint as a connection is being established to provide
* the uri being connected to to the transport layer.
*
* This transport policy doesn't use the uri except to forward it to the
* socket layer.
*
* @since 0.6.0
*
* @param u The uri to set
*/
void set_uri(uri_ptr u) {
socket_con_type::set_uri(u);
}
/// Sets the tcp pre init handler
/**
* The tcp pre init handler is called after the raw tcp connection has been
* established but before any additional wrappers (proxy connects, TLS
* handshakes, etc) have been performed.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*
* @param h The handler to call on tcp pre init.
*/
@@ -144,7 +166,7 @@ public:
* etc have been performed. This is fired before any bytes are read or any
* WebSocket specific handshake logic has been performed.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*
* @param h The handler to call on tcp post init.
*/
@@ -164,19 +186,19 @@ public:
*
* @param ec A status value
*/
void set_proxy(const std::string & uri, lib::error_code & ec) {
void set_proxy(std::string const & uri, lib::error_code & ec) {
// TODO: return errors for illegal URIs here?
// TODO: should https urls be illegal for the moment?
m_proxy = uri;
m_proxy_data.reset(new proxy_data());
m_proxy_data = lib::make_shared<proxy_data>();
ec = lib::error_code();
}
/// Set the proxy to connect through (exception)
void set_proxy(const std::string & uri) {
void set_proxy(std::string const & uri) {
lib::error_code ec;
set_proxy(uri,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
/// Set the basic auth credentials to use (exception free)
@@ -192,8 +214,8 @@ public:
*
* @param ec A status value
*/
void set_proxy_basic_auth(const std::string & username, const
std::string & password, lib::error_code & ec)
void set_proxy_basic_auth(std::string const & username, std::string const &
password, lib::error_code & ec)
{
if (!m_proxy_data) {
ec = make_error_code(websocketpp::error::invalid_state);
@@ -207,12 +229,12 @@ public:
}
/// Set the basic auth credentials to use (exception)
void set_proxy_basic_auth(const std::string & username, const
std::string & password)
void set_proxy_basic_auth(std::string const & username, std::string const &
password)
{
lib::error_code ec;
set_proxy_basic_auth(username,password,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
/// Set the proxy timeout duration (exception free)
@@ -239,10 +261,10 @@ public:
void set_proxy_timeout(long duration) {
lib::error_code ec;
set_proxy_timeout(duration,ec);
if (ec) { throw ec; }
if (ec) { throw exception(ec); }
}
const std::string & get_proxy() const {
std::string const & get_proxy() const {
return m_proxy;
}
@@ -289,11 +311,9 @@ public:
* needed.
*/
timer_ptr set_timer(long duration, timer_handler callback) {
timer_ptr new_timer(
new boost::asio::deadline_timer(
*m_io_service,
boost::posix_time::milliseconds(duration)
)
timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
lib::ref(*m_io_service),
lib::asio::milliseconds(duration)
);
if (config::enable_multithreading) {
@@ -322,15 +342,15 @@ public:
*
* TODO: candidate for protected status
*
* @param t Pointer to the timer in question
* @param post_timer Pointer to the timer in question
* @param callback The function to call back
* @param ec The status code
*/
void handle_timer(timer_ptr t, timer_handler callback,
boost::system::error_code const & ec)
void handle_timer(timer_ptr, timer_handler callback,
lib::asio::error_code const & ec)
{
if (ec) {
if (ec == boost::asio::error::operation_aborted) {
if (ec == lib::asio::error::operation_aborted) {
callback(make_error_code(transport::error::operation_aborted));
} else {
log_err(log::elevel::info,"asio handle_timer",ec);
@@ -340,16 +360,39 @@ public:
callback(lib::error_code());
}
}
protected:
/// Get a pointer to this connection's strand
strand_ptr get_strand() {
return m_strand;
}
/// Get the internal transport error code for a closed/failed connection
/**
* Retrieves a machine readable detailed error code indicating the reason
* that the connection was closed or failed. Valid only after the close or
* fail handler is called.
*
* Primarily used if you are using mismatched asio / system_error
* implementations such as `boost::asio` with `std::system_error`. In these
* cases the transport error type is different than the library error type
* and some WebSocket++ functions that return transport errors via the
* library error code type will be coerced into a catch all `pass_through`
* or `tls_error` error. This method will return the original machine
* readable transport error in the native type.
*
* @since 0.7.0
*
* @return Error code indicating the reason the connection was closed or
* failed
*/
lib::asio::error_code get_transport_ec() const {
return m_tec;
}
/// Initialize transport for reading
/**
* init_asio is called once immediately after construction to initialize
* boost::asio components to the io_service
* Asio components to the io_service
*
* The transport initialization sequence consists of the following steps:
* - Pre-init: the underlying socket is initialized to the point where
@@ -363,6 +406,7 @@ protected:
* read or write the WebSocket handshakes. At this point the original
* callback function is called.
*/
protected:
void init(init_handler callback) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"asio connection init");
@@ -371,12 +415,11 @@ protected:
// TODO: pre-init timeout. Right now no implemented socket policies
// actually have an asyncronous pre-init
m_init_handler = callback;
socket_con_type::pre_init(
lib::bind(
&type::handle_pre_init,
get_shared(),
callback,
lib::placeholders::_1
)
);
@@ -407,7 +450,7 @@ protected:
/// Finish constructing the transport
/**
* init_asio is called once immediately after construction to initialize
* boost::asio components to the io_service
* Asio components to the io_service.
*
* @param io_service A pointer to the io_service to register with this
* connection
@@ -415,38 +458,20 @@ protected:
* @return Status code for the success or failure of the initialization
*/
lib::error_code init_asio (io_service_ptr io_service) {
// do we need to store or use the io_service at this level?
m_io_service = io_service;
if (config::enable_multithreading) {
m_strand.reset(new boost::asio::strand(*io_service));
m_async_read_handler = m_strand->wrap(lib::bind(
&type::handle_async_read, get_shared(),
lib::placeholders::_1, lib::placeholders::_2
));
m_async_write_handler = m_strand->wrap(lib::bind(
&type::handle_async_write, get_shared(),
lib::placeholders::_1, lib::placeholders::_2
));
} else {
// TODO: goal: not have this line here
//m_strand.reset(new boost::asio::strand(*io_service));
m_async_read_handler = lib::bind(
&type::handle_async_read, get_shared(),
lib::placeholders::_1, lib::placeholders::_2
);
m_async_write_handler = lib::bind(&type::handle_async_write,
get_shared(), lib::placeholders::_1, lib::placeholders::_2);
m_strand = lib::make_shared<lib::asio::io_service::strand>(
lib::ref(*io_service));
}
return socket_con_type::init_asio(io_service, m_strand, m_is_server);
lib::error_code ec = socket_con_type::init_asio(io_service, m_strand,
m_is_server);
return ec;
}
void handle_pre_init(lib::error_code const & ec) {
void handle_pre_init(init_handler callback, lib::error_code const & ec) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"asio connection handle pre_init");
}
@@ -456,48 +481,60 @@ protected:
}
if (ec) {
m_init_handler(ec);
callback(ec);
}
// If we have a proxy set issue a proxy connect, otherwise skip to
// post_init
if (!m_proxy.empty()) {
proxy_write();
proxy_write(callback);
} else {
post_init();
post_init(callback);
}
}
void post_init() {
void post_init(init_handler callback) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"asio connection post_init");
}
timer_ptr post_timer;
post_timer = set_timer(
config::timeout_socket_post_init,
lib::bind(
&type::handle_post_init_timeout,
get_shared(),
post_timer,
m_init_handler,
lib::placeholders::_1
)
);
if (config::timeout_socket_post_init > 0) {
post_timer = set_timer(
config::timeout_socket_post_init,
lib::bind(
&type::handle_post_init_timeout,
get_shared(),
post_timer,
callback,
lib::placeholders::_1
)
);
}
socket_con_type::post_init(
lib::bind(
&type::handle_post_init,
get_shared(),
post_timer,
m_init_handler,
callback,
lib::placeholders::_1
)
);
}
void handle_post_init_timeout(timer_ptr post_timer, init_handler callback,
const lib::error_code& ec)
/// Post init timeout callback
/**
* The timer pointer is included to ensure the timer isn't destroyed until
* after it has expired.
*
* @param post_timer Pointer to the timer in question
* @param callback The function to call back
* @param ec The status code
*/
void handle_post_init_timeout(timer_ptr, init_handler callback,
lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -518,35 +555,46 @@ protected:
}
}
m_alog.write(log::alevel::devel,"Asio transport post-init timed out");
socket_con_type::cancel_socket();
m_alog.write(log::alevel::devel, "Asio transport post-init timed out");
cancel_socket_checked();
callback(ret_ec);
}
void handle_post_init(timer_ptr post_timer, init_handler callback, const
lib::error_code& ec)
/// Post init timeout callback
/**
* The timer pointer is included to ensure the timer isn't destroyed until
* after it has expired.
*
* @param post_timer Pointer to the timer in question
* @param callback The function to call back
* @param ec The status code
*/
void handle_post_init(timer_ptr post_timer, init_handler callback,
lib::error_code const & ec)
{
if (ec == transport::error::operation_aborted ||
post_timer->expires_from_now().is_negative())
(post_timer && lib::asio::is_neg(post_timer->expires_from_now())))
{
m_alog.write(log::alevel::devel,"post_init cancelled");
return;
}
post_timer->cancel();
if (post_timer) {
post_timer->cancel();
}
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"asio connection handle_post_init");
}
if (m_tcp_post_init_handler) {
if (m_tcp_post_init_handler) {
m_tcp_post_init_handler(m_connection_hdl);
}
callback(ec);
}
void proxy_write() {
void proxy_write(init_handler callback) {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"asio connection proxy_write");
}
@@ -554,14 +602,14 @@ protected:
if (!m_proxy_data) {
m_elog.write(log::elevel::library,
"assertion failed: !m_proxy_data in asio::connection::proxy_write");
m_init_handler(make_error_code(error::general));
callback(make_error_code(error::general));
return;
}
m_proxy_data->write_buf = m_proxy_data->req.raw();
m_bufs.push_back(boost::asio::buffer(m_proxy_data->write_buf.data(),
m_proxy_data->write_buf.size()));
m_bufs.push_back(lib::asio::buffer(m_proxy_data->write_buf.data(),
m_proxy_data->write_buf.size()));
m_alog.write(log::alevel::devel,m_proxy_data->write_buf);
@@ -571,29 +619,29 @@ protected:
lib::bind(
&type::handle_proxy_timeout,
get_shared(),
m_init_handler,
callback,
lib::placeholders::_1
)
);
// Send proxy request
if (config::enable_multithreading) {
boost::asio::async_write(
lib::asio::async_write(
socket_con_type::get_next_layer(),
m_bufs,
m_strand->wrap(lib::bind(
&type::handle_proxy_write, get_shared(),
m_init_handler,
callback,
lib::placeholders::_1
))
);
} else {
boost::asio::async_write(
lib::asio::async_write(
socket_con_type::get_next_layer(),
m_bufs,
lib::bind(
&type::handle_proxy_write, get_shared(),
m_init_handler,
callback,
lib::placeholders::_1
)
);
@@ -612,13 +660,13 @@ protected:
} else {
m_alog.write(log::alevel::devel,
"asio handle_proxy_write timer expired");
socket_con_type::cancel_socket();
cancel_socket_checked();
callback(make_error_code(transport::error::timeout));
}
}
void handle_proxy_write(init_handler callback,
boost::system::error_code const & ec)
lib::asio::error_code const & ec)
{
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,
@@ -630,8 +678,8 @@ protected:
// Timer expired or the operation was aborted for some reason.
// Whatever aborted it will be issuing the callback so we are safe to
// return
if (ec == boost::asio::error::operation_aborted ||
m_proxy_data->timer->expires_from_now().is_negative())
if (ec == lib::asio::error::operation_aborted ||
lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
{
m_elog.write(log::elevel::devel,"write operation aborted");
return;
@@ -661,7 +709,7 @@ protected:
}
if (config::enable_multithreading) {
boost::asio::async_read_until(
lib::asio::async_read_until(
socket_con_type::get_next_layer(),
m_proxy_data->read_buf,
"\r\n\r\n",
@@ -672,7 +720,7 @@ protected:
))
);
} else {
boost::asio::async_read_until(
lib::asio::async_read_until(
socket_con_type::get_next_layer(),
m_proxy_data->read_buf,
"\r\n\r\n",
@@ -685,8 +733,14 @@ protected:
}
}
/// Proxy read callback
/**
* @param init_handler The function to call back
* @param ec The status code
* @param bytes_transferred The number of bytes read
*/
void handle_proxy_read(init_handler callback,
boost::system::error_code const & ec, size_t bytes_transferred)
lib::asio::error_code const & ec, size_t)
{
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,
@@ -696,8 +750,8 @@ protected:
// Timer expired or the operation was aborted for some reason.
// Whatever aborted it will be issuing the callback so we are safe to
// return
if (ec == boost::asio::error::operation_aborted ||
m_proxy_data->timer->expires_from_now().is_negative())
if (ec == lib::asio::error::operation_aborted ||
lib::asio::is_neg(m_proxy_data->timer->expires_from_now()))
{
m_elog.write(log::elevel::devel,"read operation aborted");
return;
@@ -758,15 +812,11 @@ protected:
m_proxy_data.reset();
// Continue with post proxy initialization
post_init();
post_init(callback);
}
}
/// read at least num_bytes bytes into buf and then call handler.
/**
*
*
*/
void async_read_at_least(size_t num_bytes, char *buf, size_t len,
read_handler handler)
{
@@ -776,13 +826,6 @@ protected:
m_alog.write(log::alevel::devel,s.str());
}
if (!m_async_read_handler) {
m_alog.write(log::alevel::devel,
"async_read_at_least called after async_shutdown");
handler(make_error_code(transport::error::action_after_shutdown), 0);
return;
}
// TODO: safety vs speed ?
// maybe move into an if devel block
/*if (num_bytes > len) {
@@ -793,97 +836,161 @@ protected:
return;
}*/
m_read_handler = handler;
boost::asio::async_read(
socket_con_type::get_socket(),
boost::asio::buffer(buf,len),
boost::asio::transfer_at_least(num_bytes),
make_custom_alloc_handler(
m_read_handler_allocator,
m_async_read_handler
)
);
if (config::enable_multithreading) {
lib::asio::async_read(
socket_con_type::get_socket(),
lib::asio::buffer(buf,len),
lib::asio::transfer_at_least(num_bytes),
m_strand->wrap(make_custom_alloc_handler(
m_read_handler_allocator,
lib::bind(
&type::handle_async_read, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
))
);
} else {
lib::asio::async_read(
socket_con_type::get_socket(),
lib::asio::buffer(buf,len),
lib::asio::transfer_at_least(num_bytes),
make_custom_alloc_handler(
m_read_handler_allocator,
lib::bind(
&type::handle_async_read, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
)
);
}
}
void handle_async_read(const boost::system::error_code& ec,
void handle_async_read(read_handler handler, lib::asio::error_code const & ec,
size_t bytes_transferred)
{
if (!ec) {
m_read_handler(lib::error_code(), bytes_transferred);
return;
}
m_alog.write(log::alevel::devel, "asio con handle_async_read");
// translate boost error codes into more lib::error_codes
if (ec == boost::asio::error::eof) {
m_read_handler(make_error_code(transport::error::eof),
bytes_transferred);
} else if (ec.value() == 335544539) {
m_read_handler(make_error_code(transport::error::tls_short_read),
bytes_transferred);
// translate asio error codes into more lib::error_codes
lib::error_code tec;
if (ec == lib::asio::error::eof) {
tec = make_error_code(transport::error::eof);
} else if (ec) {
// We don't know much more about the error at this point. As our
// socket/security policy if it knows more:
tec = socket_con_type::translate_ec(ec);
m_tec = ec;
if (tec == transport::error::tls_error ||
tec == transport::error::pass_through)
{
// These are aggregate/catch all errors. Log some human readable
// information to the info channel to give library users some
// more details about why the upstream method may have failed.
log_err(log::elevel::info,"asio async_read_at_least",ec);
}
}
if (handler) {
handler(tec,bytes_transferred);
} else {
log_err(log::elevel::info,"asio async_read_at_least",ec);
m_read_handler(make_error_code(transport::error::pass_through),
bytes_transferred);
// This can happen in cases where the connection is terminated while
// the transport is waiting on a read.
m_alog.write(log::alevel::devel,
"handle_async_read called with null read handler");
}
}
/// Initiate a potentially asyncronous write of the given buffer
void async_write(const char* buf, size_t len, write_handler handler) {
if (!m_async_write_handler) {
m_alog.write(log::alevel::devel,
"async_write (single) called after async_shutdown");
handler(make_error_code(transport::error::action_after_shutdown));
return;
m_bufs.push_back(lib::asio::buffer(buf,len));
if (config::enable_multithreading) {
lib::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
m_strand->wrap(make_custom_alloc_handler(
m_write_handler_allocator,
lib::bind(
&type::handle_async_write, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
))
);
} else {
lib::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
make_custom_alloc_handler(
m_write_handler_allocator,
lib::bind(
&type::handle_async_write, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
)
);
}
m_bufs.push_back(boost::asio::buffer(buf,len));
m_write_handler = handler;
boost::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
make_custom_alloc_handler(
m_write_handler_allocator,
m_async_write_handler
)
);
}
void async_write(const std::vector<buffer>& bufs, write_handler handler) {
if (!m_async_write_handler) {
m_alog.write(log::alevel::devel,
"async_write (vector) called after async_shutdown");
handler(make_error_code(transport::error::action_after_shutdown));
return;
}
/// Initiate a potentially asyncronous write of the given buffers
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); ++it) {
m_bufs.push_back(boost::asio::buffer((*it).buf,(*it).len));
m_bufs.push_back(lib::asio::buffer((*it).buf,(*it).len));
}
m_write_handler = handler;
boost::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
make_custom_alloc_handler(
m_write_handler_allocator,
m_async_write_handler
)
);
if (config::enable_multithreading) {
lib::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
m_strand->wrap(make_custom_alloc_handler(
m_write_handler_allocator,
lib::bind(
&type::handle_async_write, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
))
);
} else {
lib::asio::async_write(
socket_con_type::get_socket(),
m_bufs,
make_custom_alloc_handler(
m_write_handler_allocator,
lib::bind(
&type::handle_async_write, get_shared(),
handler,
lib::placeholders::_1, lib::placeholders::_2
)
)
);
}
}
void handle_async_write(boost::system::error_code const & ec,
size_t bytes_transferred)
{
/// Async write callback
/**
* @param ec The status code
* @param bytes_transferred The number of bytes read
*/
void handle_async_write(write_handler handler, lib::asio::error_code const & ec, size_t) {
m_bufs.clear();
lib::error_code tec;
if (ec) {
log_err(log::elevel::info,"asio async_write",ec);
m_write_handler(make_error_code(transport::error::pass_through));
tec = make_error_code(transport::error::pass_through);
}
if (handler) {
handler(tec);
} else {
m_write_handler(lib::error_code());
// This can happen in cases where the connection is terminated while
// the transport is waiting on a read.
m_alog.write(log::alevel::devel,
"handle_async_write called with null write handler");
}
}
@@ -931,13 +1038,6 @@ protected:
m_alog.write(log::alevel::devel,"asio connection async_shutdown");
}
// Reset cached handlers now that we won't be reading or writing anymore
// These cached handlers store shared pointers to this connection and will leak
// the connection if not destroyed.
m_async_read_handler = 0;
m_async_write_handler = 0;
m_init_handler = 0;
timer_ptr shutdown_timer;
shutdown_timer = set_timer(
config::timeout_socket_shutdown,
@@ -961,8 +1061,14 @@ protected:
);
}
void handle_async_shutdown_timeout(timer_ptr shutdown_timer, init_handler
callback, const lib::error_code& ec)
/// Async shutdown timeout handler
/**
* @param shutdown_timer A pointer to the timer to keep it in scope
* @param callback The function to call back
* @param ec The status code
*/
void handle_async_shutdown_timeout(timer_ptr, init_handler callback,
lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -973,7 +1079,7 @@ protected:
return;
}
log_err(log::elevel::devel,"asio handle_async_socket_shutdown",ec);
log_err(log::elevel::devel,"asio handle_async_shutdown_timeout",ec);
ret_ec = ec;
} else {
ret_ec = make_error_code(transport::error::timeout);
@@ -981,15 +1087,15 @@ protected:
m_alog.write(log::alevel::devel,
"Asio transport socket shutdown timed out");
socket_con_type::cancel_socket();
cancel_socket_checked();
callback(ret_ec);
}
void handle_async_shutdown(timer_ptr shutdown_timer, shutdown_handler
callback, const boost::system::error_code & ec)
callback, lib::asio::error_code const & ec)
{
if (ec == boost::asio::error::operation_aborted ||
shutdown_timer->expires_from_now().is_negative())
if (ec == lib::asio::error::operation_aborted ||
lib::asio::is_neg(shutdown_timer->expires_from_now()))
{
m_alog.write(log::alevel::devel,"async_shutdown cancelled");
return;
@@ -997,26 +1103,53 @@ protected:
shutdown_timer->cancel();
lib::error_code tec;
if (ec) {
log_err(log::elevel::info,"asio async_shutdown",ec);
if (ec == boost::asio::error::not_connected) {
if (ec == lib::asio::error::not_connected) {
// The socket was already closed when we tried to close it. This
// happens periodically (usually if a read or write fails
// earlier and if it is a real error will be caught at another
// level of the stack.
callback(lib::error_code());
} else {
callback(make_error_code(transport::error::pass_through));
// We don't know anything more about this error, give our
// socket/security policy a crack at it.
tec = socket_con_type::translate_ec(ec);
m_tec = ec;
if (tec == transport::error::tls_short_read) {
// TLS short read at this point is somewhat expected if both
// sides try and end the connection at the same time or if
// SSLv2 is being used. In general there is nothing that can
// be done here other than a low level development log.
} else {
// all other errors are effectively pass through errors of
// some sort so print some detail on the info channel for
// library users to look up if needed.
log_err(log::elevel::info,"asio async_shutdown",ec);
}
}
} else {
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,
"asio con handle_async_shutdown");
}
}
callback(tec);
}
callback(lib::error_code());
/// Cancel the underlying socket and log any errors
void cancel_socket_checked() {
lib::asio::error_code cec = socket_con_type::cancel_socket();
if (cec) {
if (cec == lib::asio::error::operation_not_supported) {
// cancel not supported on this OS, ignore and log at dev level
m_alog.write(log::alevel::devel, "socket cancel not supported");
} else {
log_err(log::elevel::warn, "socket cancel failed", cec);
}
}
}
private:
/// Convenience method for logging the code and message for an error_code
template <typename error_type>
@@ -1037,7 +1170,7 @@ private:
request_type req;
response_type res;
std::string write_buf;
boost::asio::streambuf read_buf;
lib::asio::streambuf read_buf;
long timeout_proxy;
timer_ptr timer;
};
@@ -1050,7 +1183,10 @@ private:
strand_ptr m_strand;
connection_hdl m_connection_hdl;
std::vector<boost::asio::const_buffer> m_bufs;
std::vector<lib::asio::const_buffer> m_bufs;
/// Detailed internal error code
lib::asio::error_code m_tec;
// Handlers
tcp_init_handler m_tcp_pre_init_handler;
@@ -1058,13 +1194,6 @@ private:
handler_allocator m_read_handler_allocator;
handler_allocator m_write_handler_allocator;
read_handler m_read_handler;
write_handler m_write_handler;
init_handler m_init_handler;
async_read_handler m_async_read_handler;
async_write_handler m_async_write_handler;
};
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,26 +28,26 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_HPP
#include <websocketpp/common/functional.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/endpoint.hpp>
#include <websocketpp/transport/asio/connection.hpp>
#include <websocketpp/transport/asio/security/none.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/system/error_code.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <iostream>
#include <websocketpp/common/functional.hpp>
#include <sstream>
#include <string>
namespace websocketpp {
namespace transport {
namespace asio {
/// Boost Asio based endpoint transport component
/// Asio based endpoint transport component
/**
* transport::asio::endpoint implements an endpoint transport component using
* Boost ASIO.
* Asio.
*/
template <typename config>
class endpoint : public config::socket_type {
@@ -77,20 +77,22 @@ public:
typedef typename transport_con_type::ptr transport_con_ptr;
/// Type of a pointer to the ASIO io_service being used
typedef boost::asio::io_service* io_service_ptr;
typedef lib::asio::io_service * io_service_ptr;
/// Type of a shared pointer to the acceptor being used
typedef lib::shared_ptr<boost::asio::ip::tcp::acceptor> acceptor_ptr;
typedef lib::shared_ptr<lib::asio::ip::tcp::acceptor> acceptor_ptr;
/// Type of a shared pointer to the resolver being used
typedef lib::shared_ptr<boost::asio::ip::tcp::resolver> resolver_ptr;
typedef lib::shared_ptr<lib::asio::ip::tcp::resolver> resolver_ptr;
/// Type of timer handle
typedef lib::shared_ptr<boost::asio::deadline_timer> timer_ptr;
typedef lib::shared_ptr<lib::asio::steady_timer> timer_ptr;
/// Type of a shared pointer to an io_service work object
typedef lib::shared_ptr<boost::asio::io_service::work> work_ptr;
typedef lib::shared_ptr<lib::asio::io_service::work> work_ptr;
// generate and manage our own io_service
explicit endpoint()
: m_external_io_service(false)
: m_io_service(NULL)
, m_external_io_service(false)
, m_listen_backlog(0)
, m_reuse_addr(false)
, m_state(UNINITIALIZED)
{
//std::cout << "transport::asio::endpoint constructor" << std::endl;
@@ -98,7 +100,11 @@ public:
~endpoint() {
// clean up our io_service if we were initialized with an internal one.
// Explicitly destroy local objects
m_acceptor.reset();
m_resolver.reset();
m_work.reset();
if (m_state != UNINITIALIZED && !m_external_io_service) {
delete m_io_service;
}
@@ -107,22 +113,28 @@ public:
/// transport::asio objects are moveable but not copyable or assignable.
/// The following code sets this situation up based on whether or not we
/// have C++11 support or not
#ifdef _WEBSOCKETPP_DELETED_FUNCTIONS_
endpoint(const endpoint& src) = delete;
#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
endpoint(const endpoint & src) = delete;
endpoint& operator= (const endpoint & rhs) = delete;
#else
private:
endpoint(const endpoint& src);
endpoint& operator= (const endpoint & rhs);
endpoint(const endpoint & src);
endpoint & operator= (const endpoint & rhs);
public:
#endif
#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_
#ifdef _WEBSOCKETPP_RVALUE_REFERENCES_
endpoint (endpoint&& src)
: m_io_service(src.m_io_service)
#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_
endpoint (endpoint && src)
: config::socket_type(std::move(src))
, m_tcp_pre_init_handler(src.m_tcp_pre_init_handler)
, m_tcp_post_init_handler(src.m_tcp_post_init_handler)
, m_io_service(src.m_io_service)
, m_external_io_service(src.m_external_io_service)
, m_acceptor(src.m_acceptor)
, m_listen_backlog(0)
, m_listen_backlog(lib::asio::socket_base::max_connections)
, m_reuse_addr(src.m_reuse_addr)
, m_elog(src.m_elog)
, m_alog(src.m_alog)
, m_state(src.m_state)
{
src.m_io_service = NULL;
@@ -131,23 +143,27 @@ public:
src.m_state = UNINITIALIZED;
}
endpoint& operator= (const endpoint && rhs) {
/*endpoint & operator= (const endpoint && rhs) {
if (this != &rhs) {
m_io_service = rhs.m_io_service;
m_external_io_service = rhs.m_external_io_service;
m_acceptor = rhs.m_acceptor;
m_listen_backlog = rhs.m_listen_backlog
m_listen_backlog = rhs.m_listen_backlog;
m_reuse_addr = rhs.m_reuse_addr;
m_state = rhs.m_state;
rhs.m_io_service = NULL;
rhs.m_external_io_service = false;
rhs.m_acceptor = NULL;
rhs.m_listen_backlog = 0;
rhs.m_listen_backlog = lib::asio::socket_base::max_connections;
rhs.m_state = UNINITIALIZED;
// TODO: this needs to be updated
}
return *this;
}
#endif
}*/
#endif // _WEBSOCKETPP_MOVE_SEMANTICS_
/// Return whether or not the endpoint produces secure connections.
bool is_secure() const {
return socket_type::is_secure();
@@ -175,7 +191,9 @@ public:
m_io_service = ptr;
m_external_io_service = true;
m_acceptor.reset(new boost::asio::ip::tcp::acceptor(*m_io_service));
m_acceptor = lib::make_shared<lib::asio::ip::tcp::acceptor>(
lib::ref(*m_io_service));
m_state = READY;
ec = lib::error_code();
}
@@ -191,9 +209,7 @@ public:
void init_asio(io_service_ptr ptr) {
lib::error_code ec;
init_asio(ptr,ec);
if (ec) {
throw ec;
}
if (ec) { throw exception(ec); }
}
/// Initialize asio transport with internal io_service (exception free)
@@ -206,7 +222,17 @@ public:
* @param ec Set to indicate what error occurred, if any.
*/
void init_asio(lib::error_code & ec) {
init_asio(new boost::asio::io_service(),ec);
// Use a smart pointer until the call is successful and ownership has
// successfully been taken. Use unique_ptr when available.
// TODO: remove the use of auto_ptr when C++98/03 support is no longer
// necessary.
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
#else
lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
#endif
init_asio(service.get(), ec);
if( !ec ) service.release(); // Call was successful, transfer ownership
m_external_io_service = false;
}
@@ -218,7 +244,18 @@ public:
* @see init_asio(io_service_ptr ptr)
*/
void init_asio() {
init_asio(new boost::asio::io_service());
// Use a smart pointer until the call is successful and ownership has
// successfully been taken. Use unique_ptr when available.
// TODO: remove the use of auto_ptr when C++98/03 support is no longer
// necessary.
#ifdef _WEBSOCKETPP_CPP11_MEMORY_
lib::unique_ptr<lib::asio::io_service> service(new lib::asio::io_service());
#else
lib::auto_ptr<lib::asio::io_service> service(new lib::asio::io_service());
#endif
init_asio( service.get() );
// If control got this far without an exception, then ownership has successfully been taken
service.release();
m_external_io_service = false;
}
@@ -228,7 +265,7 @@ public:
* established but before any additional wrappers (proxy connects, TLS
* handshakes, etc) have been performed.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*
* @param h The handler to call on tcp pre init.
*/
@@ -257,7 +294,7 @@ public:
* etc have been performed. This is fired before any bytes are read or any
* WebSocket specific handshake logic has been performed.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*
* @param h The handler to call on tcp post init.
*/
@@ -280,7 +317,7 @@ public:
* A value of zero will use the operating system default. This is the
* default value.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*
* @param backlog The maximum length of the queue of pending connections
*/
@@ -288,6 +325,24 @@ public:
m_listen_backlog = backlog;
}
/// Sets whether to use the SO_REUSEADDR flag when opening listening sockets
/**
* Specifies whether or not to use the SO_REUSEADDR TCP socket option. What
* this flag does depends on your operating system. Please consult operating
* system documentation for more details.
*
* New values affect future calls to listen only.
*
* The default is false.
*
* @since 0.3.0
*
* @param value Whether or not to use the SO_REUSEADDR option
*/
void set_reuse_addr(bool value) {
m_reuse_addr = value;
}
/// Retrieve a reference to the endpoint's io_service
/**
* The io_service may be an internal or external one. This may be used to
@@ -299,9 +354,31 @@ public:
*
* @return A reference to the endpoint's io_service
*/
boost::asio::io_service & get_io_service() {
lib::asio::io_service & get_io_service() {
return *m_io_service;
}
/// Get local TCP endpoint
/**
* Extracts the local endpoint from the acceptor. This represents the
* address that WebSocket++ is listening on.
*
* Sets a bad_descriptor error if the acceptor is not currently listening
* or otherwise unavailable.
*
* @since 0.7.0
*
* @param ec Set to indicate what error occurred, if any.
* @return The local endpoint
*/
lib::asio::ip::tcp::endpoint get_local_endpoint(lib::asio::error_code & ec) {
if (m_acceptor) {
return m_acceptor->local_endpoint(ec);
} else {
ec = lib::asio::error::make_error_code(lib::asio::error::bad_descriptor);
return lib::asio::ip::tcp::endpoint();
}
}
/// Set up endpoint for listening manually (exception free)
/**
@@ -311,7 +388,7 @@ public:
* @param ep An endpoint to read settings from
* @param ec Set to indicate what error occurred, if any.
*/
void listen(boost::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
void listen(lib::asio::ip::tcp::endpoint const & ep, lib::error_code & ec)
{
if (m_state != READY) {
m_elog->write(log::elevel::library,
@@ -323,16 +400,28 @@ public:
m_alog->write(log::alevel::devel,"asio::listen");
m_acceptor->open(ep.protocol());
m_acceptor->set_option(boost::asio::socket_base::reuse_address(true));
m_acceptor->bind(ep);
if (m_listen_backlog == 0) {
m_acceptor->listen();
lib::asio::error_code bec;
m_acceptor->open(ep.protocol(),bec);
if (!bec) {
m_acceptor->set_option(lib::asio::socket_base::reuse_address(m_reuse_addr),bec);
}
if (!bec) {
m_acceptor->bind(ep,bec);
}
if (!bec) {
m_acceptor->listen(m_listen_backlog,bec);
}
if (bec) {
if (m_acceptor->is_open()) {
m_acceptor->close();
}
log_err(log::elevel::info,"asio listen",bec);
ec = make_error_code(error::pass_through);
} else {
m_acceptor->listen(m_listen_backlog);
m_state = LISTENING;
ec = lib::error_code();
}
m_state = LISTENING;
ec = lib::error_code();
}
/// Set up endpoint for listening manually
@@ -341,12 +430,10 @@ public:
*
* @param ep An endpoint to read settings from
*/
void listen(boost::asio::ip::tcp::endpoint const & ep) {
void listen(lib::asio::ip::tcp::endpoint const & ep) {
lib::error_code ec;
listen(ep,ec);
if (ec) {
throw ec;
}
if (ec) { throw exception(ec); }
}
/// Set up endpoint for listening with protocol and port (exception free)
@@ -356,8 +443,8 @@ public:
* listening.
*
* Common options include:
* - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6()
* - IPv4 only: boost::asio::ip::tcp::v4()
* - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
* - IPv4 only: lib::asio::ip::tcp::v4()
*
* @param internet_protocol The internet protocol to use.
* @param port The port to listen on.
@@ -367,7 +454,7 @@ public:
void listen(InternetProtocol const & internet_protocol, uint16_t port,
lib::error_code & ec)
{
boost::asio::ip::tcp::endpoint ep(internet_protocol, port);
lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
listen(ep,ec);
}
@@ -378,8 +465,8 @@ public:
* listening.
*
* Common options include:
* - IPv6 with mapped IPv4 for dual stack hosts boost::asio::ip::tcp::v6()
* - IPv4 only: boost::asio::ip::tcp::v4()
* - IPv6 with mapped IPv4 for dual stack hosts lib::asio::ip::tcp::v6()
* - IPv4 only: lib::asio::ip::tcp::v4()
*
* @param internet_protocol The internet protocol to use.
* @param port The port to listen on.
@@ -387,7 +474,7 @@ public:
template <typename InternetProtocol>
void listen(InternetProtocol const & internet_protocol, uint16_t port)
{
boost::asio::ip::tcp::endpoint ep(internet_protocol, port);
lib::asio::ip::tcp::endpoint ep(internet_protocol, port);
listen(ep);
}
@@ -404,7 +491,7 @@ public:
* @param ec Set to indicate what error occurred, if any.
*/
void listen(uint16_t port, lib::error_code & ec) {
listen(boost::asio::ip::tcp::v6(), port, ec);
listen(lib::asio::ip::tcp::v6(), port, ec);
}
/// Set up endpoint for listening on a port
@@ -420,13 +507,13 @@ public:
* @param ec Set to indicate what error occurred, if any.
*/
void listen(uint16_t port) {
listen(boost::asio::ip::tcp::v6(), port);
listen(lib::asio::ip::tcp::v6(), port);
}
/// Set up endpoint for listening on a host and service (exception free)
/**
* Bind the internal acceptor using the given host and service. More details
* about what host and service can be are available in the boost asio
* about what host and service can be are available in the Asio
* documentation for ip::basic_resolver_query::basic_resolver_query's
* constructors.
*
@@ -442,7 +529,7 @@ public:
void listen(std::string const & host, std::string const & service,
lib::error_code & ec)
{
using boost::asio::ip::tcp;
using lib::asio::ip::tcp;
tcp::resolver r(*m_io_service);
tcp::resolver::query query(host, service);
tcp::resolver::iterator endpoint_iterator = r.resolve(query);
@@ -459,7 +546,7 @@ public:
/// Set up endpoint for listening on a host and service
/**
* Bind the internal acceptor using the given host and service. More details
* about what host and service can be are available in the boost asio
* about what host and service can be are available in the Asio
* documentation for ip::basic_resolver_query::basic_resolver_query's
* constructors.
*
@@ -476,9 +563,7 @@ public:
{
lib::error_code ec;
listen(host,service,ec);
if (ec) {
throw ec;
}
if (ec) { throw exception(ec); }
}
/// Stop listening (exception free)
@@ -513,9 +598,15 @@ public:
void stop_listening() {
lib::error_code ec;
stop_listening(ec);
if (ec) {
throw ec;
}
if (ec) { throw exception(ec); }
}
/// Check if the endpoint is listening
/**
* @return Whether or not the endpoint is listening.
*/
bool is_listening() const {
return (m_state == LISTENING);
}
/// wraps the run method of the internal io_service object
@@ -566,10 +657,12 @@ public:
* called either before the endpoint has run out of work or before it was
* started
*
* @since 0.4.0-alpha1
* @since 0.3.0
*/
void start_perpetual() {
m_work.reset(new boost::asio::io_service::work(*m_io_service));
m_work = lib::make_shared<lib::asio::io_service::work>(
lib::ref(*m_io_service)
);
}
/// Clears the endpoint's perpetual flag, allowing it to exit when empty
@@ -578,7 +671,7 @@ public:
* method to exit normally when it runs out of connections. If there are
* currently active connections it will not end until they are complete.
*
* @since 0.4.0-alpha1
* @since 0.3.0
*/
void stop_perpetual() {
m_work.reset();
@@ -597,11 +690,9 @@ public:
* needed.
*/
timer_ptr set_timer(long duration, timer_handler callback) {
timer_ptr new_timer(
new boost::asio::deadline_timer(
*m_io_service,
boost::posix_time::milliseconds(duration)
)
timer_ptr new_timer = lib::make_shared<lib::asio::steady_timer>(
*m_io_service,
lib::asio::milliseconds(duration)
);
new_timer->async_wait(
@@ -617,7 +708,7 @@ public:
return new_timer;
}
/// Timer callback
/// Timer handler
/**
* The timer pointer is included to ensure the timer isn't destroyed until
* after it has expired.
@@ -626,11 +717,11 @@ public:
* @param callback The function to call back
* @param ec A status code indicating an error, if any.
*/
void handle_timer(timer_ptr t, timer_handler callback,
boost::system::error_code const & ec)
void handle_timer(timer_ptr, timer_handler callback,
lib::asio::error_code const & ec)
{
if (ec) {
if (ec == boost::asio::error::operation_aborted) {
if (ec == lib::asio::error::operation_aborted) {
callback(make_error_code(transport::error::operation_aborted));
} else {
m_elog->write(log::elevel::info,
@@ -653,10 +744,8 @@ public:
lib::error_code & ec)
{
if (m_state != LISTENING) {
m_elog->write(log::elevel::library,
"asio::async_accept called from the wrong state");
using websocketpp::error::make_error_code;
ec = make_error_code(websocketpp::error::invalid_state);
ec = make_error_code(websocketpp::error::async_accept_not_listening);
return;
}
@@ -693,9 +782,7 @@ public:
void async_accept(transport_con_ptr tcon, accept_handler callback) {
lib::error_code ec;
async_accept(tcon,callback,ec);
if (ec) {
throw ec;
}
if (ec) { throw exception(ec); }
}
protected:
/// Initialize logging
@@ -713,16 +800,20 @@ protected:
m_elog = e;
}
void handle_accept(accept_handler callback, boost::system::error_code const
& boost_ec)
void handle_accept(accept_handler callback, lib::asio::error_code const &
asio_ec)
{
lib::error_code ret_ec;
m_alog->write(log::alevel::devel, "asio::handle_accept");
if (boost_ec) {
log_err(log::elevel::devel,"asio handle_accept",boost_ec);
ret_ec = make_error_code(error::pass_through);
if (asio_ec) {
if (asio_ec == lib::asio::errc::operation_canceled) {
ret_ec = make_error_code(websocketpp::error::operation_canceled);
} else {
log_err(log::elevel::info,"asio handle_accept",asio_ec);
ret_ec = make_error_code(error::pass_through);
}
}
callback(ret_ec);
@@ -731,13 +822,16 @@ protected:
/// Initiate a new connection
// TODO: there have to be some more failure conditions here
void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
using namespace boost::asio::ip;
using namespace lib::asio::ip;
// Create a resolver
if (!m_resolver) {
m_resolver.reset(new boost::asio::ip::tcp::resolver(*m_io_service));
m_resolver = lib::make_shared<lib::asio::ip::tcp::resolver>(
lib::ref(*m_io_service));
}
tcon->set_uri(u);
std::string proxy = tcon->get_proxy();
std::string host;
std::string port;
@@ -748,7 +842,7 @@ protected:
} else {
lib::error_code ec;
uri_ptr pu(new uri(proxy));
uri_ptr pu = lib::make_shared<uri>(proxy);
if (!pu->get_valid()) {
cb(make_error_code(error::proxy_invalid));
@@ -814,7 +908,16 @@ protected:
}
}
void handle_resolve_timeout(timer_ptr dns_timer, connect_handler callback,
/// DNS resolution timeout handler
/**
* The timer pointer is included to ensure the timer isn't destroyed until
* after it has expired.
*
* @param dns_timer Pointer to the timer in question
* @param callback The function to call back
* @param ec A status code indicating an error, if any.
*/
void handle_resolve_timeout(timer_ptr, connect_handler callback,
lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -838,11 +941,11 @@ protected:
}
void handle_resolve(transport_con_ptr tcon, timer_ptr dns_timer,
connect_handler callback, boost::system::error_code const & ec,
boost::asio::ip::tcp::resolver::iterator iterator)
connect_handler callback, lib::asio::error_code const & ec,
lib::asio::ip::tcp::resolver::iterator iterator)
{
if (ec == boost::asio::error::operation_aborted ||
dns_timer->expires_from_now().is_negative())
if (ec == lib::asio::error::operation_aborted ||
lib::asio::is_neg(dns_timer->expires_from_now()))
{
m_alog->write(log::alevel::devel,"async_resolve cancelled");
return;
@@ -860,7 +963,7 @@ protected:
std::stringstream s;
s << "Async DNS resolve successful. Results: ";
boost::asio::ip::tcp::resolver::iterator it, end;
lib::asio::ip::tcp::resolver::iterator it, end;
for (it = iterator; it != end; ++it) {
s << (*it).endpoint() << " ";
}
@@ -885,7 +988,7 @@ protected:
);
if (config::enable_multithreading) {
boost::asio::async_connect(
lib::asio::async_connect(
tcon->get_raw_socket(),
iterator,
tcon->get_strand()->wrap(lib::bind(
@@ -898,7 +1001,7 @@ protected:
))
);
} else {
boost::asio::async_connect(
lib::asio::async_connect(
tcon->get_raw_socket(),
iterator,
lib::bind(
@@ -913,7 +1016,17 @@ protected:
}
}
void handle_connect_timeout(transport_con_ptr tcon, timer_ptr con_timer,
/// Asio connect timeout handler
/**
* The timer pointer is included to ensure the timer isn't destroyed until
* after it has expired.
*
* @param tcon Pointer to the transport connection that is being connected
* @param con_timer Pointer to the timer in question
* @param callback The function to call back
* @param ec A status code indicating an error, if any.
*/
void handle_connect_timeout(transport_con_ptr tcon, timer_ptr,
connect_handler callback, lib::error_code const & ec)
{
lib::error_code ret_ec;
@@ -932,15 +1045,15 @@ protected:
}
m_alog->write(log::alevel::devel,"TCP connect timed out");
tcon->cancel_socket();
tcon->cancel_socket_checked();
callback(ret_ec);
}
void handle_connect(transport_con_ptr tcon, timer_ptr con_timer,
connect_handler callback, boost::system::error_code const & ec)
connect_handler callback, lib::asio::error_code const & ec)
{
if (ec == boost::asio::error::operation_aborted ||
con_timer->expires_from_now().is_negative())
if (ec == lib::asio::error::operation_aborted ||
lib::asio::is_neg(con_timer->expires_from_now()))
{
m_alog->write(log::alevel::devel,"async_connect cancelled");
return;
@@ -962,10 +1075,6 @@ protected:
callback(lib::error_code());
}
bool is_listening() const {
return (m_state == LISTENING);
}
/// Initialize a connection
/**
* init is called by an endpoint once for each newly created connection.
@@ -1022,6 +1131,7 @@ private:
// Network constants
int m_listen_backlog;
bool m_reuse_addr;
elog_type* m_elog;
alog_type* m_alog;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,15 +28,13 @@
#ifndef WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_ASIO_SOCKET_BASE_HPP
#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
// Interface that sockets/security policies must implement
@@ -57,6 +55,7 @@
// Connection
// TODO
// set_hostname(std::string hostname)
// pre_init(init_handler);
// post_init(init_handler);
@@ -65,6 +64,8 @@ namespace transport {
namespace asio {
namespace socket {
typedef lib::function<void(lib::asio::error_code const &)> shutdown_handler;
/**
* The transport::asio::socket::* classes are a set of security/socket related
* policies and support code for the ASIO transport types.
@@ -95,14 +96,20 @@ namespace error {
pass_through,
/// Required tls_init handler not present
missing_tls_init_handler
missing_tls_init_handler,
/// TLS Handshake Failed
tls_handshake_failed,
/// Failed to set TLS SNI hostname
tls_failed_sni_hostname
};
} // namespace error
/// Error category related to asio transport socket policies
class socket_category : public lib::error_category {
public:
const char *name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
return "websocketpp.transport.asio.socket";
}
@@ -119,16 +126,20 @@ public:
case error::tls_handshake_timeout:
return "TLS handshake timed out";
case error::pass_through:
return "Pass through from underlying library";
return "Pass through from socket policy";
case error::missing_tls_init_handler:
return "Required tls_init handler not present.";
case error::tls_handshake_failed:
return "TLS handshake failed";
case error::tls_failed_sni_hostname:
return "Failed to set TLS SNI hostname";
default:
return "Unknown";
}
}
};
inline const lib::error_category& get_socket_category() {
inline lib::error_category const & get_socket_category() {
static socket_category instance;
return instance;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -28,26 +28,32 @@
#ifndef WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
#define WEBSOCKETPP_TRANSPORT_SECURITY_NONE_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/transport/asio/security/base.hpp>
#include <boost/asio.hpp>
#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/memory.hpp>
#include <iostream>
#include <sstream>
#include <string>
namespace websocketpp {
namespace transport {
namespace asio {
/// A socket policy for the asio transport that implements a plain, unencrypted
/// socket
namespace basic_socket {
typedef lib::function<void(connection_hdl,boost::asio::ip::tcp::socket&)>
/// The signature of the socket init handler for this socket policy
typedef lib::function<void(connection_hdl,lib::asio::ip::tcp::socket&)>
socket_init_handler;
/// Basic Boost ASIO connection socket component
/// Basic Asio connection socket component
/**
* transport::asio::basic_socket::connection implements a connection socket
* component using Boost ASIO ip::tcp::socket.
* component using Asio ip::tcp::socket.
*/
class connection : public lib::enable_shared_from_this<connection> {
public:
@@ -56,12 +62,14 @@ public:
/// Type of a shared pointer to this connection socket component
typedef lib::shared_ptr<type> ptr;
/// Type of a pointer to the ASIO io_service being used
typedef boost::asio::io_service* io_service_ptr;
/// Type of a pointer to the ASIO io_service strand being used
typedef lib::shared_ptr<boost::asio::io_service::strand> strand_ptr;
/// Type of a pointer to the Asio io_service being used
typedef lib::asio::io_service* io_service_ptr;
/// Type of a pointer to the Asio io_service strand being used
typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
/// Type of the ASIO socket being used
typedef lib::asio::ip::tcp::socket socket_type;
/// Type of a shared pointer to the socket being used.
typedef lib::shared_ptr<boost::asio::ip::tcp::socket> socket_ptr;
typedef lib::shared_ptr<socket_type> socket_ptr;
explicit connection() : m_state(UNINITIALIZED) {
//std::cout << "transport::asio::basic_socket::connection constructor"
@@ -85,7 +93,7 @@ public:
/**
* The socket initialization handler is called after the socket object is
* created but before it is used. This gives the application a chance to
* set any ASIO socket options it needs.
* set any Asio socket options it needs.
*
* @param h The new socket_init_handler
*/
@@ -97,7 +105,7 @@ public:
/**
* This is used internally. It can also be used to set socket options, etc
*/
boost::asio::ip::tcp::socket& get_socket() {
lib::asio::ip::tcp::socket & get_socket() {
return *m_socket;
}
@@ -105,7 +113,7 @@ public:
/**
* This is used internally.
*/
boost::asio::ip::tcp::socket& get_next_layer() {
lib::asio::ip::tcp::socket & get_next_layer() {
return *m_socket;
}
@@ -113,7 +121,7 @@ public:
/**
* This is used internally. It can also be used to set socket options, etc
*/
boost::asio::ip::tcp::socket& get_raw_socket() {
lib::asio::ip::tcp::socket & get_raw_socket() {
return *m_socket;
}
@@ -127,16 +135,16 @@ public:
*
* @return A string identifying the address of the remote endpoint
*/
std::string get_remote_endpoint(lib::error_code &ec) const {
std::string get_remote_endpoint(lib::error_code & ec) const {
std::stringstream s;
boost::system::error_code bec;
boost::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(bec);
lib::asio::error_code aec;
lib::asio::ip::tcp::endpoint ep = m_socket->remote_endpoint(aec);
if (bec) {
if (aec) {
ec = error::make_error_code(error::pass_through);
s << "Error getting remote endpoint: " << bec
<< " (" << bec.message() << ")";
s << "Error getting remote endpoint: " << aec
<< " (" << aec.message() << ")";
return s.str();
} else {
ec = lib::error_code();
@@ -148,26 +156,39 @@ protected:
/// Perform one time initializations
/**
* init_asio is called once immediately after construction to initialize
* boost::asio components to the io_service
* Asio components to the io_service
*
* @param service A pointer to the endpoint's io_service
* @param strand A shared pointer to the connection's asio strand
* @param is_server Whether or not the endpoint is a server or not.
*/
lib::error_code init_asio (io_service_ptr service, strand_ptr strand,
bool is_server)
lib::error_code init_asio (io_service_ptr service, strand_ptr, bool)
{
if (m_state != UNINITIALIZED) {
return socket::make_error_code(socket::error::invalid_state);
}
m_socket.reset(new boost::asio::ip::tcp::socket(*service));
m_socket = lib::make_shared<lib::asio::ip::tcp::socket>(
lib::ref(*service));
m_state = READY;
return lib::error_code();
}
/// Set uri hook
/**
* Called by the transport as a connection is being established to provide
* the uri being connected to to the security/socket layer.
*
* This socket policy doesn't use the uri so it is ignored.
*
* @since 0.6.0
*
* @param u The uri to set
*/
void set_uri(uri_ptr) {}
/// Pre-initialize security policy
/**
* Called by the transport after a new connection is created to initialize
@@ -216,19 +237,62 @@ protected:
}
/// Cancel all async operations on this socket
void cancel_socket() {
m_socket->cancel();
/**
* Attempts to cancel all async operations on this socket and reports any
* failures.
*
* NOTE: Windows XP and earlier do not support socket cancellation.
*
* @return The error that occurred, if any.
*/
lib::asio::error_code cancel_socket() {
lib::asio::error_code ec;
m_socket->cancel(ec);
return ec;
}
void async_shutdown(socket_shutdown_handler h) {
boost::system::error_code ec;
m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both,ec);
void async_shutdown(socket::shutdown_handler h) {
lib::asio::error_code ec;
m_socket->shutdown(lib::asio::ip::tcp::socket::shutdown_both, ec);
h(ec);
}
lib::error_code get_ec() const {
return lib::error_code();
}
/// Translate any security policy specific information about an error code
/**
* Translate_ec takes an Asio error code and attempts to convert its value
* to an appropriate websocketpp error code. In the case that the Asio and
* Websocketpp error types are the same (such as using boost::asio and
* boost::system_error or using standalone asio and std::system_error the
* code will be passed through natively.
*
* In the case of a mismatch (boost::asio with std::system_error) a
* translated code will be returned. The plain socket policy does not have
* any additional information so all such errors will be reported as the
* generic transport pass_through error.
*
* @since 0.3.0
*
* @param ec The error code to translate_ec
* @return The translated error code
*/
template <typename ErrorCodeType>
lib::error_code translate_ec(ErrorCodeType) {
// We don't know any more information about this error so pass through
return make_error_code(transport::error::pass_through);
}
/// Overload of translate_ec to catch cases where lib::error_code is the
/// same type as lib::asio::error_code
lib::error_code translate_ec(lib::error_code ec) {
// We don't know any more information about this error, but the error is
// the same type as the one we are translating to, so pass through
// untranslated.
return ec;
}
private:
enum state {
UNINITIALIZED = 0,
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -29,31 +29,36 @@
#define WEBSOCKETPP_TRANSPORT_SECURITY_TLS_HPP
#include <websocketpp/transport/asio/security/base.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/common/asio_ssl.hpp>
#include <websocketpp/common/asio.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/memory.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <sstream>
#include <string>
namespace websocketpp {
namespace transport {
namespace asio {
/// A socket policy for the asio transport that implements a TLS encrypted
/// socket by wrapping with an asio::ssl::stream
namespace tls_socket {
typedef lib::function<void(connection_hdl,boost::asio::ssl::stream<
boost::asio::ip::tcp::socket>&)> socket_init_handler;
typedef lib::function<lib::shared_ptr<boost::asio::ssl::context>(connection_hdl)>
/// The signature of the socket_init_handler for this socket policy
typedef lib::function<void(connection_hdl,lib::asio::ssl::stream<
lib::asio::ip::tcp::socket>&)> socket_init_handler;
/// The signature of the tls_init_handler for this socket policy
typedef lib::function<lib::shared_ptr<lib::asio::ssl::context>(connection_hdl)>
tls_init_handler;
/// TLS enabled Boost ASIO connection socket component
/// TLS enabled Asio connection socket component
/**
* transport::asio::tls_socket::connection implements a secure connection socket
* component that uses Boost ASIO's ssl::stream to wrap an ip::tcp::socket.
* component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
*/
class connection : public lib::enable_shared_from_this<connection> {
public:
@@ -63,17 +68,15 @@ public:
typedef lib::shared_ptr<type> ptr;
/// Type of the ASIO socket being used
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_type;
typedef lib::asio::ssl::stream<lib::asio::ip::tcp::socket> socket_type;
/// Type of a shared pointer to the ASIO socket being used
typedef lib::shared_ptr<socket_type> socket_ptr;
/// Type of a pointer to the ASIO io_service being used
typedef boost::asio::io_service* io_service_ptr;
typedef lib::asio::io_service * io_service_ptr;
/// Type of a pointer to the ASIO io_service strand being used
typedef lib::shared_ptr<boost::asio::io_service::strand> strand_ptr;
typedef lib::shared_ptr<lib::asio::io_service::strand> strand_ptr;
/// Type of a shared pointer to the ASIO TLS context being used
typedef lib::shared_ptr<boost::asio::ssl::context> context_ptr;
typedef boost::system::error_code boost_error;
typedef lib::shared_ptr<lib::asio::ssl::context> context_ptr;
explicit connection() {
//std::cout << "transport::asio::tls_socket::connection constructor"
@@ -97,7 +100,7 @@ public:
/**
* This is used internally. It can also be used to set socket options, etc
*/
socket_type::lowest_layer_type& get_raw_socket() {
socket_type::lowest_layer_type & get_raw_socket() {
return m_socket->lowest_layer();
}
@@ -105,7 +108,7 @@ public:
/**
* This is used internally.
*/
socket_type::next_layer_type& get_next_layer() {
socket_type::next_layer_type & get_next_layer() {
return m_socket->next_layer();
}
@@ -113,7 +116,7 @@ public:
/**
* This is used internally.
*/
socket_type& get_socket() {
socket_type & get_socket() {
return *m_socket;
}
@@ -152,16 +155,16 @@ public:
*
* @return A string identifying the address of the remote endpoint
*/
std::string get_remote_endpoint(lib::error_code &ec) const {
std::string get_remote_endpoint(lib::error_code & ec) const {
std::stringstream s;
boost::system::error_code bec;
boost::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(bec);
lib::asio::error_code aec;
lib::asio::ip::tcp::endpoint ep = m_socket->lowest_layer().remote_endpoint(aec);
if (bec) {
if (aec) {
ec = error::make_error_code(error::pass_through);
s << "Error getting remote endpoint: " << bec
<< " (" << bec.message() << ")";
s << "Error getting remote endpoint: " << aec
<< " (" << aec.message() << ")";
return s.str();
} else {
ec = lib::error_code();
@@ -173,7 +176,7 @@ protected:
/// Perform one time initializations
/**
* init_asio is called once immediately after construction to initialize
* boost::asio components to the io_service
* Asio components to the io_service
*
* @param service A pointer to the endpoint's io_service
* @param strand A pointer to the connection's strand
@@ -190,7 +193,8 @@ protected:
if (!m_context) {
return socket::make_error_code(socket::error::invalid_tls_context);
}
m_socket.reset(new socket_type(*service,*m_context));
m_socket = lib::make_shared<socket_type>(
_WEBSOCKETPP_REF(*service),lib::ref(*m_context));
m_io_service = service;
m_strand = strand;
@@ -199,6 +203,22 @@ protected:
return lib::error_code();
}
/// Set hostname hook
/**
* Called by the transport as a connection is being established to provide
* the hostname being connected to to the security/socket layer.
*
* This socket policy uses the hostname to set the appropriate TLS SNI
* header.
*
* @since 0.6.0
*
* @param u The uri to set
*/
void set_uri(uri_ptr u) {
m_uri = u;
}
/// Pre-initialize security policy
/**
* Called by the transport after a new connection is created to initialize
@@ -209,6 +229,22 @@ protected:
* @param callback Handler to call back with completion information
*/
void pre_init(init_handler callback) {
// TODO: is this the best way to check whether this function is
// available in the version of OpenSSL being used?
// TODO: consider case where host is an IP address
#if OPENSSL_VERSION_NUMBER >= 0x90812f
if (!m_is_server) {
// For clients on systems with a suitable OpenSSL version, set the
// TLS SNI hostname header so connecting to TLS servers using SNI
// will work.
long res = SSL_set_tlsext_host_name(
get_socket().native_handle(), m_uri->get_host().c_str());
if (!(1 == res)) {
callback(socket::make_error_code(socket::error::tls_failed_sni_hostname));
}
}
#endif
if (m_socket_init_handler) {
m_socket_init_handler(m_hdl,get_socket());
}
@@ -260,11 +296,9 @@ protected:
m_hdl = hdl;
}
void handle_init(init_handler callback, const
boost::system::error_code& ec)
{
void handle_init(init_handler callback,lib::asio::error_code const & ec) {
if (ec) {
m_ec = socket::make_error_code(socket::error::pass_through);
m_ec = socket::make_error_code(socket::error::tls_handshake_failed);
} else {
m_ec = lib::error_code();
}
@@ -277,19 +311,83 @@ protected:
}
/// Cancel all async operations on this socket
void cancel_socket() {
get_raw_socket().cancel();
/**
* Attempts to cancel all async operations on this socket and reports any
* failures.
*
* NOTE: Windows XP and earlier do not support socket cancellation.
*
* @return The error that occurred, if any.
*/
lib::asio::error_code cancel_socket() {
lib::asio::error_code ec;
get_raw_socket().cancel(ec);
return ec;
}
void async_shutdown(socket_shutdown_handler callback) {
m_socket->async_shutdown(callback);
void async_shutdown(socket::shutdown_handler callback) {
if (m_strand) {
m_socket->async_shutdown(m_strand->wrap(callback));
} else {
m_socket->async_shutdown(callback);
}
}
/// Translate any security policy specific information about an error code
/**
* Translate_ec takes an Asio error code and attempts to convert its value
* to an appropriate websocketpp error code. In the case that the Asio and
* Websocketpp error types are the same (such as using boost::asio and
* boost::system_error or using standalone asio and std::system_error the
* code will be passed through natively.
*
* In the case of a mismatch (boost::asio with std::system_error) a
* translated code will be returned. Any error that is determined to be
* related to TLS but does not have a more specific websocketpp error code
* is returned under the catch all error `tls_error`. Non-TLS related errors
* are returned as the transport generic error `pass_through`
*
* @since 0.3.0
*
* @param ec The error code to translate_ec
* @return The translated error code
*/
template <typename ErrorCodeType>
lib::error_code translate_ec(ErrorCodeType ec) {
if (ec.category() == lib::asio::error::get_ssl_category()) {
if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
return make_error_code(transport::error::tls_short_read);
} else {
// We know it is a TLS related error, but otherwise don't know
// more. Pass through as TLS generic.
return make_error_code(transport::error::tls_error);
}
} else {
// We don't know any more information about this error so pass
// through
return make_error_code(transport::error::pass_through);
}
}
/// Overload of translate_ec to catch cases where lib::error_code is the
/// same type as lib::asio::error_code
lib::error_code translate_ec(lib::error_code ec) {
// Normalize the tls_short_read error as it is used by the library and
// needs a consistent value. All other errors pass through natively.
// TODO: how to get the SSL category from std::error?
/*if (ec.category() == lib::asio::error::get_ssl_category()) {
if (ERR_GET_REASON(ec.value()) == SSL_R_SHORT_READ) {
return make_error_code(transport::error::tls_short_read);
}
}*/
return ec;
}
private:
socket_type::handshake_type get_handshake_type() {
if (m_is_server) {
return boost::asio::ssl::stream_base::server;
return lib::asio::ssl::stream_base::server;
} else {
return boost::asio::ssl::stream_base::client;
return lib::asio::ssl::stream_base::client;
}
}
@@ -297,6 +395,7 @@ private:
strand_ptr m_strand;
context_ptr m_context;
socket_ptr m_socket;
uri_ptr m_uri;
bool m_is_server;
lib::error_code m_ec;
@@ -306,10 +405,10 @@ private:
tls_init_handler m_tls_init_handler;
};
/// TLS enabled Boost ASIO endpoint socket component
/// TLS enabled Asio endpoint socket component
/**
* transport::asio::tls_socket::endpoint implements a secure endpoint socket
* component that uses Boost ASIO's ssl::stream to wrap an ip::tcp::socket.
* component that uses Asio's ssl::stream to wrap an ip::tcp::socket.
*/
class endpoint {
public:
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -31,9 +31,10 @@
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/system_error.hpp>
#include <string>
namespace websocketpp {
/// Transport policies provide network connectivity and timers
/**
@@ -141,6 +142,7 @@ struct buffer {
size_t len;
};
/// Generic transport related errors
namespace error {
enum value {
/// Catch-all error for transport policy errors that don't fit in other
@@ -170,9 +172,12 @@ enum value {
/// Timer expired
timeout,
/// read or write after shutdown
action_after_shutdown
action_after_shutdown,
/// Other TLS error
tls_error
};
class category : public lib::error_category {
@@ -197,12 +202,14 @@ class category : public lib::error_category {
return "The operation is not supported by this transport";
case eof:
return "End of File";
case tls_short_read:
case tls_short_read:
return "TLS Short Read";
case timeout:
return "Timer Expired";
case action_after_shutdown:
return "A transport action was requested after shutdown";
case tls_error:
return "Generic TLS related error";
default:
return "Unknown";
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,13 +28,8 @@
#ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP
#define WEBSOCKETPP_TRANSPORT_BASE_HPP
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/uri.hpp>
#include <iostream>
namespace websocketpp {
/// Transport policies provide network connectivity and timers
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -30,14 +30,36 @@
#include <websocketpp/common/system_error.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <string>
#include <vector>
namespace websocketpp {
namespace transport {
/// Transport policy that uses STL iostream for I/O and does not support timers
namespace iostream {
/// The type and signature of the callback used by iostream transport to write
typedef lib::function<lib::error_code(connection_hdl, char const *, size_t)>
write_handler;
/// The type and signature of the callback used by iostream transport to perform
/// vectored writes.
/**
* If a vectored write handler is not set the standard write handler will be
* called multiple times.
*/
typedef lib::function<lib::error_code(connection_hdl, std::vector<transport::buffer> const
& bufs)> vector_write_handler;
/// The type and signature of the callback used by iostream transport to signal
/// a transport shutdown.
typedef lib::function<lib::error_code(connection_hdl)> shutdown_handler;
/// iostream transport errors
namespace error {
enum value {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,14 +28,22 @@
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/transport/iostream/base.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/memory.hpp>
#include <websocketpp/common/platforms.hpp>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace websocketpp {
@@ -99,6 +107,19 @@ public:
m_output_stream = o;
}
/// Set uri hook
/**
* Called by the endpoint as a connection is being established to provide
* the uri being connected to to the transport layer.
*
* This transport policy doesn't use the uri so it is ignored.
*
* @since 0.6.0
*
* @param u The uri to set
*/
void set_uri(uri_ptr) {}
/// Overloaded stream input operator
/**
* Attempts to read input from the given stream into the transport. Bytes
@@ -127,14 +148,13 @@ public:
return in;
}
/// Manual input supply
/// Manual input supply (read some)
/**
* Copies bytes from buf into WebSocket++'s input buffers. Bytes will be
* copied from the supplied buffer to fulfill any pending library reads. It
* will return the number of bytes successfully processed. If there are no
* pending reads read_some will return immediately. Not all of the bytes may
* be able to be read in one call
*
* be able to be read in one call.
*
* @since 0.3.0-alpha4
*
@@ -149,6 +169,37 @@ public:
return this->read_some_impl(buf,len);
}
/// Manual input supply (read all)
/**
* Similar to read_some, but continues to read until all bytes in the
* supplied buffer have been read or the connection runs out of read
* requests.
*
* This method still may not read all of the bytes in the input buffer. if
* it doesn't it indicates that the connection was most likely closed or
* is in an error state where it is no longer accepting new input.
*
* @since 0.3.0
*
* @param buf Char buffer to read into the websocket
* @param len Length of buf
* @return The number of characters from buf actually read.
*/
size_t read_all(char const * buf, size_t len) {
// this serializes calls to external read.
scoped_lock_type lock(m_read_mutex);
size_t total_read = 0;
size_t temp_read = 0;
do {
temp_read = this->read_some_impl(buf+total_read,len-total_read);
total_read += temp_read;
} while (temp_read != 0 && total_read < len);
return total_read;
}
/// Manual input supply (DEPRECATED)
/**
* @deprecated DEPRECATED in favor of read_some()
@@ -273,9 +324,82 @@ public:
* @return A handle that can be used to cancel the timer if it is no longer
* needed.
*/
timer_ptr set_timer(long duration, timer_handler handler) {
timer_ptr set_timer(long, timer_handler) {
return timer_ptr();
}
/// Sets the write handler
/**
* The write handler is called when the iostream transport receives data
* that needs to be written to the appropriate output location. This handler
* can be used in place of registering an ostream for output.
*
* The signature of the handler is
* `lib::error_code (connection_hdl, char const *, size_t)` The
* code returned will be reported and logged by the core library.
*
* See also, set_vector_write_handler, for an optional write handler that
* allows more efficient handling of multiple writes at once.
*
* @see set_vector_write_handler
*
* @since 0.5.0
*
* @param h The handler to call when data is to be written.
*/
void set_write_handler(write_handler h) {
m_write_handler = h;
}
/// Sets the vectored write handler
/**
* The vectored write handler is called when the iostream transport receives
* multiple chunks of data that need to be written to the appropriate output
* location. This handler can be used in conjunction with the write_handler
* in place of registering an ostream for output.
*
* The sequence of buffers represents bytes that should be written
* consecutively and it is suggested to group the buffers into as few next
* layer packets as possible. Vector write is used to allow implementations
* that support it to coalesce writes into a single TCP packet or TLS
* segment for improved efficiency.
*
* This is an optional handler. If it is not defined then multiple calls
* will be made to the standard write handler.
*
* The signature of the handler is
* `lib::error_code (connection_hdl, std::vector<websocketpp::transport::buffer>
* const & bufs)`. The code returned will be reported and logged by the core
* library. The `websocketpp::transport::buffer` type is a struct with two
* data members. buf (char const *) and len (size_t).
*
* @since 0.6.0
*
* @param h The handler to call when vectored data is to be written.
*/
void set_vector_write_handler(vector_write_handler h) {
m_vector_write_handler = h;
}
/// Sets the shutdown handler
/**
* The shutdown handler is called when the iostream transport receives a
* notification from the core library that it is finished with all read and
* write operations and that the underlying transport can be cleaned up.
*
* If you are using iostream transport with another socket library, this is
* a good time to close/shutdown the socket for this connection.
*
* The signature of the handler is `lib::error_code (connection_hdl)`. The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_shutdown_handler(shutdown_handler h) {
m_shutdown_handler = h;
}
protected:
/// Initialize the connection transport
/**
@@ -344,7 +468,7 @@ protected:
/// Asyncronous Transport Write
/**
* Write len bytes in buf to the output stream. Call handler to report
* Write len bytes in buf to the output method. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
@@ -352,31 +476,40 @@ protected:
* output_stream_required: No output stream was registered to write to
* bad_stream: a ostream pass through error
*
* This method will attempt to write to the registered ostream first. If an
* ostream is not registered it will use the write handler. If neither are
* registered then an error is passed up to the connection.
*
* @param buf buffer to read bytes from
* @param len number of bytes to write
* @param handler Callback to invoke with operation status.
*/
void async_write(char const * buf, size_t len, write_handler handler) {
void async_write(char const * buf, size_t len, transport::write_handler
handler)
{
m_alog.write(log::alevel::devel,"iostream_con async_write");
// TODO: lock transport state?
if (!m_output_stream) {
handler(make_error_code(error::output_stream_required));
return;
}
lib::error_code ec;
m_output_stream->write(buf,len);
if (m_output_stream) {
m_output_stream->write(buf,len);
if (m_output_stream->bad()) {
handler(make_error_code(error::bad_stream));
if (m_output_stream->bad()) {
ec = make_error_code(error::bad_stream);
}
} else if (m_write_handler) {
ec = m_write_handler(m_connection_hdl, buf, len);
} else {
handler(lib::error_code());
ec = make_error_code(error::output_stream_required);
}
handler(ec);
}
/// Asyncronous Transport Write (scatter-gather)
/**
* Write a sequence of buffers to the output stream. Call handler to report
* Write a sequence of buffers to the output method. Call handler to report
* success or failure. handler may or may not be called during async_write,
* but it must be safe for this to happen.
*
@@ -384,28 +517,45 @@ protected:
* output_stream_required: No output stream was registered to write to
* bad_stream: a ostream pass through error
*
* This method will attempt to write to the registered ostream first. If an
* ostream is not registered it will use the write handler. If neither are
* registered then an error is passed up to the connection.
*
* @param bufs vector of buffers to write
* @param handler Callback to invoke with operation status.
*/
void async_write(std::vector<buffer> const & bufs, write_handler handler) {
void async_write(std::vector<buffer> const & bufs, transport::write_handler
handler)
{
m_alog.write(log::alevel::devel,"iostream_con async_write buffer list");
// TODO: lock transport state?
if (!m_output_stream) {
handler(make_error_code(error::output_stream_required));
return;
}
lib::error_code ec;
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
m_output_stream->write((*it).buf,(*it).len);
if (m_output_stream) {
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
m_output_stream->write((*it).buf,(*it).len);
if (m_output_stream->bad()) {
handler(make_error_code(error::bad_stream));
if (m_output_stream->bad()) {
ec = make_error_code(error::bad_stream);
break;
}
}
} else if (m_vector_write_handler) {
ec = m_vector_write_handler(m_connection_hdl, bufs);
} else if (m_write_handler) {
std::vector<buffer>::const_iterator it;
for (it = bufs.begin(); it != bufs.end(); it++) {
ec = m_write_handler(m_connection_hdl, (*it).buf, (*it).len);
if (ec) {break;}
}
} else {
ec = make_error_code(error::output_stream_required);
}
handler(lib::error_code());
handler(ec);
}
/// Set Connection Handle
@@ -434,10 +584,20 @@ protected:
/// Perform cleanup on socket shutdown_handler
/**
* @param h The `shutdown_handler` to call back when complete
* If a shutdown handler is set, call it and pass through its return error
* code. Otherwise assume there is nothing to do and pass through a success
* code.
*
* @param handler The `shutdown_handler` to call back when complete
*/
void async_shutdown(shutdown_handler handler) {
handler(lib::error_code());
void async_shutdown(transport::shutdown_handler handler) {
lib::error_code ec;
if (m_shutdown_handler) {
ec = m_shutdown_handler(m_connection_hdl);
}
handler(ec);
}
private:
void read(std::istream &in) {
@@ -479,7 +639,7 @@ private:
return 0;
}
size_t bytes_to_copy = std::min(len,m_len-m_cursor);
size_t bytes_to_copy = (std::min)(len,m_len-m_cursor);
std::copy(buf,buf+bytes_to_copy,m_buf+m_cursor);
@@ -527,6 +687,9 @@ private:
// transport resources
std::ostream * m_output_stream;
connection_hdl m_connection_hdl;
write_handler m_write_handler;
vector_write_handler m_vector_write_handler;
shutdown_handler m_shutdown_handler;
bool m_reading;
bool const m_is_server;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,13 +28,15 @@
#ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
#define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/transport/base/endpoint.hpp>
#include <websocketpp/transport/iostream/connection.hpp>
#include <iostream>
#include <websocketpp/uri.hpp>
#include <websocketpp/logger/levels.hpp>
#include <websocketpp/common/memory.hpp>
#include <ostream>
namespace websocketpp {
namespace transport {
@@ -114,6 +116,44 @@ public:
bool is_secure() const {
return m_is_secure;
}
/// Sets the write handler
/**
* The write handler is called when the iostream transport receives data
* that needs to be written to the appropriate output location. This handler
* can be used in place of registering an ostream for output.
*
* The signature of the handler is
* `lib::error_code (connection_hdl, char const *, size_t)` The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_write_handler(write_handler h) {
m_write_handler = h;
}
/// Sets the shutdown handler
/**
* The shutdown handler is called when the iostream transport receives a
* notification from the core library that it is finished with all read and
* write operations and that the underlying transport can be cleaned up.
*
* If you are using iostream transport with another socket library, this is
* a good time to close/shutdown the socket for this connection.
*
* The signature of the handler is lib::error_code (connection_hdl). The
* code returned will be reported and logged by the core library.
*
* @since 0.5.0
*
* @param h The handler to call on connection shutdown.
*/
void set_shutdown_handler(shutdown_handler h) {
m_shutdown_handler = h;
}
protected:
/// Initialize logging
/**
@@ -140,7 +180,7 @@ protected:
* @param u A URI pointer to the URI to connect to.
* @param cb The function to call back with the results when complete.
*/
void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) {
void async_connect(transport_con_ptr, uri_ptr, connect_handler cb) {
cb(lib::error_code());
}
@@ -156,10 +196,19 @@ protected:
*/
lib::error_code init(transport_con_ptr tcon) {
tcon->register_ostream(m_output_stream);
if (m_shutdown_handler) {
tcon->set_shutdown_handler(m_shutdown_handler);
}
if (m_write_handler) {
tcon->set_write_handler(m_write_handler);
}
return lib::error_code();
}
private:
std::ostream * m_output_stream;
shutdown_handler m_shutdown_handler;
write_handler m_write_handler;
elog_type * m_elog;
alog_type * m_alog;
bool m_is_secure;
+28 -27
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -28,11 +28,11 @@
#ifndef WEBSOCKETPP_URI_HPP
#define WEBSOCKETPP_URI_HPP
#include <websocketpp/common/memory.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/common/memory.hpp>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
@@ -47,27 +47,28 @@ static uint16_t const uri_default_secure_port = 443;
class uri {
public:
explicit uri(std::string const & uri) : m_valid(false) {
explicit uri(std::string const & uri_string) : m_valid(false) {
std::string::const_iterator it;
std::string::const_iterator temp;
int state = 0;
it = uri.begin();
it = uri_string.begin();
size_t uri_len = uri_string.length();
if (std::equal(it,it+6,"wss://")) {
if (uri_len >= 7 && std::equal(it,it+6,"wss://")) {
m_secure = true;
m_scheme = "wss";
it += 6;
} else if (std::equal(it,it+5,"ws://")) {
} else if (uri_len >= 6 && std::equal(it,it+5,"ws://")) {
m_secure = false;
m_scheme = "ws";
it += 5;
} else if (std::equal(it,it+7,"http://")) {
} else if (uri_len >= 8 && std::equal(it,it+7,"http://")) {
m_secure = false;
m_scheme = "http";
it += 7;
} else if (std::equal(it,it+8,"https://")) {
} else if (uri_len >= 9 && std::equal(it,it+8,"https://")) {
m_secure = true;
m_scheme = "https";
it += 8;
@@ -88,14 +89,14 @@ public:
//temp = std::find(it,it2,']');
temp = it;
while (temp != uri.end()) {
while (temp != uri_string.end()) {
if (*temp == ']') {
break;
}
++temp;
}
if (temp == uri.end()) {
if (temp == uri_string.end()) {
return;
} else {
// validate IPv6 literal parts
@@ -103,7 +104,7 @@ public:
m_host.append(it,temp);
}
it = temp+1;
if (it == uri.end()) {
if (it == uri_string.end()) {
state = 2;
} else if (*it == '/') {
state = 2;
@@ -119,7 +120,7 @@ public:
// IPv4 or hostname
// extract until : or /
while (state == 0) {
if (it == uri.end()) {
if (it == uri_string.end()) {
state = 2;
break;
} else if (*it == '/') {
@@ -135,9 +136,9 @@ public:
}
// parse port
std::string port = "";
std::string port;
while (state == 1) {
if (it == uri.end()) {
if (it == uri_string.end()) {
// state is not used after this point presently.
// this should be re-enabled if it ever is needed in a future
// refactoring
@@ -159,7 +160,7 @@ public:
}
m_resource = "/";
m_resource.append(it,uri.end());
m_resource.append(it,uri_string.end());
m_valid = true;
@@ -169,7 +170,7 @@ public:
std::string const & resource)
: m_scheme(secure ? "wss" : "ws")
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_port(port)
, m_secure(secure)
, m_valid(true) {}
@@ -177,7 +178,7 @@ public:
uri(bool secure, std::string const & host, std::string const & resource)
: m_scheme(secure ? "wss" : "ws")
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_port(secure ? uri_default_secure_port : uri_default_port)
, m_secure(secure)
, m_valid(true) {}
@@ -186,7 +187,7 @@ public:
std::string const & resource)
: m_scheme(secure ? "wss" : "ws")
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_secure(secure)
{
lib::error_code ec;
@@ -198,7 +199,7 @@ public:
std::string const & resource)
: m_scheme(scheme)
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_port(port)
, m_secure(scheme == "wss" || scheme == "https")
, m_valid(true) {}
@@ -206,7 +207,7 @@ public:
uri(std::string scheme, std::string const & host, std::string const & resource)
: m_scheme(scheme)
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_port((scheme == "wss" || scheme == "https") ? uri_default_secure_port : uri_default_port)
, m_secure(scheme == "wss" || scheme == "https")
, m_valid(true) {}
@@ -215,7 +216,7 @@ public:
std::string const & port, std::string const & resource)
: m_scheme(scheme)
, m_host(host)
, m_resource(resource == "" ? "/" : resource)
, m_resource(resource.empty() ? "/" : resource)
, m_secure(scheme == "wss" || scheme == "https")
{
lib::error_code ec;
@@ -290,10 +291,10 @@ public:
* @return query portion of the URI.
*/
std::string get_query() const {
std::size_t found = m_resource.find('?');
if (found != std::string::npos) {
return m_resource.substr(found + 1);
} else {
std::size_t found = m_resource.find('?');
if (found != std::string::npos) {
return m_resource.substr(found + 1);
} else {
return "";
}
}
@@ -321,7 +322,7 @@ private:
{
ec = lib::error_code();
if (port == "") {
if (port.empty()) {
return (m_secure ? uri_default_secure_port : uri_default_port);
}
@@ -31,6 +31,8 @@
#include <websocketpp/common/stdint.hpp>
#include <string>
namespace websocketpp {
namespace utf8_validator {
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* 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:
@@ -78,7 +78,7 @@ struct ci_less : std::binary_function<std::string, std::string, bool> {
: public std::binary_function<unsigned char,unsigned char,bool>
{
bool operator() (unsigned char const & c1, unsigned char const & c2) const {
return std::tolower (c1) < std::tolower (c2);
return tolower (c1) < tolower (c2);
}
};
bool operator() (std::string const & s1, std::string const & s2) const {
+5 -4
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, Peter Thorson. All rights reserved.
* Copyright (c) 2015, Peter Thorson. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -42,7 +42,7 @@ namespace websocketpp {
/// Library major version number
static int const major_version = 0;
/// Library minor version number
static int const minor_version = 3;
static int const minor_version = 7;
/// Library patch version number
static int const patch_version = 0;
/// Library pre-release flag
@@ -50,10 +50,11 @@ static int const patch_version = 0;
* This is a textual flag indicating the type and number for pre-release
* versions (dev, alpha, beta, rc). This will be blank for release versions.
*/
static char const prerelease_flag[] = "alpha4";
static char const prerelease_flag[] = "";
/// Default user agent string
static char const user_agent[] = "WebSocket++/0.3.0-alpha4";
static char const user_agent[] = "WebSocket++/0.7.0";
} // namespace websocketpp