From 19f95f6204f7d4823ae213bd6eba7d0f37bfbd93 Mon Sep 17 00:00:00 2001 From: Albert Medela Date: Sun, 5 Feb 2017 00:44:48 +0100 Subject: [PATCH] #319, #320, c++11 mode, mysql errors to server log --- src/dbofficial/serverdbthread.cpp | 11 +- src/gui/qt/aboutpokerth/aboutpokerthimpl.cpp | 2 +- .../gamelobbydialog/gamelobbydialogimpl.cpp | 3 + src/gui/qt/gametable/gametableimpl.cpp | 2 +- src/gui/qt/gametable/myavatarlabel.cpp | 16 +- src/gui/qt/startwindow/startwindowimpl.cpp | 2 +- src/net/common/sessiondata.cpp | 5 + src/net/common/websendbuffer.cpp | 15 +- src/net/serveracceptwebhelper.h | 6 +- src/third_party/websocketpp/COPYING | 2 +- .../websocketpp/websocketpp/base64/base64.hpp | 48 +- .../websocketpp/websocketpp/client.hpp | 2 +- .../websocketpp/websocketpp/close.hpp | 63 +- .../websocketpp/websocketpp/common/chrono.hpp | 21 +- .../websocketpp/common/connection_hdl.hpp | 4 +- .../websocketpp/websocketpp/common/cpp11.hpp | 75 +- .../websocketpp/common/functional.hpp | 43 +- .../websocketpp/websocketpp/common/md5.hpp | 140 +- .../websocketpp/websocketpp/common/memory.hpp | 25 +- .../websocketpp/common/network.hpp | 36 +- .../websocketpp/common/platforms.hpp | 9 +- .../websocketpp/websocketpp/common/random.hpp | 20 +- .../websocketpp/websocketpp/common/regex.hpp | 2 +- .../websocketpp/websocketpp/common/stdint.hpp | 4 +- .../websocketpp/common/system_error.hpp | 22 +- .../websocketpp/websocketpp/common/thread.hpp | 21 +- .../websocketpp/concurrency/basic.hpp | 2 +- .../websocketpp/concurrency/none.hpp | 28 +- .../websocketpp/websocketpp/config/asio.hpp | 2 +- .../websocketpp/config/asio_client.hpp | 2 +- .../websocketpp/config/asio_no_tls.hpp | 2 +- .../websocketpp/config/asio_no_tls_client.hpp | 2 +- .../websocketpp/config/boost_config.hpp | 11 +- .../websocketpp/websocketpp/config/core.hpp | 26 +- .../websocketpp/config/core_client.hpp | 34 +- .../websocketpp/websocketpp/config/debug.hpp | 26 +- .../websocketpp/config/debug_asio.hpp | 2 +- .../websocketpp/config/debug_asio_no_tls.hpp | 2 +- .../websocketpp/websocketpp/connection.hpp | 406 ++++-- .../websocketpp/connection_base.hpp | 2 +- .../websocketpp/websocketpp/endpoint.hpp | 216 ++- .../websocketpp/websocketpp/endpoint_base.hpp | 2 +- .../websocketpp/websocketpp/error.hpp | 70 +- .../websocketpp/error_container.hpp | 71 - .../websocketpp/extensions/extension.hpp | 4 +- .../permessage_deflate/disabled.hpp | 57 +- .../extensions/permessage_deflate/enabled.hpp | 244 ++-- .../websocketpp/websocketpp/frame.hpp | 36 +- .../websocketpp/http/constants.hpp | 16 +- .../websocketpp/http/impl/parser.hpp | 65 +- .../websocketpp/http/impl/request.hpp | 114 +- .../websocketpp/http/impl/response.hpp | 67 +- .../websocketpp/websocketpp/http/parser.hpp | 126 +- .../websocketpp/websocketpp/http/request.hpp | 26 +- .../websocketpp/websocketpp/http/response.hpp | 36 +- .../websocketpp/impl/connection_impl.hpp | 1220 +++++++++++------ .../websocketpp/impl/endpoint_impl.hpp | 103 +- .../websocketpp/impl/utilities_impl.hpp | 13 +- .../websocketpp/websocketpp/logger/basic.hpp | 86 +- .../websocketpp/websocketpp/logger/levels.hpp | 32 +- .../websocketpp/websocketpp/logger/stub.hpp | 78 +- .../websocketpp/message_buffer/alloc.hpp | 10 +- .../websocketpp/message_buffer/message.hpp | 14 +- .../websocketpp/message_buffer/pool.hpp | 10 +- .../websocketpp/processors/base.hpp | 17 +- .../websocketpp/processors/hybi00.hpp | 124 +- .../websocketpp/processors/hybi07.hpp | 24 +- .../websocketpp/processors/hybi08.hpp | 26 +- .../websocketpp/processors/hybi13.hpp | 240 ++-- .../websocketpp/processors/processor.hpp | 110 +- .../websocketpp/websocketpp/random/none.hpp | 3 +- .../websocketpp/random/random_device.hpp | 2 +- .../websocketpp/roles/client_endpoint.hpp | 13 +- .../websocketpp/roles/server_endpoint.hpp | 127 +- .../websocketpp/websocketpp/server.hpp | 2 +- .../websocketpp/websocketpp/sha1/sha1.hpp | 373 ++--- .../websocketpp/transport/asio/base.hpp | 139 +- .../websocketpp/transport/asio/connection.hpp | 565 +++++--- .../websocketpp/transport/asio/endpoint.hpp | 334 +++-- .../transport/asio/security/base.hpp | 27 +- .../transport/asio/security/none.hpp | 126 +- .../transport/asio/security/tls.hpp | 179 ++- .../websocketpp/transport/base/connection.hpp | 17 +- .../websocketpp/transport/base/endpoint.hpp | 7 +- .../websocketpp/transport/iostream/base.hpp | 24 +- .../transport/iostream/connection.hpp | 235 +++- .../transport/iostream/endpoint.hpp | 61 +- .../websocketpp/websocketpp/uri.hpp | 55 +- .../websocketpp/utf8_validator.hpp | 2 + .../websocketpp/websocketpp/utilities.hpp | 4 +- .../websocketpp/websocketpp/version.hpp | 9 +- 91 files changed, 4561 insertions(+), 2146 deletions(-) delete mode 100644 src/third_party/websocketpp/websocketpp/error_container.hpp diff --git a/src/dbofficial/serverdbthread.cpp b/src/dbofficial/serverdbthread.cpp index 28a7e1e3..98f8ce35 100644 --- a/src/dbofficial/serverdbthread.cpp +++ b/src/dbofficial/serverdbthread.cpp @@ -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; } } diff --git a/src/gui/qt/aboutpokerth/aboutpokerthimpl.cpp b/src/gui/qt/aboutpokerth/aboutpokerthimpl.cpp index 6e49cfb8..67f9cfcf 100755 --- a/src/gui/qt/aboutpokerth/aboutpokerthimpl.cpp +++ b/src/gui/qt/aboutpokerth/aboutpokerthimpl.cpp @@ -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 #endif #endif diff --git a/src/gui/qt/gamelobbydialog/gamelobbydialogimpl.cpp b/src/gui/qt/gamelobbydialog/gamelobbydialogimpl.cpp index 02dae737..ad891aa8 100644 --- a/src/gui/qt/gamelobbydialog/gamelobbydialogimpl.cpp +++ b/src/gui/qt/gamelobbydialog/gamelobbydialogimpl.cpp @@ -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 diff --git a/src/gui/qt/gametable/gametableimpl.cpp b/src/gui/qt/gametable/gametableimpl.cpp index 3d31711f..f2bf9ec5 100755 --- a/src/gui/qt/gametable/gametableimpl.cpp +++ b/src/gui/qt/gametable/gametableimpl.cpp @@ -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 #endif #endif diff --git a/src/gui/qt/gametable/myavatarlabel.cpp b/src/gui/qt/gametable/myavatarlabel.cpp index bff9801c..7b2af440 100644 --- a/src/gui/qt/gametable/myavatarlabel.cpp +++ b/src/gui/qt/gametable/myavatarlabel.cpp @@ -156,9 +156,14 @@ void MyAvatarLabel::setPlayerRating(QString playerInfo) std::list result; std::string separator="(!#$%)"; std::list::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(""); } diff --git a/src/gui/qt/startwindow/startwindowimpl.cpp b/src/gui/qt/startwindow/startwindowimpl.cpp index 4d7b3c78..fa931c2f 100644 --- a/src/gui/qt/startwindow/startwindowimpl.cpp +++ b/src/gui/qt/startwindow/startwindowimpl.cpp @@ -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 #endif #endif diff --git a/src/net/common/sessiondata.cpp b/src/net/common/sessiondata.cpp index fd30cc44..78aa6fc6 100644 --- a/src/net/common/sessiondata.cpp +++ b/src/net/common/sessiondata.cpp @@ -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 } } diff --git a/src/net/common/websendbuffer.cpp b/src/net/common/websendbuffer.cpp index 2ba665a5..7e42e1dc 100644 --- a/src/net/common/websendbuffer.cpp +++ b/src/net/common/websendbuffer.cpp @@ -57,9 +57,14 @@ void WebSendBuffer::AsyncSendNextPacket(boost::shared_ptr session) { if (closeAfterSend) { - boost::system::error_code ec; boost::shared_ptr 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 session, boost google::protobuf::uint8 *buf = new google::protobuf::uint8[packetSize]; packet->GetMsg()->SerializeWithCachedSizesToArray(buf); - boost::system::error_code ec; boost::shared_ptr 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(); } diff --git a/src/net/serveracceptwebhelper.h b/src/net/serveracceptwebhelper.h index ea5b926e..513aac50 100644 --- a/src/net/serveracceptwebhelper.h +++ b/src/net/serveracceptwebhelper.h @@ -49,9 +49,11 @@ public: virtual void Close(); protected: - +#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) // c++11 + typedef std::map, std::owner_less > SessionMap; +#else typedef std::map > SessionMap; - +#endif bool validate(websocketpp::connection_hdl hdl); void on_open(websocketpp::connection_hdl hdl); void on_close(websocketpp::connection_hdl hdl); diff --git a/src/third_party/websocketpp/COPYING b/src/third_party/websocketpp/COPYING index 7eeeb8f3..f8cc5ba1 100644 --- a/src/third_party/websocketpp/COPYING +++ b/src/third_party/websocketpp/COPYING @@ -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: diff --git a/src/third_party/websocketpp/websocketpp/base64/base64.hpp b/src/third_party/websocketpp/websocketpp/base64/base64.hpp index 6572c495..ff1561d1 100644 --- a/src/third_party/websocketpp/websocketpp/base64/base64.hpp +++ b/src/third_party/websocketpp/websocketpp/base64/base64.hpp @@ -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(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(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(base64_chars.find(char_array_4[i])); diff --git a/src/third_party/websocketpp/websocketpp/client.hpp b/src/third_party/websocketpp/websocketpp/client.hpp index 45268be3..8782d7e4 100644 --- a/src/third_party/websocketpp/websocketpp/client.hpp +++ b/src/third_party/websocketpp/websocketpp/client.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: diff --git a/src/third_party/websocketpp/websocketpp/close.hpp b/src/third_party/websocketpp/websocketpp/close.hpp index eee44f61..ded77657 100644 --- a/src/third_party/websocketpp/websocketpp/close.hpp +++ b/src/third_party/websocketpp/websocketpp/close.hpp @@ -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) { diff --git a/src/third_party/websocketpp/websocketpp/common/chrono.hpp b/src/third_party/websocketpp/websocketpp/common/chrono.hpp index 731d7703..975ee043 100644 --- a/src/third_party/websocketpp/websocketpp/common/chrono.hpp +++ b/src/third_party/websocketpp/websocketpp/common/chrono.hpp @@ -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 + +// 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 diff --git a/src/third_party/websocketpp/websocketpp/common/connection_hdl.hpp b/src/third_party/websocketpp/websocketpp/common/connection_hdl.hpp index 71f490cf..1044c88e 100644 --- a/src/third_party/websocketpp/websocketpp/common/connection_hdl.hpp +++ b/src/third_party/websocketpp/websocketpp/common/connection_hdl.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: @@ -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. * diff --git a/src/third_party/websocketpp/websocketpp/common/cpp11.hpp b/src/third_party/websocketpp/websocketpp/common/cpp11.hpp index 2a638dce..492a3b8f 100644 --- a/src/third_party/websocketpp/websocketpp/common/cpp11.hpp +++ b/src/third_party/websocketpp/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: @@ -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 diff --git a/src/third_party/websocketpp/websocketpp/common/functional.hpp b/src/third_party/websocketpp/websocketpp/common/functional.hpp index 8c52c08c..d332dd15 100644 --- a/src/third_party/websocketpp/websocketpp/common/functional.hpp +++ b/src/third_party/websocketpp/websocketpp/common/functional.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 + +// 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 #else #include #include + #include #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 + 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 + void clear_function(T & x) { + x.clear(); } #endif diff --git a/src/third_party/websocketpp/websocketpp/common/md5.hpp b/src/third_party/websocketpp/websocketpp/common/md5.hpp index ed48e992..279725f4 100644 --- a/src/third_party/websocketpp/websocketpp/common/md5.hpp +++ b/src/third_party/websocketpp/websocketpp/common/md5.hpp @@ -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 . + added conditionalization for C++ compilation from Martin + Purschke . 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(nbytes)); + int copy = (offset + nbytes > 64 ? 64 - offset : static_cast(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'}; diff --git a/src/third_party/websocketpp/websocketpp/common/memory.hpp b/src/third_party/websocketpp/websocketpp/common/memory.hpp index 65a5b906..581aa559 100644 --- a/src/third_party/websocketpp/websocketpp/common/memory.hpp +++ b/src/third_party/websocketpp/websocketpp/common/memory.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,16 +28,32 @@ #ifndef WEBSOCKETPP_COMMON_MEMORY_HPP #define WEBSOCKETPP_COMMON_MEMORY_HPP -#if defined _WEBSOCKETPP_CPP11_STL_ && !defined _WEBSOCKETPP_NO_CPP11_MEMORY_ +#include + +// 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 #else #include + #include #include #include #include @@ -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 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 unique_ptr_uchar_array; #endif diff --git a/src/third_party/websocketpp/websocketpp/common/network.hpp b/src/third_party/websocketpp/websocketpp/common/network.hpp index 6a01f140..26a4cb9e 100644 --- a/src/third_party/websocketpp/websocketpp/common/network.hpp +++ b/src/third_party/websocketpp/websocketpp/common/network.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: @@ -29,20 +29,22 @@ #define WEBSOCKETPP_COMMON_NETWORK_HPP // For ntohs and htons -#if defined(WIN32) +#if defined(_WIN32) #include #else //#include #include #endif +#include + namespace websocketpp { namespace lib { namespace net { inline bool is_little_endian() { short int val = 0x1; - char *ptr = (char*)&val; + char *ptr = reinterpret_cast(&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 diff --git a/src/third_party/websocketpp/websocketpp/common/platforms.hpp b/src/third_party/websocketpp/websocketpp/common/platforms.hpp index 4e031aa3..87798577 100644 --- a/src/third_party/websocketpp/websocketpp/common/platforms.hpp +++ b/src/third_party/websocketpp/websocketpp/common/platforms.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: @@ -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 diff --git a/src/third_party/websocketpp/websocketpp/common/random.hpp b/src/third_party/websocketpp/websocketpp/common/random.hpp index fe139516..ddf99694 100644 --- a/src/third_party/websocketpp/websocketpp/common/random.hpp +++ b/src/third_party/websocketpp/websocketpp/common/random.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,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 + +// 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 #else diff --git a/src/third_party/websocketpp/websocketpp/common/regex.hpp b/src/third_party/websocketpp/websocketpp/common/regex.hpp index b85cd709..326635de 100644 --- a/src/third_party/websocketpp/websocketpp/common/regex.hpp +++ b/src/third_party/websocketpp/websocketpp/common/regex.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: diff --git a/src/third_party/websocketpp/websocketpp/common/stdint.hpp b/src/third_party/websocketpp/websocketpp/common/stdint.hpp index 233e36f5..ec48ea75 100644 --- a/src/third_party/websocketpp/websocketpp/common/stdint.hpp +++ b/src/third_party/websocketpp/websocketpp/common/stdint.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: @@ -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 using boost::int8_t; diff --git a/src/third_party/websocketpp/websocketpp/common/system_error.hpp b/src/third_party/websocketpp/websocketpp/common/system_error.hpp index fff3dfe9..e5aea254 100644 --- a/src/third_party/websocketpp/websocketpp/common/system_error.hpp +++ b/src/third_party/websocketpp/websocketpp/common/system_error.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,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 + +// 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 #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; diff --git a/src/third_party/websocketpp/websocketpp/common/thread.hpp b/src/third_party/websocketpp/websocketpp/common/thread.hpp index e02a6582..09f6b3c5 100644 --- a/src/third_party/websocketpp/websocketpp/common/thread.hpp +++ b/src/third_party/websocketpp/websocketpp/common/thread.hpp @@ -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 + +// 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 and +#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 diff --git a/src/third_party/websocketpp/websocketpp/concurrency/basic.hpp b/src/third_party/websocketpp/websocketpp/concurrency/basic.hpp index 84e61b36..1943ad77 100644 --- a/src/third_party/websocketpp/websocketpp/concurrency/basic.hpp +++ b/src/third_party/websocketpp/websocketpp/concurrency/basic.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: diff --git a/src/third_party/websocketpp/websocketpp/concurrency/none.hpp b/src/third_party/websocketpp/websocketpp/concurrency/none.hpp index 9d19d64c..da9aa411 100644 --- a/src/third_party/websocketpp/websocketpp/concurrency/none.hpp +++ b/src/third_party/websocketpp/websocketpp/concurrency/none.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: @@ -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; }; diff --git a/src/third_party/websocketpp/websocketpp/config/asio.hpp b/src/third_party/websocketpp/websocketpp/config/asio.hpp index b2070edb..d28d0fb8 100644 --- a/src/third_party/websocketpp/websocketpp/config/asio.hpp +++ b/src/third_party/websocketpp/websocketpp/config/asio.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: diff --git a/src/third_party/websocketpp/websocketpp/config/asio_client.hpp b/src/third_party/websocketpp/websocketpp/config/asio_client.hpp index 26d4c4db..1cb594d0 100644 --- a/src/third_party/websocketpp/websocketpp/config/asio_client.hpp +++ b/src/third_party/websocketpp/websocketpp/config/asio_client.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: diff --git a/src/third_party/websocketpp/websocketpp/config/asio_no_tls.hpp b/src/third_party/websocketpp/websocketpp/config/asio_no_tls.hpp index a40e99a8..6c1357fb 100644 --- a/src/third_party/websocketpp/websocketpp/config/asio_no_tls.hpp +++ b/src/third_party/websocketpp/websocketpp/config/asio_no_tls.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: diff --git a/src/third_party/websocketpp/websocketpp/config/asio_no_tls_client.hpp b/src/third_party/websocketpp/websocketpp/config/asio_no_tls_client.hpp index 8b9635f8..6e3f7ba0 100644 --- a/src/third_party/websocketpp/websocketpp/config/asio_no_tls_client.hpp +++ b/src/third_party/websocketpp/websocketpp/config/asio_no_tls_client.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: diff --git a/src/third_party/websocketpp/websocketpp/config/boost_config.hpp b/src/third_party/websocketpp/websocketpp/config/boost_config.hpp index 19c89053..57671ccd 100644 --- a/src/third_party/websocketpp/websocketpp/config/boost_config.hpp +++ b/src/third_party/websocketpp/websocketpp/config/boost_config.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: @@ -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 diff --git a/src/third_party/websocketpp/websocketpp/config/core.hpp b/src/third_party/websocketpp/websocketpp/config/core.hpp index e400da50..a95b4021 100644 --- a/src/third_party/websocketpp/websocketpp/config/core.hpp +++ b/src/third_party/websocketpp/websocketpp/config/core.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: @@ -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; diff --git a/src/third_party/websocketpp/websocketpp/config/core_client.hpp b/src/third_party/websocketpp/websocketpp/config/core_client.hpp index 328bcc86..dadf8a4e 100644 --- a/src/third_party/websocketpp/websocketpp/config/core_client.hpp +++ b/src/third_party/websocketpp/websocketpp/config/core_client.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: @@ -34,7 +34,11 @@ #include // Concurrency +#ifndef _WEBSOCKETPP_NO_THREADING_ #include +#else +#include +#endif // Transport #include @@ -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; diff --git a/src/third_party/websocketpp/websocketpp/config/debug.hpp b/src/third_party/websocketpp/websocketpp/config/debug.hpp index 691229a6..223f72fb 100644 --- a/src/third_party/websocketpp/websocketpp/config/debug.hpp +++ b/src/third_party/websocketpp/websocketpp/config/debug.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: @@ -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; diff --git a/src/third_party/websocketpp/websocketpp/config/debug_asio.hpp b/src/third_party/websocketpp/websocketpp/config/debug_asio.hpp index 66a65fdd..a57c736c 100644 --- a/src/third_party/websocketpp/websocketpp/config/debug_asio.hpp +++ b/src/third_party/websocketpp/websocketpp/config/debug_asio.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: diff --git a/src/third_party/websocketpp/websocketpp/config/debug_asio_no_tls.hpp b/src/third_party/websocketpp/websocketpp/config/debug_asio_no_tls.hpp index 7437650a..b3dc83b4 100644 --- a/src/third_party/websocketpp/websocketpp/config/debug_asio_no_tls.hpp +++ b/src/third_party/websocketpp/websocketpp/config/debug_asio_no_tls.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: diff --git a/src/third_party/websocketpp/websocketpp/connection.hpp b/src/third_party/websocketpp/websocketpp/connection.hpp index c8be3633..3bbbbb31 100644 --- a/src/third_party/websocketpp/websocketpp/connection.hpp +++ b/src/third_party/websocketpp/websocketpp/connection.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: @@ -29,19 +29,20 @@ #define WEBSOCKETPP_CONNECTION_HPP #include -#include -#include -#include #include #include -#include + #include #include #include +#include + +#include +#include +#include -#include -#include #include +#include #include #include @@ -118,7 +119,7 @@ typedef lib::function 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& get_supported_versions() const; + std::vector 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 + 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 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 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; diff --git a/src/third_party/websocketpp/websocketpp/connection_base.hpp b/src/third_party/websocketpp/websocketpp/connection_base.hpp index f780da7b..2e700962 100644 --- a/src/third_party/websocketpp/websocketpp/connection_base.hpp +++ b/src/third_party/websocketpp/websocketpp/connection_base.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: diff --git a/src/third_party/websocketpp/websocketpp/endpoint.hpp b/src/third_party/websocketpp/websocketpp/endpoint.hpp index 2df806a3..65584d8a 100644 --- a/src/third_party/websocketpp/websocketpp/endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/endpoint.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: @@ -29,11 +29,11 @@ #define WEBSOCKETPP_ENDPOINT_HPP #include + #include #include -#include -#include +#include namespace websocketpp { @@ -85,25 +85,78 @@ public: // TODO: organize these typedef typename connection_type::termination_handler termination_handler; - typedef lib::shared_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() {} + + #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( 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; diff --git a/src/third_party/websocketpp/websocketpp/endpoint_base.hpp b/src/third_party/websocketpp/websocketpp/endpoint_base.hpp index c6f26285..1ad1a44d 100644 --- a/src/third_party/websocketpp/websocketpp/endpoint_base.hpp +++ b/src/third_party/websocketpp/websocketpp/endpoint_base.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: diff --git a/src/third_party/websocketpp/websocketpp/error.hpp b/src/third_party/websocketpp/websocketpp/error.hpp index d699075d..562fb6ed 100644 --- a/src/third_party/websocketpp/websocketpp/error.hpp +++ b/src/third_party/websocketpp/websocketpp/error.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,7 +28,9 @@ #ifndef WEBSOCKETPP_ERROR_HPP #define WEBSOCKETPP_ERROR_HPP +#include #include +#include #include #include @@ -38,7 +40,7 @@ namespace websocketpp { /// Combination error code / string type for returning two values typedef std::pair 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 diff --git a/src/third_party/websocketpp/websocketpp/error_container.hpp b/src/third_party/websocketpp/websocketpp/error_container.hpp deleted file mode 100644 index b7c75ac9..00000000 --- a/src/third_party/websocketpp/websocketpp/error_container.hpp +++ /dev/null @@ -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 - void set_msg(const T& thing) { - std::stringsteam val; - val << thing; - this->set_msg(val.str()); - } - - template - 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 diff --git a/src/third_party/websocketpp/websocketpp/extensions/extension.hpp b/src/third_party/websocketpp/websocketpp/extensions/extension.hpp index 104ea704..f5fbd9f7 100644 --- a/src/third_party/websocketpp/websocketpp/extensions/extension.hpp +++ b/src/third_party/websocketpp/websocketpp/extensions/extension.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; } diff --git a/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp b/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp index 3a5da7fd..49c0e1dc 100644 --- a/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/disabled.hpp +++ b/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/disabled.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: @@ -53,10 +53,29 @@ class disabled { typedef std::pair 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); } }; diff --git a/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp b/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp index 092690c2..1581f14c 100644 --- a/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp +++ b/src/third_party/websocketpp/websocketpp/extensions/permessage_deflate/enabled.hpp @@ -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 #include +#include #include #include @@ -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(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(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; diff --git a/src/third_party/websocketpp/websocketpp/frame.hpp b/src/third_party/websocketpp/websocketpp/frame.hpp index 32b50a64..8a173375 100644 --- a/src/third_party/websocketpp/websocketpp/frame.hpp +++ b/src/third_party/websocketpp/websocketpp/frame.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: @@ -29,6 +29,7 @@ #define WEBSOCKETPP_FRAME_HPP #include +#include #include #include @@ -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(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 diff --git a/src/third_party/websocketpp/websocketpp/http/constants.hpp b/src/third_party/websocketpp/websocketpp/http/constants.hpp index be3006aa..f946cb31 100644 --- a/src/third_party/websocketpp/websocketpp/http/constants.hpp +++ b/src/third_party/websocketpp/websocketpp/http/constants.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,11 +28,14 @@ #ifndef HTTP_CONSTANTS_HPP #define HTTP_CONSTANTS_HPP +#include #include #include #include +#include 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) diff --git a/src/third_party/websocketpp/websocketpp/http/impl/parser.hpp b/src/third_party/websocketpp/websocketpp/http/impl/parser.hpp index d1d90813..1d59b938 100644 --- a/src/third_party/websocketpp/websocketpp/http/impl/parser.hpp +++ b/src/third_party/websocketpp/websocketpp/http/impl/parser.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: @@ -29,6 +29,8 @@ #define HTTP_PARSER_IMPL_HPP #include +#include +#include #include #include @@ -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, diff --git a/src/third_party/websocketpp/websocketpp/http/impl/request.hpp b/src/third_party/websocketpp/websocketpp/http/impl/request.hpp index 804a7faf..311a620f 100644 --- a/src/third_party/websocketpp/websocketpp/http/impl/request.hpp +++ b/src/third_party/websocketpp/websocketpp/http/impl/request.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: @@ -30,6 +30,7 @@ #include #include +#include #include @@ -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(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(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; } diff --git a/src/third_party/websocketpp/websocketpp/http/impl/response.hpp b/src/third_party/websocketpp/websocketpp/http/impl/response.hpp index bcddd176..4400cda5 100644 --- a/src/third_party/websocketpp/websocketpp/http/impl/response.hpp +++ b/src/third_party/websocketpp/websocketpp/http/impl/response.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: @@ -29,7 +29,9 @@ #define HTTP_PARSER_RESPONSE_IMPL_HPP #include +#include #include +#include #include @@ -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(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); diff --git a/src/third_party/websocketpp/websocketpp/http/parser.hpp b/src/third_party/websocketpp/websocketpp/http/parser.hpp index 771c5397..90f49ebe 100644 --- a/src/third_party/websocketpp/websocketpp/http/parser.hpp +++ b/src/third_party/websocketpp/websocketpp/http/parser.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: @@ -29,8 +29,9 @@ #define HTTP_PARSER_HPP #include -#include #include +#include +#include #include #include @@ -48,6 +49,14 @@ namespace state { }; } +namespace body_encoding { + enum value { + unknown, + plain, + chunked + }; +} + typedef std::map 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 diff --git a/src/third_party/websocketpp/websocketpp/http/request.hpp b/src/third_party/websocketpp/websocketpp/http/request.hpp index 1b8c3e2a..3355c99b 100644 --- a/src/third_party/websocketpp/websocketpp/http/request.hpp +++ b/src/third_party/websocketpp/websocketpp/http/request.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,6 +28,8 @@ #ifndef HTTP_PARSER_REQUEST_HPP #define HTTP_PARSER_REQUEST_HPP +#include + #include #include @@ -51,12 +53,9 @@ public: typedef lib::shared_ptr ptr; request() - : m_buf(new std::string()) + : m_buf(lib::make_shared()) , 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; } diff --git a/src/third_party/websocketpp/websocketpp/http/response.hpp b/src/third_party/websocketpp/websocketpp/http/response.hpp index 1ee622b1..e724a3d3 100644 --- a/src/third_party/websocketpp/websocketpp/http/response.hpp +++ b/src/third_party/websocketpp/websocketpp/http/response.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,6 +28,9 @@ #ifndef HTTP_PARSER_RESPONSE_HPP #define HTTP_PARSER_RESPONSE_HPP +#include +#include + #include namespace websocketpp { @@ -58,7 +61,7 @@ public: response() : m_read(0) - , m_buf(new std::string()) + , m_buf(lib::make_shared()) , 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, diff --git a/src/third_party/websocketpp/websocketpp/impl/connection_impl.hpp b/src/third_party/websocketpp/websocketpp/impl/connection_impl.hpp index 0449e746..d1f8dff2 100644 --- a/src/third_party/websocketpp/websocketpp/impl/connection_impl.hpp +++ b/src/third_party/websocketpp/websocketpp/impl/connection_impl.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,15 +28,23 @@ #ifndef WEBSOCKETPP_CONNECTION_IMPL_HPP #define WEBSOCKETPP_CONNECTION_IMPL_HPP -#include - -#include - #include #include #include #include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + namespace websocketpp { namespace istate = session::internal_state; @@ -54,7 +62,7 @@ void connection::set_termination_handler( } template -const std::string& connection::get_origin() const { +std::string const & connection::get_origin() const { //scoped_lock_type lock(m_connection_state_lock); return m_processor->get_origin(m_request); } @@ -72,17 +80,18 @@ session::state::value connection::get_state() const { } template -lib::error_code connection::send(const std::string& payload, +lib::error_code connection::send(std::string const & payload, frame::opcode::value op) { message_ptr msg = m_msg_manager->get_message(op,payload.size()); msg->append_payload(payload); + msg->set_compressed(true); return send(msg); } template -lib::error_code connection::send(const void* payload, size_t len, +lib::error_code connection::send(void const * payload, size_t len, frame::opcode::value op) { message_ptr msg = m_msg_manager->get_message(op,len); @@ -94,11 +103,15 @@ lib::error_code connection::send(const void* payload, size_t len, template lib::error_code connection::send(typename config::message_type::ptr msg) { - m_alog.write(log::alevel::devel,"connection send"); - // TODO: + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection send"); + } - if (m_state != session::state::open) { - return error::make_error_code(error::invalid_state); + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + return error::make_error_code(error::invalid_state); + } } message_ptr outgoing_msg; @@ -139,12 +152,20 @@ lib::error_code connection::send(typename config::message_type::ptr msg) } template -void connection::ping(const std::string& payload, lib::error_code& ec) { - m_alog.write(log::alevel::devel,"connection ping"); +void connection::ping(std::string const& payload, lib::error_code& ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection ping"); + } - if (m_state != session::state::open) { - ec = error::make_error_code(error::invalid_state); - return; + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::ping called from invalid state " << m_state; + m_alog.write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } } message_ptr msg = m_msg_manager->get_message(); @@ -200,17 +221,17 @@ void connection::ping(const std::string& payload, lib::error_code& ec) { } template -void connection::ping(const std::string& payload) { +void connection::ping(std::string const & payload) { lib::error_code ec; ping(payload,ec); if (ec) { - throw ec; + throw exception(ec); } } template -void connection::handle_pong_timeout(std::string payload, const lib::error_code & - ec) +void connection::handle_pong_timeout(std::string payload, + lib::error_code const & ec) { if (ec) { if (ec == transport::error::operation_aborted) { @@ -228,12 +249,20 @@ void connection::handle_pong_timeout(std::string payload, const lib::err } template -void connection::pong(const std::string& payload, lib::error_code& ec) { - m_alog.write(log::alevel::devel,"connection pong"); +void connection::pong(std::string const& payload, lib::error_code& ec) { + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection pong"); + } - if (m_state != session::state::open) { - ec = error::make_error_code(error::invalid_state); - return; + { + scoped_lock_type lock(m_connection_state_lock); + if (m_state != session::state::open) { + std::stringstream ss; + ss << "connection::pong called from invalid state " << m_state; + m_alog.write(log::alevel::devel,ss.str()); + ec = error::make_error_code(error::invalid_state); + return; + } } message_ptr msg = m_msg_manager->get_message(); @@ -263,11 +292,11 @@ void connection::pong(const std::string& payload, lib::error_code& ec) { } template -void connection::pong(const std::string& payload) { +void connection::pong(std::string const & payload) { lib::error_code ec; pong(payload,ec); if (ec) { - throw ec; + throw exception(ec); } } @@ -275,17 +304,21 @@ template void connection::close(close::status::value const code, std::string const & reason, lib::error_code & ec) { - m_alog.write(log::alevel::devel,"connection close"); - - if (m_state != session::state::open) { - ec = error::make_error_code(error::invalid_state); - return; + if (m_alog.static_test(log::alevel::devel)) { + m_alog.write(log::alevel::devel,"connection close"); } // Truncate reason to maximum size allowable in a close frame. std::string tr(reason,0,std::min(reason.size(), frame::limits::close_reason_size)); + scoped_lock_type lock(m_connection_state_lock); + + if (m_state != session::state::open) { + ec = error::make_error_code(error::invalid_state); + return; + } + ec = this->send_close_frame(code,tr,false,close::status::terminal(code)); } @@ -296,7 +329,7 @@ void connection::close(close::status::value const code, lib::error_code ec; close(code,reason,ec); if (ec) { - throw ec; + throw exception(ec); } } @@ -323,6 +356,41 @@ void connection::handle_interrupt() { } } +template +lib::error_code connection::pause_reading() { + m_alog.write(log::alevel::devel,"connection connection::pause_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_pause_reading, + type::get_shared() + ) + ); +} + +/// Pause reading handler. Not safe to call directly +template +void connection::handle_pause_reading() { + m_alog.write(log::alevel::devel,"connection connection::handle_pause_reading"); + m_read_flag = false; +} + +template +lib::error_code connection::resume_reading() { + m_alog.write(log::alevel::devel,"connection connection::resume_reading"); + return transport_con_type::dispatch( + lib::bind( + &type::handle_resume_reading, + type::get_shared() + ) + ); +} + +/// Resume reading helper method. Not safe to call directly +template +void connection::handle_resume_reading() { + m_read_flag = true; + read_frame(); +} @@ -341,13 +409,13 @@ bool connection::get_secure() const { } template -const std::string& connection::get_host() const { +std::string const & connection::get_host() const { //scoped_lock_type lock(m_connection_state_lock); return m_uri->get_host(); } template -const std::string& connection::get_resource() const { +std::string const & connection::get_resource() const { //scoped_lock_type lock(m_connection_state_lock); return m_uri->get_resource(); } @@ -376,18 +444,18 @@ void connection::set_uri(uri_ptr uri) { template -const std::string & connection::get_subprotocol() const { +std::string const & connection::get_subprotocol() const { return m_subprotocol; } template -const std::vector & +std::vector const & connection::get_requested_subprotocols() const { return m_requested_subprotocols; } template -void connection::add_subprotocol(const std::string & value, +void connection::add_subprotocol(std::string const & value, lib::error_code & ec) { if (m_is_server) { @@ -407,17 +475,17 @@ void connection::add_subprotocol(const std::string & value, } template -void connection::add_subprotocol(const std::string & value) { +void connection::add_subprotocol(std::string const & value) { lib::error_code ec; this->add_subprotocol(value,ec); if (ec) { - throw ec; + throw exception(ec); } } template -void connection::select_subprotocol(const std::string & value, +void connection::select_subprotocol(std::string const & value, lib::error_code & ec) { if (!m_is_server) { @@ -445,134 +513,199 @@ void connection::select_subprotocol(const std::string & value, } template -void connection::select_subprotocol(const std::string & value) { +void connection::select_subprotocol(std::string const & value) { lib::error_code ec; this->select_subprotocol(value,ec); if (ec) { - throw ec; + throw exception(ec); } } template -const std::string & -connection::get_request_header(const std::string &key) { +std::string const & +connection::get_request_header(std::string const & key) const { return m_request.get_header(key); } template -const std::string & -connection::get_response_header(const std::string &key) { - return m_response.get_header(key); +std::string const & +connection::get_request_body() const { + return m_request.get_body(); } +template +std::string const & +connection::get_response_header(std::string const & key) const { + return m_response.get_header(key); +} + +// TODO: EXCEPTION_FREE template void connection::set_status(http::status_code::value code) { - //scoped_lock_type lock(m_connection_state_lock); - if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw error::make_error_code(error::invalid_state); - //throw exception("Call to set_status from invalid state", - // error::INVALID_STATE); + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); } - m_response.set_status(code); } + +// TODO: EXCEPTION_FREE template void connection::set_status(http::status_code::value code, std::string const & msg) { - //scoped_lock_type lock(m_connection_state_lock); - if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw error::make_error_code(error::invalid_state); - //throw exception("Call to set_status from invalid state", - // error::INVALID_STATE); + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); } m_response.set_status(code,msg); } + +// TODO: EXCEPTION_FREE template void connection::set_body(std::string const & value) { - //scoped_lock_type lock(m_connection_state_lock); - if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { - throw error::make_error_code(error::invalid_state); - //throw exception("Call to set_status from invalid state", - // error::INVALID_STATE); + throw exception("Call to set_status from invalid state", + error::make_error_code(error::invalid_state)); } m_response.set_body(value); } +// TODO: EXCEPTION_FREE template void connection::append_header(std::string const & key, std::string const & val) { - //scoped_lock_type lock(m_connection_state_lock); - if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection m_response.append_header(key,val); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection m_request.append_header(key,val); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to append_header from invalid state", + error::make_error_code(error::invalid_state)); } } } + +// TODO: EXCEPTION_FREE template void connection::replace_header(std::string const & key, std::string const & val) { - // scoped_lock_type lock(m_connection_state_lock); - if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection m_response.replace_header(key,val); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection m_request.replace_header(key,val); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to replace_header from invalid state", + error::make_error_code(error::invalid_state)); } } } + +// TODO: EXCEPTION_FREE template void connection::remove_header(std::string const & key) { - //scoped_lock_type lock(m_connection_state_lock); - if (m_is_server) { if (m_internal_state == istate::PROCESS_HTTP_REQUEST) { // we are setting response headers for an incoming server connection m_response.remove_header(key); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); } } else { if (m_internal_state == istate::USER_INIT) { // we are setting initial headers for an outgoing client connection m_request.remove_header(key); } else { - throw error::make_error_code(error::invalid_state); + throw exception("Call to remove_header from invalid state", + error::make_error_code(error::invalid_state)); } } } +/// Defer HTTP Response until later +/** + * 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. + * + * @return A status code, zero on success, non-zero otherwise + */ +template +lib::error_code connection::defer_http_response() { + // Cancel handshake timer, otherwise the connection will time out and we'll + // close the connection before the app has a chance to send a response. + if (m_handshake_timer) { + m_handshake_timer->cancel(); + m_handshake_timer.reset(); + } + + // Do something to signal deferral + m_http_state = session::http_state::deferred; + + return lib::error_code(); +} +/// 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 + */ +template +void connection::send_http_response(lib::error_code & ec) { + { + scoped_lock_type lock(m_connection_state_lock); + if (m_http_state != session::http_state::deferred) { + ec = error::make_error_code(error::invalid_state); + return; + } + + m_http_state = session::http_state::body_written; + } + + this->write_http_response(lib::error_code()); + ec = lib::error_code(); +} + +template +void connection::send_http_response() { + lib::error_code ec; + this->send_http_response(ec); + if (ec) { + throw exception(ec); + } +} @@ -583,11 +716,13 @@ template void connection::start() { m_alog.write(log::alevel::devel,"connection start"); - this->atomic_state_change( - istate::USER_INIT, - istate::TRANSPORT_INIT, - "Start must be called from user init state" - ); + if (m_internal_state != istate::USER_INIT) { + m_alog.write(log::alevel::devel,"Start called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::TRANSPORT_INIT; // Depending on how the transport implements init this function may return // immediately and call handle_transport_init later or call @@ -605,40 +740,31 @@ template void connection::handle_transport_init(lib::error_code const & ec) { m_alog.write(log::alevel::devel,"connection handle_transport_init"); - { - scoped_lock_type lock(m_connection_state_lock); + lib::error_code ecm = ec; - if (m_internal_state != istate::TRANSPORT_INIT) { - throw error::make_error_code(error::invalid_state); - //throw exception("handle_transport_init must be called from transport init state", - // error::INVALID_STATE); - } - - if (!ec) { - // unless there was a transport error, advance internal state. - if (m_is_server) { - m_internal_state = istate::READ_HTTP_REQUEST; - } else { - m_internal_state = istate::WRITE_HTTP_REQUEST; - } - } + if (m_internal_state != istate::TRANSPORT_INIT) { + m_alog.write(log::alevel::devel, + "handle_transport_init must be called from transport init state"); + ecm = error::make_error_code(error::invalid_state); } - if (ec) { + if (ecm) { std::stringstream s; - s << "handle_transport_init received error: "<< ec.message(); - m_elog.write(log::elevel::fatal,s.str()); + s << "handle_transport_init received error: "<< ecm.message(); + m_elog.write(log::elevel::rerror,s.str()); - this->terminate(ec); + this->terminate(ecm); return; } // At this point the transport is ready to read and write bytes. if (m_is_server) { + m_internal_state = istate::READ_HTTP_REQUEST; this->read_handshake(1); } else { // We are a client. Set the processor to the version specified in the // config file and send a handshake request. + m_internal_state = istate::WRITE_HTTP_REQUEST; m_processor = get_processor(config::client_version); this->send_http_request(); } @@ -646,16 +772,18 @@ void connection::handle_transport_init(lib::error_code const & ec) { template void connection::read_handshake(size_t num_bytes) { - m_alog.write(log::alevel::devel,"connection read"); + m_alog.write(log::alevel::devel,"connection read_handshake"); - m_handshake_timer = transport_con_type::set_timer( - config::timeout_open_handshake, - lib::bind( - &type::handle_open_handshake_timeout, - type::get_shared(), - lib::placeholders::_1 - ) - ); + if (m_open_handshake_timeout_dur > 0) { + m_handshake_timer = transport_con_type::set_timer( + m_open_handshake_timeout_dur, + lib::bind( + &type::handle_open_handshake_timeout, + type::get_shared(), + lib::placeholders::_1 + ) + ); + } transport_con_type::async_read_at_least( num_bytes, @@ -670,32 +798,45 @@ void connection::read_handshake(size_t num_bytes) { ); } -// All exit paths for this function need to call send_http_response() or submit +// All exit paths for this function need to call write_http_response() or submit // a new read request with this function as the handler. template -void connection::handle_read_handshake(const lib::error_code& ec, +void connection::handle_read_handshake(lib::error_code const & ec, size_t bytes_transferred) { m_alog.write(log::alevel::devel,"connection handle_read_handshake"); - this->atomic_state_check( - istate::READ_HTTP_REQUEST, - "handle_read_handshake must be called from READ_HTTP_REQUEST state" - ); + lib::error_code ecm = ec; - if (ec) { - if (ec == transport::error::eof) { - // we expect to get eof if the connection is closed already - if (m_state == session::state::closed) { - m_alog.write(log::alevel::devel,"got eof from closed con"); - return; - } + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_read_handshake invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); } + } - std::stringstream s; - s << "error in handle_read_handshake: "<< ec.message(); - m_elog.write(log::elevel::fatal,s.str()); - this->terminate(ec); + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_handshake",ecm); + this->terminate(ecm); return; } @@ -713,13 +854,13 @@ void connection::handle_read_handshake(const lib::error_code& ec, // All HTTP exceptions will result in this request failing and an error // response being returned. No more bytes will be read in this con. m_response.set_status(e.m_error_code,e.m_error_msg); - this->send_http_response_error(); + this->write_http_response_error(error::make_error_code(error::http_parse_error)); return; } // More paranoid boundaries checking. // TODO: Is this overkill? - if (bytes_processed > config::connection_read_buffer_size) { + if (bytes_processed > bytes_transferred) { m_elog.write(log::elevel::fatal,"Fatal boundaries checking error."); this->terminate(make_error_code(error::general)); return; @@ -733,8 +874,9 @@ void connection::handle_read_handshake(const lib::error_code& ec, } if (m_request.ready()) { - if (!this->initialize_processor()) { - this->send_http_response_error(); + lib::error_code processor_ec = this->initialize_processor(); + if (processor_ec) { + this->write_http_response_error(processor_ec); return; } @@ -751,14 +893,14 @@ void connection::handle_read_handshake(const lib::error_code& ec, // TODO: need more bytes m_alog.write(log::alevel::devel,"short key3 read"); m_response.set_status(http::status_code::internal_server_error); - this->send_http_response_error(); + this->write_http_response_error(processor::error::make_error_code(processor::error::short_key3)); return; } } if (m_alog.static_test(log::alevel::devel)) { m_alog.write(log::alevel::devel,m_request.raw()); - if (m_request.get_header("Sec-WebSocket-Key3") != "") { + if (!m_request.get_header("Sec-WebSocket-Key3").empty()) { m_alog.write(log::alevel::devel, utility::to_hex(m_request.get_header("Sec-WebSocket-Key3"))); } @@ -770,15 +912,18 @@ void connection::handle_read_handshake(const lib::error_code& ec, std::copy(m_buf+bytes_processed,m_buf+bytes_transferred,m_buf); m_buf_cursor = bytes_transferred-bytes_processed; - this->atomic_state_change( - istate::READ_HTTP_REQUEST, - istate::PROCESS_HTTP_REQUEST, - "send_http_response must be called from READ_HTTP_REQUEST state" - ); + m_internal_state = istate::PROCESS_HTTP_REQUEST; + // We have the complete request. Process it. - this->process_handshake_request(); - this->send_http_response(); + lib::error_code handshake_ec = this->process_handshake_request(); + + // Write a response if this is a websocket connection or if it is an + // HTTP connection for which the response has not been deferred or + // started yet by a different system (i.e. still in init state). + if (!m_is_http || m_http_state == session::http_state::init) { + this->write_http_response(handshake_ec); + } } else { // read at least 1 more byte transport_con_type::async_read_at_least( @@ -795,36 +940,43 @@ void connection::handle_read_handshake(const lib::error_code& ec, } } -// send_http_response requires the request to be fully read and the connection +// write_http_response requires the request to be fully read and the connection // to be in the PROCESS_HTTP_REQUEST state. In some cases we can detect errors // before the request is fully read (specifically at a point where we aren't // sure if the hybi00 key3 bytes need to be read). This method sets the correct -// state and calls send_http_response +// state and calls write_http_response template -void connection::send_http_response_error() { - this->atomic_state_change( - istate::READ_HTTP_REQUEST, - istate::PROCESS_HTTP_REQUEST, - "send_http_response must be called from READ_HTTP_REQUEST state" - ); - this->send_http_response(); +void connection::write_http_response_error(lib::error_code const & ec) { + if (m_internal_state != istate::READ_HTTP_REQUEST) { + m_alog.write(log::alevel::devel, + "write_http_response_error called in invalid state"); + this->terminate(error::make_error_code(error::invalid_state)); + return; + } + + m_internal_state = istate::PROCESS_HTTP_REQUEST; + + this->write_http_response(ec); } -// All exit paths for this function need to call send_http_response() or submit +// All exit paths for this function need to call write_http_response() or submit // a new read request with this function as the handler. template -void connection::handle_read_frame(const lib::error_code& ec, +void connection::handle_read_frame(lib::error_code const & ec, size_t bytes_transferred) { //m_alog.write(log::alevel::devel,"connection handle_read_frame"); - this->atomic_state_check( - istate::PROCESS_CONNECTION, - "handle_read_frame must be called from PROCESS_CONNECTION state" - ); + lib::error_code ecm = ec; - if (ec) { - if (ec == transport::error::eof) { + if (!ecm && m_internal_state != istate::PROCESS_CONNECTION) { + ecm = error::make_error_code(error::invalid_state); + } + + if (ecm) { + log::level echannel = log::elevel::rerror; + + if (ecm == transport::error::eof) { if (m_state == session::state::closed) { // we expect to get eof if the connection is closed already // just ignore it @@ -837,17 +989,31 @@ void connection::handle_read_frame(const lib::error_code& ec, terminate(lib::error_code()); return; } + } else if (ecm == error::invalid_state) { + // In general, invalid state errors in the closed state are the + // result of handlers that were in the system already when the state + // changed and should be ignored as they pose no problems and there + // is nothing useful that we can do about them. + if (m_state == session::state::closed) { + m_alog.write(log::alevel::devel, + "handle_read_frame: got invalid istate in closed state"); + return; + } + } else if (ecm == transport::error::tls_short_read) { + if (m_state == session::state::closed) { + // We expect to get a TLS short read if we try to read after the + // connection is closed. If this happens ignore and exit the + // read frame path. + terminate(lib::error_code()); + return; + } + echannel = log::elevel::rerror; + } else if (ecm == transport::error::action_after_shutdown) { + echannel = log::elevel::info; } - if (ec == transport::error::tls_short_read) { - m_elog.write(log::elevel::rerror,"got TLS short read, killing connection for now"); - this->terminate(ec); - return; - } - - std::stringstream s; - s << "error in handle_read_frame: " << ec.message() << " (" << ec << ")"; - m_elog.write(log::elevel::fatal,s.str()); - this->terminate(ec); + + log_err(echannel, "handle_read_frame", ecm); + this->terminate(ecm); return; } @@ -873,12 +1039,18 @@ void connection::handle_read_frame(const lib::error_code& ec, m_alog.write(log::alevel::devel,s.str()); } - lib::error_code ec; + lib::error_code consume_ec; + + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "Processing Bytes: " << utility::to_hex(reinterpret_cast(m_buf)+p,bytes_transferred-p); + m_alog.write(log::alevel::devel,s.str()); + } p += m_processor->consume( reinterpret_cast(m_buf)+p, bytes_transferred-p, - ec + consume_ec ); if (m_alog.static_test(log::alevel::devel)) { @@ -886,20 +1058,22 @@ void connection::handle_read_frame(const lib::error_code& ec, s << "bytes left after consume: " << bytes_transferred-p; m_alog.write(log::alevel::devel,s.str()); } - if (ec) { - m_elog.write(log::elevel::rerror,"consume error: "+ec.message()); + if (consume_ec) { + log_err(log::elevel::rerror, "consume", consume_ec); if (config::drop_on_protocol_error) { - this->terminate(ec); + this->terminate(consume_ec); return; } else { lib::error_code close_ec; - this->close(processor::error::to_ws(ec),ec.message(),close_ec); + this->close( + processor::error::to_ws(consume_ec), + consume_ec.message(), + close_ec + ); if (close_ec) { - m_elog.write(log::elevel::fatal, - "Failed to send a close frame after protocol error: " - +close_ec.message()); + log_err(log::elevel::fatal, "Protocol error close frame ", close_ec); this->terminate(close_ec); return; } @@ -910,20 +1084,18 @@ void connection::handle_read_frame(const lib::error_code& ec, if (m_processor->ready()) { if (m_alog.static_test(log::alevel::devel)) { std::stringstream s; - s << "Complete frame received. Dispatching"; + s << "Complete message received. Dispatching"; m_alog.write(log::alevel::devel,s.str()); } message_ptr msg = m_processor->get_message(); if (!msg) { - m_alog.write(log::alevel::devel, - "null message from m_processor"); + m_alog.write(log::alevel::devel, "null message from m_processor"); } else if (!is_control(msg->get_opcode())) { // data message, dispatch to user if (m_state != session::state::open) { - m_elog.write(log::elevel::warn, - "got non-close data frame in state closing"); + m_elog.write(log::elevel::warn, "got non-close frame while closing"); } else if (m_message_handler) { m_message_handler(m_connection_hdl, msg); } @@ -933,6 +1105,16 @@ void connection::handle_read_frame(const lib::error_code& ec, } } + read_frame(); +} + +/// Issue a new transport read unless reading is paused. +template +void connection::read_frame() { + if (!m_read_flag) { + return; + } + transport_con_type::async_read_at_least( // std::min wont work with undefined static const values. // TODO: is there a more elegant way to do this? @@ -944,49 +1126,41 @@ void connection::handle_read_frame(const lib::error_code& ec, 1, m_buf, config::connection_read_buffer_size, - /*lib::bind( - &type::handle_read_frame, - type::get_shared(), - lib::placeholders::_1, - lib::placeholders::_2 - )*/ m_handle_read_frame ); } template -bool connection::initialize_processor() { +lib::error_code connection::initialize_processor() { m_alog.write(log::alevel::devel,"initialize_processor"); // if it isn't a websocket handshake nothing to do. if (!processor::is_websocket_handshake(m_request)) { - return true; + return lib::error_code(); } int version = processor::get_websocket_version(m_request); if (version < 0) { - m_alog.write(log::alevel::devel, - "BAD REQUEST: can't determine version"); + m_alog.write(log::alevel::devel, "BAD REQUEST: can't determine version"); m_response.set_status(http::status_code::bad_request); - return false; + return error::make_error_code(error::invalid_version); } m_processor = get_processor(version); // if the processor is not null we are done if (m_processor) { - return true; + return lib::error_code(); } // We don't have a processor for this version. Return bad request // with Sec-WebSocket-Version header filled with values we do accept - m_alog.write(log::alevel::devel, - "BAD REQUEST: no processor for version"); + m_alog.write(log::alevel::devel, "BAD REQUEST: no processor for version"); m_response.set_status(http::status_code::bad_request); std::stringstream ss; - std::string sep = ""; + std::string sep; std::vector::const_iterator it; for (it = versions_supported.begin(); it != versions_supported.end(); it++) { @@ -995,11 +1169,11 @@ bool connection::initialize_processor() { } m_response.replace_header("Sec-WebSocket-Version",ss.str()); - return false; + return error::make_error_code(error::unsupported_version); } template -bool connection::process_handshake_request() { +lib::error_code connection::process_handshake_request() { m_alog.write(log::alevel::devel,"process handshake request"); if (!processor::is_websocket_handshake(m_request)) { @@ -1013,19 +1187,24 @@ bool connection::process_handshake_request() { ); if (!m_uri->get_valid()) { - m_alog.write(log::alevel::devel, - std::string("Bad request: failed to parse uri")); + m_alog.write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return false; + return error::make_error_code(error::invalid_uri); } if (m_http_handler) { + m_is_http = true; m_http_handler(m_connection_hdl); + + if (m_state == session::state::closed) { + return error::make_error_code(error::http_connection_ended); + } } else { set_status(http::status_code::upgrade_required); + return error::make_error_code(error::upgrade_required); } - return true; + return lib::error_code(); } lib::error_code ec = m_processor->validate_handshake(m_request); @@ -1033,10 +1212,9 @@ bool connection::process_handshake_request() { // Validate: make sure all required elements are present. if (ec){ // Not a valid handshake request - m_alog.write(log::alevel::devel, - "BAD REQUEST (724) "+ec.message()); + m_alog.write(log::alevel::devel, "Bad request " + ec.message()); m_response.set_status(http::status_code::bad_request); - return false; + return ec; } // Read extension parameters and set up values necessary for the end user @@ -1047,10 +1225,9 @@ bool connection::process_handshake_request() { if (neg_results.first) { // There was a fatal error in extension parsing that should result in // a failed connection attempt. - m_alog.write(log::alevel::devel, - "BAD REQUEST: (737) " + neg_results.first.message()); + m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message()); m_response.set_status(http::status_code::bad_request); - return false; + return neg_results.first; } else { // extension negotiation succeeded, set response header accordingly // we don't send an empty extensions header because it breaks many @@ -1066,10 +1243,9 @@ bool connection::process_handshake_request() { if (!m_uri->get_valid()) { - m_alog.write(log::alevel::devel, - std::string("Bad request: failed to parse uri")); + m_alog.write(log::alevel::devel, "Bad request: failed to parse uri"); m_response.set_status(http::status_code::bad_request); - return false; + return error::make_error_code(error::invalid_uri); } // extract subprotocols @@ -1091,14 +1267,14 @@ bool connection::process_handshake_request() { if (ec) { std::stringstream s; s << "Processing error: " << ec << "(" << ec.message() << ")"; - m_alog.write(log::alevel::devel,s.str()); + m_alog.write(log::alevel::devel, s.str()); m_response.set_status(http::status_code::internal_server_error); - return false; + return ec; } } else { // User application has rejected the handshake - m_alog.write(log::alevel::devel,"USER REJECT"); + m_alog.write(log::alevel::devel, "USER REJECT"); // Use Bad Request if the user handler did not provide a more // specific http response error code. @@ -1106,25 +1282,33 @@ bool connection::process_handshake_request() { if (m_response.get_status_code() == http::status_code::uninitialized) { m_response.set_status(http::status_code::bad_request); } - - return false; + + return error::make_error_code(error::rejected); } - return true; + return lib::error_code(); } template -void connection::send_http_response() { - m_alog.write(log::alevel::devel,"connection send_http_response"); +void connection::write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"connection write_http_response"); + + if (ec == error::make_error_code(error::http_connection_ended)) { + m_alog.write(log::alevel::http,"An HTTP handler took over the connection."); + return; + } if (m_response.get_status_code() == http::status_code::uninitialized) { m_response.set_status(http::status_code::internal_server_error); + m_ec = error::make_error_code(error::general); + } else { + m_ec = ec; } m_response.set_version("HTTP/1.1"); // Set server header based on the user agent settings - if (m_response.get_header("Server") == "") { + if (m_response.get_header("Server").empty()) { if (!m_user_agent.empty()) { m_response.replace_header("Server",m_user_agent); } else { @@ -1142,7 +1326,7 @@ void connection::send_http_response() { if (m_alog.static_test(log::alevel::devel)) { m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer); - if (m_response.get_header("Sec-WebSocket-Key3") != "") { + if (!m_response.get_header("Sec-WebSocket-Key3").empty()) { m_alog.write(log::alevel::devel, utility::to_hex(m_response.get_header("Sec-WebSocket-Key3"))); } @@ -1153,7 +1337,7 @@ void connection::send_http_response() { m_handshake_buffer.data(), m_handshake_buffer.size(), lib::bind( - &type::handle_send_http_response, + &type::handle_write_http_response, type::get_shared(), lib::placeholders::_1 ) @@ -1161,24 +1345,42 @@ void connection::send_http_response() { } template -void connection::handle_send_http_response( - const lib::error_code& ec) -{ - m_alog.write(log::alevel::devel,"handle_send_http_response"); +void connection::handle_write_http_response(lib::error_code const & ec) { + m_alog.write(log::alevel::devel,"handle_write_http_response"); - this->atomic_state_check( - istate::PROCESS_HTTP_REQUEST, - "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state" - ); + lib::error_code ecm = ec; - if (ec) { - m_elog.write(log::elevel::rerror, - "error in handle_send_http_response: "+ec.message()); - this->terminate(ec); - return; + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::PROCESS_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_write_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } } - this->log_open_result(); + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_write_http_response",ecm); + this->terminate(ecm); + return; + } if (m_handshake_timer) { m_handshake_timer->cancel(); @@ -1187,8 +1389,11 @@ void connection::handle_send_http_response( if (m_response.get_status_code() != http::status_code::switching_protocols) { - if (m_processor) { - // this was a websocket connection that ended in an error + /*if (m_processor || m_ec == error::http_parse_error || + m_ec == error::invalid_version || m_ec == error::unsupported_version + || m_ec == error::upgrade_required) + {*/ + if (!m_is_http) { std::stringstream s; s << "Handshake ended with HTTP error: " << m_response.get_status_code(); @@ -1196,18 +1401,24 @@ void connection::handle_send_http_response( } else { // if this was not a websocket connection, we have written // the expected response and the connection can be closed. - } - this->terminate(make_error_code(error::http_connection_ended)); + + this->log_http_result(); + + if (m_ec) { + m_alog.write(log::alevel::devel, + "got to writing HTTP results with m_ec set: "+m_ec.message()); + } + m_ec = make_error_code(error::http_connection_ended); + } + + this->terminate(m_ec); return; } - this->atomic_state_change( - istate::PROCESS_HTTP_REQUEST, - istate::PROCESS_CONNECTION, - session::state::connecting, - session::state::open, - "handle_send_http_response must be called from PROCESS_HTTP_REQUEST state" - ); + this->log_open_result(); + + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; if (m_open_handler) { m_open_handler(m_connection_hdl); @@ -1230,18 +1441,16 @@ void connection::send_http_request() { m_requested_subprotocols); if (ec) { - m_elog.write(log::elevel::fatal, - "Internal library error: processor error: "+ec.message()); + log_err(log::elevel::fatal,"Internal library error: Processor",ec); return; } } else { - m_elog.write(log::elevel::fatal, - "Internal library error: missing processor"); + m_elog.write(log::elevel::fatal,"Internal library error: missing processor"); return; } // Unless the user has overridden the user agent, send generic WS++ UA. - if (m_request.get_header("User-Agent") == "") { + if (m_request.get_header("User-Agent").empty()) { if (!m_user_agent.empty()) { m_request.replace_header("User-Agent",m_user_agent); } else { @@ -1252,8 +1461,7 @@ void connection::send_http_request() { m_handshake_buffer = m_request.raw(); if (m_alog.static_test(log::alevel::devel)) { - m_alog.write(log::alevel::devel, - "Raw Handshake request:\n"+m_handshake_buffer); + m_alog.write(log::alevel::devel,"Raw Handshake request:\n"+m_handshake_buffer); } if (m_open_handshake_timeout_dur > 0) { @@ -1279,26 +1487,44 @@ void connection::send_http_request() { } template -void connection::handle_send_http_request(const lib::error_code& ec) { +void connection::handle_send_http_request(lib::error_code const & ec) { m_alog.write(log::alevel::devel,"handle_send_http_request"); - this->atomic_state_check( - istate::WRITE_HTTP_REQUEST, - "handle_send_http_request must be called from WRITE_HTTP_REQUEST state" - ); + lib::error_code ecm = ec; - if (ec) { - m_elog.write(log::elevel::rerror, - "error in handle_send_http_request: "+ec.message()); - this->terminate(ec); - return; + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::WRITE_HTTP_REQUEST) { + ecm = error::make_error_code(error::invalid_state); + } else { + m_internal_state = istate::READ_HTTP_RESPONSE; + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_send_http_request invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } } - this->atomic_state_change( - istate::WRITE_HTTP_REQUEST, - istate::READ_HTTP_RESPONSE, - "handle_send_http_request must be called from WRITE_HTTP_REQUEST state" - ); + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_send_http_request",ecm); + this->terminate(ecm); + return; + } transport_con_type::async_read_at_least( 1, @@ -1314,22 +1540,45 @@ void connection::handle_send_http_request(const lib::error_code& ec) { } template -void connection::handle_read_http_response(const lib::error_code& ec, +void connection::handle_read_http_response(lib::error_code const & ec, size_t bytes_transferred) { m_alog.write(log::alevel::devel,"handle_read_http_response"); - this->atomic_state_check( - istate::READ_HTTP_RESPONSE, - "handle_read_http_response must be called from READ_HTTP_RESPONSE state" - ); + lib::error_code ecm = ec; - if (ec) { - m_elog.write(log::elevel::rerror, - "error in handle_read_http_response: "+ec.message()); - this->terminate(ec); + if (!ecm) { + scoped_lock_type lock(m_connection_state_lock); + + if (m_state == session::state::connecting) { + if (m_internal_state != istate::READ_HTTP_RESPONSE) { + ecm = error::make_error_code(error::invalid_state); + } + } else if (m_state == session::state::closed) { + // The connection was canceled while the response was being sent, + // usually by the handshake timer. This is basically expected + // (though hopefully rare) and there is nothing we can do so ignore. + m_alog.write(log::alevel::devel, + "handle_read_http_response invoked after connection was closed"); + return; + } else { + ecm = error::make_error_code(error::invalid_state); + } + } + + if (ecm) { + if (ecm == transport::error::eof && m_state == session::state::closed) { + // we expect to get eof if the connection is closed already + m_alog.write(log::alevel::devel, + "got (expected) eof/state error from closed con"); + return; + } + + log_err(log::elevel::rerror,"handle_read_http_response",ecm); + this->terminate(ecm); return; } + size_t bytes_processed = 0; // TODO: refactor this to use error codes rather than exceptions try { @@ -1349,27 +1598,38 @@ void connection::handle_read_http_response(const lib::error_code& ec, m_handshake_timer.reset(); } - lib::error_code ec = m_processor->validate_server_handshake_response( + lib::error_code validate_ec = m_processor->validate_server_handshake_response( m_request, m_response ); - if (ec) { - m_elog.write(log::elevel::rerror, - std::string("Server handshake response was invalid: ")+ - ec.message() - ); - this->terminate(ec); + if (validate_ec) { + log_err(log::elevel::rerror,"Server handshake response",validate_ec); + this->terminate(validate_ec); return; } - // response is valid, connection can now be assumed to be open - this->atomic_state_change( - istate::READ_HTTP_RESPONSE, - istate::PROCESS_CONNECTION, - session::state::connecting, - session::state::open, - "handle_read_http_response must be called from READ_HTTP_RESPONSE state" - ); + // Read extension parameters and set up values necessary for the end + // user to complete extension negotiation. + std::pair neg_results; + neg_results = m_processor->negotiate_extensions(m_response); + + if (neg_results.first) { + // There was a fatal error in extension negotiation. For the moment + // kill all connections that fail extension negotiation. + + // TODO: deal with cases where the response is well formed but + // doesn't match the options requested by the client. Its possible + // that the best behavior in this cases is to log and continue with + // an unextended connection. + m_alog.write(log::alevel::devel, "Extension negotiation failed: " + + neg_results.first.message()); + this->terminate(make_error_code(error::extension_neg_failed)); + // TODO: close connection with reason 1010 (and list extensions) + } + + // response is valid, connection can now be assumed to be open + m_internal_state = istate::PROCESS_CONNECTION; + m_state = session::state::open; this->log_open_result(); @@ -1404,14 +1664,13 @@ void connection::handle_open_handshake_timeout( lib::error_code const & ec) { if (ec == transport::error::operation_aborted) { - m_alog.write(log::alevel::devel, - "asio open handshake timer cancelled"); + m_alog.write(log::alevel::devel,"open handshake timer cancelled"); } else if (ec) { m_alog.write(log::alevel::devel, - "asio open handle_open_handshake_timeout error: "+ec.message()); + "open handle_open_handshake_timeout error: "+ec.message()); // TODO: ignore or fail here? } else { - m_alog.write(log::alevel::devel, "asio open handshake timer expired"); + m_alog.write(log::alevel::devel,"open handshake timer expired"); terminate(make_error_code(error::open_handshake_timeout)); } } @@ -1421,8 +1680,7 @@ void connection::handle_close_handshake_timeout( lib::error_code const & ec) { if (ec == transport::error::operation_aborted) { - m_alog.write(log::alevel::devel, - "asio close handshake timer cancelled"); + m_alog.write(log::alevel::devel,"asio close handshake timer cancelled"); } else if (ec) { m_alog.write(log::alevel::devel, "asio open handle_close_handshake_timeout error: "+ec.message()); @@ -1452,9 +1710,19 @@ void connection::terminate(lib::error_code const & ec) { m_local_close_reason = ec.message(); } + // TODO: does any of this need a mutex? + if (m_is_http) { + m_http_state = session::http_state::closed; + } if (m_state == session::state::connecting) { m_state = session::state::closed; tstat = failed; + + // Log fail result here before socket is shut down and we can't get + // the remote address, etc anymore + if (m_ec != error::http_connection_ended) { + log_fail_result(); + } } else if (m_state != session::state::closed) { m_state = session::state::closed; tstat = closed; @@ -1486,15 +1754,16 @@ void connection::handle_terminate(terminate_status tstat, if (ec) { // there was an error actually shutting down the connection - m_elog.write(log::elevel::rerror,ec.message()); + log_err(log::elevel::devel,"handle_terminate",ec); } // clean shutdown if (tstat == failed) { - if (m_fail_handler) { - m_fail_handler(m_connection_hdl); + if (m_ec != error::http_connection_ended) { + if (m_fail_handler) { + m_fail_handler(m_connection_hdl); + } } - log_fail_result(); } else if (tstat == closed) { if (m_close_handler) { m_close_handler(m_connection_hdl); @@ -1510,10 +1779,9 @@ void connection::handle_terminate(terminate_status tstat, if (m_termination_handler) { try { m_termination_handler(type::get_shared()); - } catch (const std::exception& e) { + } catch (std::exception const & e) { m_elog.write(log::elevel::warn, - std::string("termination_handler call failed. Reason was: ") - +e.what()); + std::string("termination_handler call failed. Reason was: ")+e.what()); } } } @@ -1533,52 +1801,83 @@ void connection::write_frame() { return; } - // Get the next message in the queue. This will return an empty - // message if the queue was empty. - m_current_msg = write_pop(); - - if (!m_current_msg) { + // pull off all the messages that are ready to write. + // stop if we get a message marked terminal + message_ptr next_message = write_pop(); + while (next_message) { + m_current_msgs.push_back(next_message); + if (!next_message->get_terminal()) { + next_message = write_pop(); + } else { + next_message = message_ptr(); + } + } + + if (m_current_msgs.empty()) { + // there was nothing to send return; + } else { + // At this point we own the next messages to be sent and are + // responsible for holding the write flag until they are + // successfully sent or there is some error + m_write_flag = true; } - - // At this point we own the next message to be sent and are - // responsible for holding the write flag until it is successfully - // sent or there is some error - m_write_flag = true; } - std::string const & header = m_current_msg->get_header(); - std::string const & payload = m_current_msg->get_payload(); - - m_send_buffer.push_back(transport::buffer(header.c_str(),header.size())); - m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size())); + typename std::vector::iterator it; + for (it = m_current_msgs.begin(); it != m_current_msgs.end(); ++it) { + std::string const & header = (*it)->get_header(); + std::string const & payload = (*it)->get_payload(); + m_send_buffer.push_back(transport::buffer(header.c_str(),header.size())); + m_send_buffer.push_back(transport::buffer(payload.c_str(),payload.size())); + } + // Print detailed send stats if those log levels are enabled if (m_alog.static_test(log::alevel::frame_header)) { if (m_alog.dynamic_test(log::alevel::frame_header)) { - std::stringstream s; - s << "Dispatching write with " << header.size() - << " header bytes and " << payload.size() - << " payload bytes" << std::endl; - m_alog.write(log::alevel::frame_header,s.str()); - m_alog.write(log::alevel::frame_header,"Header: "+utility::to_hex(header)); - } - } - if (m_alog.static_test(log::alevel::frame_payload)) { - if (m_alog.dynamic_test(log::alevel::frame_payload)) { - m_alog.write(log::alevel::frame_payload,"Payload: "+utility::to_hex(payload)); - } - } + std::stringstream general,header,payload; + + general << "Dispatching write containing " << m_current_msgs.size() + <<" message(s) containing "; + header << "Header Bytes: \n"; + payload << "Payload Bytes: \n"; + + size_t hbytes = 0; + size_t pbytes = 0; + + for (size_t i = 0; i < m_current_msgs.size(); i++) { + hbytes += m_current_msgs[i]->get_header().size(); + pbytes += m_current_msgs[i]->get_payload().size(); + + header << "[" << i << "] (" + << m_current_msgs[i]->get_header().size() << ") " + << utility::to_hex(m_current_msgs[i]->get_header()) << "\n"; + + if (m_alog.static_test(log::alevel::frame_payload)) { + if (m_alog.dynamic_test(log::alevel::frame_payload)) { + payload << "[" << i << "] (" + << m_current_msgs[i]->get_payload().size() << ") ["<get_opcode()<<"] " + << (m_current_msgs[i]->get_opcode() == frame::opcode::text ? + m_current_msgs[i]->get_payload() : + utility::to_hex(m_current_msgs[i]->get_payload()) + ) + << "\n"; + } + } + } + + general << hbytes << " header bytes and " << pbytes << " payload bytes"; + + m_alog.write(log::alevel::frame_header,general.str()); + m_alog.write(log::alevel::frame_header,header.str()); + m_alog.write(log::alevel::frame_payload,payload.str()); + } + } transport_con_type::async_write( m_send_buffer, - /*lib::bind( - &type::handle_write_frame, - type::get_shared(), - m_current_msg->get_terminal(), - lib::placeholders::_1 - )*/ m_write_frame_handler ); } @@ -1590,18 +1889,19 @@ void connection::handle_write_frame(lib::error_code const & ec) m_alog.write(log::alevel::devel,"connection handle_write_frame"); } - bool terminate = m_current_msg->get_terminal(); + bool terminal = m_current_msgs.back()->get_terminal(); m_send_buffer.clear(); - m_current_msg.reset(); + m_current_msgs.clear(); + // TODO: recycle instead of deleting if (ec) { - m_elog.write(log::elevel::fatal,"error in handle_write_frame: "+ec.message()); + log_err(log::elevel::fatal,"handle_write_frame",ec); this->terminate(ec); return; } - if (terminate) { + if (terminal) { this->terminate(lib::error_code()); return; } @@ -1625,57 +1925,13 @@ void connection::handle_write_frame(lib::error_code const & ec) } template -void connection::atomic_state_change(istate_type req, - istate_type dest, std::string msg) -{ - scoped_lock_type lock(m_connection_state_lock); - - if (m_internal_state != req) { - throw error::make_error_code(error::invalid_state); - //throw exception(msg,error::INVALID_STATE); - } - - m_internal_state = dest; -} - -template -void connection::atomic_state_change( - istate_type internal_req, istate_type internal_dest, - session::state::value external_req, session::state::value external_dest, - std::string msg) -{ - scoped_lock_type lock(m_connection_state_lock); - - if (m_internal_state != internal_req || m_state != external_req) { - throw error::make_error_code(error::invalid_state); - //throw exception(msg,error::INVALID_STATE); - } - - m_internal_state = internal_dest; - m_state = external_dest; -} - -template -void connection::atomic_state_check(istate_type req, - std::string msg) -{ - scoped_lock_type lock(m_connection_state_lock); - - if (m_internal_state != req) { - throw error::make_error_code(error::invalid_state); - //throw exception(msg,error::INVALID_STATE); - } -} - -template -const std::vector& connection::get_supported_versions() const +std::vector const & connection::get_supported_versions() const { return versions_supported; } template -void connection::process_control_frame(typename - config::message_type::ptr msg) +void connection::process_control_frame(typename config::message_type::ptr msg) { m_alog.write(log::alevel::devel,"process_control_frame"); @@ -1696,17 +1952,16 @@ void connection::process_control_frame(typename } if (op == frame::opcode::PING) { - bool pong = true; + bool should_reply = true; if (m_ping_handler) { - pong = m_ping_handler(m_connection_hdl, msg->get_payload()); + should_reply = m_ping_handler(m_connection_hdl, msg->get_payload()); } - if (pong) { + if (should_reply) { this->pong(msg->get_payload(),ec); if (ec) { - m_elog.write(log::elevel::devel, - "Failed to send response pong: "+ec.message()); + log_err(log::elevel::devel,"Failed to send response pong",ec); } } } else if (op == frame::opcode::PONG) { @@ -1722,7 +1977,7 @@ void connection::process_control_frame(typename m_remote_close_code = close::extract_code(msg->get_payload(),ec); if (ec) { - std::stringstream s; + s.str(""); if (config::drop_on_protocol_error) { s << "Received invalid close code " << m_remote_close_code << " dropping connection per config."; @@ -1735,8 +1990,7 @@ void connection::process_control_frame(typename ec = send_close_ack(close::status::protocol_error, "Invalid close code"); if (ec) { - m_elog.write(log::elevel::devel, - "send_close_ack error: "+ec.message()); + log_err(log::elevel::devel,"send_close_ack",ec); } } return; @@ -1754,27 +2008,25 @@ void connection::process_control_frame(typename ec = send_close_ack(close::status::protocol_error, "Invalid close reason"); if (ec) { - m_elog.write(log::elevel::devel, - "send_close_ack error: "+ec.message()); + log_err(log::elevel::devel,"send_close_ack",ec); } } return; } if (m_state == session::state::open) { - std::stringstream s; + s.str(""); s << "Received close frame with code " << m_remote_close_code << " and reason " << m_remote_close_reason; m_alog.write(log::alevel::devel,s.str()); ec = send_close_ack(); if (ec) { - m_elog.write(log::elevel::devel, - "send_close_ack error: "+ec.message()); + log_err(log::elevel::devel,"send_close_ack",ec); } } else if (m_state == session::state::closing && !m_was_clean) { // ack of our close - m_alog.write(log::alevel::devel,"Got acknowledgement of close"); + m_alog.write(log::alevel::devel, "Got acknowledgement of close"); m_was_clean = true; @@ -1790,11 +2042,11 @@ void connection::process_control_frame(typename } } else { // spurious, ignore - m_elog.write(log::elevel::devel,"Got close frame in wrong state"); + m_elog.write(log::elevel::devel, "Got close frame in wrong state"); } } else { // got an invalid control opcode - m_elog.write(log::elevel::devel,"Got control frame with invalid opcode"); + m_elog.write(log::elevel::devel, "Got control frame with invalid opcode"); // initiate protocol error shutdown } } @@ -1823,7 +2075,7 @@ lib::error_code connection::send_close_frame(close::status::value code, if (config::silent_close) { m_alog.write(log::alevel::devel,"closing silently"); m_local_close_code = close::status::no_status; - m_local_close_reason = ""; + m_local_close_reason.clear(); } else if (code != close::status::blank) { m_alog.write(log::alevel::devel,"closing with specified codes"); m_local_close_code = code; @@ -1831,12 +2083,12 @@ lib::error_code connection::send_close_frame(close::status::value code, } else if (!ack) { m_alog.write(log::alevel::devel,"closing with no status code"); m_local_close_code = close::status::no_status; - m_local_close_reason = ""; + m_local_close_reason.clear(); } else if (m_remote_close_code == close::status::no_status) { m_alog.write(log::alevel::devel, "acknowledging a no-status close with normal code"); m_local_close_code = close::status::normal; - m_local_close_reason = ""; + m_local_close_reason.clear(); } else { m_alog.write(log::alevel::devel,"acknowledging with remote codes"); m_local_close_code = m_remote_close_code; @@ -1906,49 +2158,49 @@ template typename connection::processor_ptr connection::get_processor(int version) const { // TODO: allow disabling certain versions + + processor_ptr p; + switch (version) { case 0: - return processor_ptr( - new processor::hybi00( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager - ) + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager ); break; case 7: - return processor_ptr( - new processor::hybi07( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) ); break; case 8: - return processor_ptr( - new processor::hybi08( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) ); break; case 13: - return processor_ptr( - new processor::hybi13( - transport_con_type::is_secure(), - m_is_server, - m_msg_manager, - m_rng - ) + p = lib::make_shared >( + transport_con_type::is_secure(), + m_is_server, + m_msg_manager, + lib::ref(m_rng) ); break; default: - return processor_ptr(); + return p; } + + // Settings not configured by the constructor + p->set_max_message_size(m_max_message_size); + + return p; } template @@ -1961,10 +2213,12 @@ void connection::write_push(typename config::message_type::ptr msg) m_send_buffer_size += msg->get_payload().size(); m_send_queue.push(msg); - std::stringstream s; - s << "write_push: message count: " << m_send_queue.size() - << " buffer size: " << m_send_buffer_size; - m_alog.write(log::alevel::devel,s.str()); + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_push: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog.write(log::alevel::devel,s.str()); + } } template @@ -1981,10 +2235,12 @@ typename config::message_type::ptr connection::write_pop() m_send_buffer_size -= msg->get_payload().size(); m_send_queue.pop(); - std::stringstream s; - s << "write_pop: message count: " << m_send_queue.size() - << " buffer size: " << m_send_buffer_size; - m_alog.write(log::alevel::devel,s.str()); + if (m_alog.static_test(log::alevel::devel)) { + std::stringstream s; + s << "write_pop: message count: " << m_send_queue.size() + << " buffer size: " << m_send_buffer_size; + m_alog.write(log::alevel::devel,s.str()); + } return msg; } @@ -2013,7 +2269,7 @@ void connection::log_open_result() // User Agent std::string ua = m_request.get_header("User-Agent"); - if (ua == "") { + if (ua.empty()) { s << "\"\" "; } else { // check if there are any quotes in the user agent @@ -2036,9 +2292,9 @@ void connection::log_close_result() s << "Disconnect " << "close local:[" << m_local_close_code - << (m_local_close_reason == "" ? "" : ","+m_local_close_reason) + << (m_local_close_reason.empty() ? "" : ","+m_local_close_reason) << "] remote:[" << m_remote_close_code - << (m_remote_close_reason == "" ? "" : ","+m_remote_close_reason) << "]"; + << (m_remote_close_reason.empty() ? "" : ","+m_remote_close_reason) << "]"; m_alog.write(log::alevel::disconnect,s.str()); } @@ -2046,9 +2302,69 @@ void connection::log_close_result() template void connection::log_fail_result() { - // TODO: include more information about the connection? - // should this be filed under connect rather than disconnect? - m_alog.write(log::alevel::disconnect,"Failed: "+m_ec.message()); + std::stringstream s; + + int version = processor::get_websocket_version(m_request); + + // Connection Type + s << "WebSocket Connection "; + + // Remote endpoint address & WebSocket version + s << transport_con_type::get_remote_endpoint(); + if (version < 0) { + s << " -"; + } else { + s << " v" << version; + } + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + // URI + s << (m_uri ? m_uri->get_resource() : "-"); + + // HTTP Status code + s << " " << m_response.get_status_code(); + + // WebSocket++ error code & reason + s << " " << m_ec << " " << m_ec.message(); + + m_alog.write(log::alevel::fail,s.str()); +} + +template +void connection::log_http_result() { + std::stringstream s; + + if (processor::is_websocket_handshake(m_request)) { + m_alog.write(log::alevel::devel,"Call to log_http_result for WebSocket"); + return; + } + + // Connection Type + s << (m_request.get_header("host").empty() ? "-" : m_request.get_header("host")) + << " " << transport_con_type::get_remote_endpoint() + << " \"" << m_request.get_method() + << " " << (m_uri ? m_uri->get_resource() : "-") + << " " << m_request.get_version() << "\" " << m_response.get_status_code() + << " " << m_response.get_body().size(); + + // User Agent + std::string ua = m_request.get_header("User-Agent"); + if (ua.empty()) { + s << " \"\" "; + } else { + // check if there are any quotes in the user agent + s << " \"" << utility::string_replace_all(ua,"\"","\\\"") << "\" "; + } + + m_alog.write(log::alevel::http,s.str()); } } // namespace websocketpp diff --git a/src/third_party/websocketpp/websocketpp/impl/endpoint_impl.hpp b/src/third_party/websocketpp/websocketpp/impl/endpoint_impl.hpp index 0dd14c5d..e09cda95 100644 --- a/src/third_party/websocketpp/websocketpp/impl/endpoint_impl.hpp +++ b/src/third_party/websocketpp/websocketpp/impl/endpoint_impl.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,6 +28,8 @@ #ifndef WEBSOCKETPP_ENDPOINT_IMPL_HPP #define WEBSOCKETPP_ENDPOINT_IMPL_HPP +#include + namespace websocketpp { template @@ -42,8 +44,8 @@ endpoint::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(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::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::create_connection() { } template -void endpoint::interrupt(connection_hdl hdl, - lib::error_code & ec) +void endpoint::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 void endpoint::interrupt(connection_hdl hdl) { lib::error_code ec; interrupt(hdl,ec); - if (ec) { throw ec; } + if (ec) { throw exception(ec); } } template -void endpoint::send(connection_hdl hdl, std::string const & - payload, frame::opcode::value op, lib::error_code & ec) +void endpoint::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 +void endpoint::pause_reading(connection_hdl hdl) { + lib::error_code ec; + pause_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::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 +void endpoint::resume_reading(connection_hdl hdl) { + lib::error_code ec; + resume_reading(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::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 +void endpoint::send_http_response(connection_hdl hdl) { + lib::error_code ec; + send_http_response(hdl,ec); + if (ec) { throw exception(ec); } +} + +template +void endpoint::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::send(connection_hdl hdl, std::string const & } template -void endpoint::send(connection_hdl hdl, std::string const & - payload, frame::opcode::value op) +void endpoint::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 @@ -139,7 +192,7 @@ void endpoint::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 @@ -155,7 +208,7 @@ template void endpoint::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 @@ -174,7 +227,7 @@ void endpoint::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 @@ -187,17 +240,16 @@ void endpoint::ping(connection_hdl hdl, std::string const & } template -void endpoint::ping(connection_hdl hdl, std::string const & - payload) +void endpoint::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 -void endpoint::pong(connection_hdl hdl, std::string const & - payload, lib::error_code & ec) +void endpoint::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::pong(connection_hdl hdl, std::string const & } template -void endpoint::pong(connection_hdl hdl, std::string const & - payload) +void endpoint::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 diff --git a/src/third_party/websocketpp/websocketpp/impl/utilities_impl.hpp b/src/third_party/websocketpp/websocketpp/impl/utilities_impl.hpp index 368b8681..6f86e22f 100644 --- a/src/third_party/websocketpp/websocketpp/impl/utilities_impl.hpp +++ b/src/third_party/websocketpp/websocketpp/impl/utilities_impl.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,6 +28,9 @@ #ifndef WEBSOCKETPP_UTILITIES_IMPL_HPP #define WEBSOCKETPP_UTILITIES_IMPL_HPP +#include +#include + 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(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) { diff --git a/src/third_party/websocketpp/websocketpp/logger/basic.hpp b/src/third_party/websocketpp/websocketpp/logger/basic.hpp index 678c7246..84514130 100644 --- a/src/third_party/websocketpp/websocketpp/logger/basic.hpp +++ b/src/third_party/websocketpp/websocketpp/logger/basic.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: @@ -40,13 +40,16 @@ * */ -#include -#include -#include +#include #include #include -#include +#include + +#include +#include +#include +#include namespace websocketpp { namespace log { @@ -55,16 +58,58 @@ namespace log { template class basic { public: - basic(std::ostream * out = &std::cout) + basic(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(std::ostream * out) : m_static_channels(0xffffffff) , m_dynamic_channels(0) , m_out(out) {} - basic(level c, std::ostream * out = &std::cout) + basic(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(level c, std::ostream * out) : m_static_channels(c) , m_dynamic_channels(0) , m_out(out) {} + /// Destructor + ~basic() {} + + /// Copy constructor + basic(basic 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 & operator=(basic const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + basic(basic && 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 & operator=(basic &&) = 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(<,"%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",<); + return os << (result == 0 ? "Unknown" : buffer); #endif } - mutex_type m_lock; - level const m_static_channels; level m_dynamic_channels; std::ostream * m_out; diff --git a/src/third_party/websocketpp/websocketpp/logger/levels.hpp b/src/third_party/websocketpp/websocketpp/logger/levels.hpp index 2c0e92c9..cd7ccd69 100644 --- a/src/third_party/websocketpp/websocketpp/logger/levels.hpp +++ b/src/third_party/websocketpp/websocketpp/logger/levels.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: @@ -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"; } diff --git a/src/third_party/websocketpp/websocketpp/logger/stub.hpp b/src/third_party/websocketpp/websocketpp/logger/stub.hpp index 4608d35b..2db6da7d 100644 --- a/src/third_party/websocketpp/websocketpp/logger/stub.hpp +++ b/src/third_party/websocketpp/websocketpp/logger/stub.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,10 +28,11 @@ #ifndef WEBSOCKETPP_LOGGER_STUB_HPP #define WEBSOCKETPP_LOGGER_STUB_HPP -#include +#include #include -#include + +#include 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; } }; diff --git a/src/third_party/websocketpp/websocketpp/message_buffer/alloc.hpp b/src/third_party/websocketpp/websocketpp/message_buffer/alloc.hpp index 5ac0ed43..75d89766 100644 --- a/src/third_party/websocketpp/websocketpp/message_buffer/alloc.hpp +++ b/src/third_party/websocketpp/websocketpp/message_buffer/alloc.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: @@ -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(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(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()); } }; diff --git a/src/third_party/websocketpp/websocketpp/message_buffer/message.hpp b/src/third_party/websocketpp/websocketpp/message_buffer/message.hpp index e7839c8a..da36e200 100644 --- a/src/third_party/websocketpp/websocketpp/message_buffer/message.hpp +++ b/src/third_party/websocketpp/websocketpp/message_buffer/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: @@ -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 */ diff --git a/src/third_party/websocketpp/websocketpp/message_buffer/pool.hpp b/src/third_party/websocketpp/websocketpp/message_buffer/pool.hpp index 46e24013..3af9e02a 100644 --- a/src/third_party/websocketpp/websocketpp/message_buffer/pool.hpp +++ b/src/third_party/websocketpp/websocketpp/message_buffer/pool.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: @@ -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(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(); } }; diff --git a/src/third_party/websocketpp/websocketpp/processors/base.hpp b/src/third_party/websocketpp/websocketpp/processors/base.hpp index e455e842..ddb8b81a 100644 --- a/src/third_party/websocketpp/websocketpp/processors/base.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/base.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,14 +28,13 @@ #ifndef WEBSOCKETPP_PROCESSOR_BASE_HPP #define WEBSOCKETPP_PROCESSOR_BASE_HPP -#include -#include - #include #include #include -#include +#include +#include + #include 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"; } diff --git a/src/third_party/websocketpp/websocketpp/processors/hybi00.hpp b/src/third_party/websocketpp/websocketpp/processors/hybi00.hpp index 27d0f936..95ad9dfa 100644 --- a/src/third_party/websocketpp/websocketpp/processors/hybi00.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/hybi00.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,15 +28,21 @@ #ifndef WEBSOCKETPP_PROCESSOR_HYBI00_HPP #define WEBSOCKETPP_PROCESSOR_HYBI00_HPP -#include - #include +#include + #include #include #include +#include #include +#include +#include +#include +#include + 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(secure, server) + explicit hybi00(bool secure, bool p_is_server, msg_manager_ptr manager) + : processor(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(8), key3.size()), + key3.c_str()+(std::min)(static_cast(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 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 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 & 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(base::m_secure, h, request.get_uri()); } else { - return uri_ptr(new uri(base::m_secure, + return lib::make_shared(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 diff --git a/src/third_party/websocketpp/websocketpp/processors/hybi07.hpp b/src/third_party/websocketpp/websocketpp/processors/hybi07.hpp index 8b2df0ee..14b67c21 100644 --- a/src/third_party/websocketpp/websocketpp/processors/hybi07.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/hybi07.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: @@ -30,6 +30,9 @@ #include +#include +#include + 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(secure, server, manager, rng) {} + explicit hybi07(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi08(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 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 const &) const { return error::make_error_code(error::no_protocol_support); } diff --git a/src/third_party/websocketpp/websocketpp/processors/hybi08.hpp b/src/third_party/websocketpp/websocketpp/processors/hybi08.hpp index 54f21036..15f6e658 100644 --- a/src/third_party/websocketpp/websocketpp/processors/hybi08.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/hybi08.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: @@ -30,6 +30,9 @@ #include +#include +#include + 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(secure, server, manager, rng) {} + explicit hybi08(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : hybi13(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 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 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: diff --git a/src/third_party/websocketpp/websocketpp/processors/hybi13.hpp b/src/third_party/websocketpp/websocketpp/processors/hybi13.hpp index b6b984fe..79486654 100644 --- a/src/third_party/websocketpp/websocketpp/processors/hybi13.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/hybi13.hpp @@ -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 - -#include -#include -#include -#include - #include +#include +#include + +#include #include #include +#include +#include + +#include +#include #include #include #include @@ -67,9 +69,8 @@ public: typedef std::pair err_str_pair; - explicit hybi13(bool secure, bool server, msg_manager_ptr manager, - rng_type& rng) - : processor(secure,server) + explicit hybi13(bool secure, bool p_is_server, msg_manager_ptr manager, rng_type& rng) + : processor(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 + 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 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(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(const_cast(i.data())), - reinterpret_cast(const_cast(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(); diff --git a/src/third_party/websocketpp/websocketpp/processors/processor.hpp b/src/third_party/websocketpp/websocketpp/processors/processor.hpp index 6333b5b5..5131cc45 100644 --- a/src/third_party/websocketpp/websocketpp/processors/processor.hpp +++ b/src/third_party/websocketpp/websocketpp/processors/processor.hpp @@ -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 #include -#include +#include #include +#include +#include namespace websocketpp { /// Processors encapsulate the protocol rules specific to each WebSocket version @@ -103,7 +105,11 @@ bool is_websocket_handshake(request_type& r) { */ template 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(scheme, h, request.get_uri()); } else { - return uri_ptr(new uri(scheme, + return lib::make_shared(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 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 & 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 diff --git a/src/third_party/websocketpp/websocketpp/random/none.hpp b/src/third_party/websocketpp/websocketpp/random/none.hpp index 60fe7728..2163e6f4 100644 --- a/src/third_party/websocketpp/websocketpp/random/none.hpp +++ b/src/third_party/websocketpp/websocketpp/random/none.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: @@ -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 { diff --git a/src/third_party/websocketpp/websocketpp/random/random_device.hpp b/src/third_party/websocketpp/websocketpp/random/random_device.hpp index 7645399b..91e7dd16 100644 --- a/src/third_party/websocketpp/websocketpp/random/random_device.hpp +++ b/src/third_party/websocketpp/websocketpp/random/random_device.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: diff --git a/src/third_party/websocketpp/websocketpp/roles/client_endpoint.hpp b/src/third_party/websocketpp/websocketpp/roles/client_endpoint.hpp index 1072a0f3..2de1a10a 100644 --- a/src/third_party/websocketpp/websocketpp/roles/client_endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/roles/client_endpoint.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: @@ -29,13 +29,16 @@ #define WEBSOCKETPP_CLIENT_ENDPOINT_HPP #include +#include + #include -#include +#include + +#include 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 endpoint_type; + friend class connection; + 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(u); if (!location->get_valid()) { ec = error::make_error_code(error::invalid_uri); diff --git a/src/third_party/websocketpp/websocketpp/roles/server_endpoint.hpp b/src/third_party/websocketpp/websocketpp/roles/server_endpoint.hpp index 77f1c291..d76eea8a 100644 --- a/src/third_party/websocketpp/websocketpp/roles/server_endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/roles/server_endpoint.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: @@ -29,13 +29,13 @@ #define WEBSOCKETPP_SERVER_ENDPOINT_HPP #include + #include -#include +#include 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 endpoint_type; - - // TODO: clean up these types + friend class connection; 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() {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no copy constructor because endpoints are not copyable + server(server &) = delete; + + // no copy assignment operator because endpoints are not copyable + server & operator=(server const &) = delete; +#endif // _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + +#ifdef _WEBSOCKETPP_MOVE_SEMANTICS_ + /// Move constructor + server(server && o) : endpoint,config>(std::move(o)) {} + +#ifdef _WEBSOCKETPP_DEFAULT_DELETE_FUNCTIONS_ + // no move assignment operator because of const member variables + server & operator=(server &&) = 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(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 diff --git a/src/third_party/websocketpp/websocketpp/server.hpp b/src/third_party/websocketpp/websocketpp/server.hpp index 50d33d10..342fa8c5 100644 --- a/src/third_party/websocketpp/websocketpp/server.hpp +++ b/src/third_party/websocketpp/websocketpp/server.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: diff --git a/src/third_party/websocketpp/websocketpp/sha1/sha1.hpp b/src/third_party/websocketpp/websocketpp/sha1/sha1.hpp index fd4f0760..43a84338 100644 --- a/src/third_party/websocketpp/websocketpp/sha1/sha1.hpp +++ b/src/third_party/websocketpp/websocketpp/sha1/sha1.hpp @@ -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 diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/base.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/base.hpp index 28ac933c..b945fe11 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/base.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/base.hpp @@ -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 #include #include #include - -#include - -#include -#include -#include +#include #include 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(&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::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 -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 - void operator()(Arg1 arg1) - { - handler_(arg1); - } + template + void operator()(Arg1 arg1) { + handler_(arg1); + } - template - void operator()(Arg1 arg1, Arg2 arg2) - { - handler_(arg1, arg2); - } + template + void operator()(Arg1 arg1, Arg2 arg2) { + handler_(arg1, arg2); + } - friend void* asio_handler_allocate(std::size_t size, - custom_alloc_handler* this_handler) - { - return this_handler->allocator_.allocate(size); - } + friend void* asio_handler_allocate(std::size_t size, + custom_alloc_handler * this_handler) + { + return this_handler->allocator_.allocate(size); + } - friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, - custom_alloc_handler* this_handler) - { - this_handler->allocator_.deallocate(pointer); - } + friend void asio_handler_deallocate(void* pointer, std::size_t /*size*/, + custom_alloc_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 inline custom_alloc_handler make_custom_alloc_handler( - handler_allocator& a, Handler h) + handler_allocator & a, Handler h) { - return custom_alloc_handler(a, h); + return custom_alloc_handler(a, h); } @@ -158,13 +142,10 @@ inline custom_alloc_handler make_custom_alloc_handler( template class endpoint; -typedef lib::function - socket_shutdown_handler; - -typedef lib::function async_read_handler; -typedef lib::function async_write_handler; typedef lib::function pre_init_handler; diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/connection.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/connection.hpp index f0aefa57..8eb8c759 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/connection.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/connection.hpp @@ -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 -#include -#include +#include + +#include + #include #include -#include -#include #include #include +#include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include namespace websocketpp { @@ -51,10 +57,10 @@ namespace asio { typedef lib::function 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 @@ -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 strand_ptr; - /// Type of a pointer to the ASIO timer class - typedef lib::shared_ptr 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 strand_ptr; + /// Type of a pointer to the Asio timer class + typedef lib::shared_ptr 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; // 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(); 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::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::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& 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 const & bufs, write_handler handler) { std::vector::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 @@ -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 m_bufs; + std::vector 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; }; diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/endpoint.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/endpoint.hpp index 2728c66b..46ff24c0 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/endpoint.hpp @@ -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 -#include #include #include #include -#include -#include -#include +#include +#include -#include +#include + +#include +#include 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 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 acceptor_ptr; + typedef lib::shared_ptr acceptor_ptr; /// Type of a shared pointer to the resolver being used - typedef lib::shared_ptr resolver_ptr; + typedef lib::shared_ptr resolver_ptr; /// Type of timer handle - typedef lib::shared_ptr timer_ptr; + typedef lib::shared_ptr timer_ptr; /// Type of a shared pointer to an io_service work object - typedef lib::shared_ptr work_ptr; + typedef lib::shared_ptr 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::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 service(new lib::asio::io_service()); +#else + lib::auto_ptr 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 service(new lib::asio::io_service()); +#else + lib::auto_ptr 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 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::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( + *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::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(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; diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/security/base.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/security/base.hpp index 7e003794..0f08f404 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/security/base.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/security/base.hpp @@ -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 #include #include #include #include #include -#include - -#include #include // 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 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; } diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/security/none.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/security/none.hpp index 1c9486fa..0e68a65c 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/security/none.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/security/none.hpp @@ -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 +#include + +#include #include -#include +#include +#include -#include +#include #include 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 +/// The signature of the socket init handler for this socket policy +typedef lib::function 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 { public: @@ -56,12 +62,14 @@ public: /// Type of a shared pointer to this connection socket component typedef lib::shared_ptr 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 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 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 socket_ptr; + typedef lib::shared_ptr 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::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 + 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, diff --git a/src/third_party/websocketpp/websocketpp/transport/asio/security/tls.hpp b/src/third_party/websocketpp/websocketpp/transport/asio/security/tls.hpp index 2a743112..7b32db81 100644 --- a/src/third_party/websocketpp/websocketpp/transport/asio/security/tls.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/asio/security/tls.hpp @@ -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 + +#include + +#include +#include #include #include #include -#include -#include -#include - -#include +#include #include 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&)> socket_init_handler; -typedef lib::function(connection_hdl)> +/// The signature of the socket_init_handler for this socket policy +typedef lib::function&)> socket_init_handler; +/// The signature of the tls_init_handler for this socket policy +typedef lib::function(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 { public: @@ -63,17 +68,15 @@ public: typedef lib::shared_ptr ptr; /// Type of the ASIO socket being used - typedef boost::asio::ssl::stream socket_type; + typedef lib::asio::ssl::stream socket_type; /// Type of a shared pointer to the ASIO socket being used typedef lib::shared_ptr 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 strand_ptr; + typedef lib::shared_ptr strand_ptr; /// Type of a shared pointer to the ASIO TLS context being used - typedef lib::shared_ptr context_ptr; - - typedef boost::system::error_code boost_error; + typedef lib::shared_ptr 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( + _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 + 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: diff --git a/src/third_party/websocketpp/websocketpp/transport/base/connection.hpp b/src/third_party/websocketpp/websocketpp/transport/base/connection.hpp index 70e1ec50..f76d4091 100644 --- a/src/third_party/websocketpp/websocketpp/transport/base/connection.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/base/connection.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: @@ -31,9 +31,10 @@ #include #include #include -#include #include +#include + 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"; } diff --git a/src/third_party/websocketpp/websocketpp/transport/base/endpoint.hpp b/src/third_party/websocketpp/websocketpp/transport/base/endpoint.hpp index 1fefd483..3b4b0d6d 100644 --- a/src/third_party/websocketpp/websocketpp/transport/base/endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/base/endpoint.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,13 +28,8 @@ #ifndef WEBSOCKETPP_TRANSPORT_BASE_HPP #define WEBSOCKETPP_TRANSPORT_BASE_HPP -#include -#include #include #include -#include - -#include namespace websocketpp { /// Transport policies provide network connectivity and timers diff --git a/src/third_party/websocketpp/websocketpp/transport/iostream/base.hpp b/src/third_party/websocketpp/websocketpp/transport/iostream/base.hpp index 5bfb5539..f8783987 100644 --- a/src/third_party/websocketpp/websocketpp/transport/iostream/base.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/iostream/base.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: @@ -30,14 +30,36 @@ #include #include +#include +#include + +#include #include +#include 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 + 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 const + & bufs)> vector_write_handler; + +/// The type and signature of the callback used by iostream transport to signal +/// a transport shutdown. +typedef lib::function shutdown_handler; + /// iostream transport errors namespace error { enum value { diff --git a/src/third_party/websocketpp/websocketpp/transport/iostream/connection.hpp b/src/third_party/websocketpp/websocketpp/transport/iostream/connection.hpp index 458e42cd..81c4f411 100644 --- a/src/third_party/websocketpp/websocketpp/transport/iostream/connection.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/iostream/connection.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,14 +28,22 @@ #ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP #define WEBSOCKETPP_TRANSPORT_IOSTREAM_CON_HPP -#include -#include -#include - -#include #include +#include + +#include + +#include + +#include +#include +#include + +#include +#include #include +#include #include 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 + * 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 const & bufs, write_handler handler) { + void async_write(std::vector 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::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::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::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; diff --git a/src/third_party/websocketpp/websocketpp/transport/iostream/endpoint.hpp b/src/third_party/websocketpp/websocketpp/transport/iostream/endpoint.hpp index ff37866d..14ec6537 100644 --- a/src/third_party/websocketpp/websocketpp/transport/iostream/endpoint.hpp +++ b/src/third_party/websocketpp/websocketpp/transport/iostream/endpoint.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,13 +28,15 @@ #ifndef WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP #define WEBSOCKETPP_TRANSPORT_IOSTREAM_HPP -#include -#include - #include #include -#include +#include +#include + +#include + +#include 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; diff --git a/src/third_party/websocketpp/websocketpp/uri.hpp b/src/third_party/websocketpp/websocketpp/uri.hpp index 5471031a..7159234f 100644 --- a/src/third_party/websocketpp/websocketpp/uri.hpp +++ b/src/third_party/websocketpp/websocketpp/uri.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,11 +28,11 @@ #ifndef WEBSOCKETPP_URI_HPP #define WEBSOCKETPP_URI_HPP -#include #include +#include + #include -#include #include #include @@ -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); } diff --git a/src/third_party/websocketpp/websocketpp/utf8_validator.hpp b/src/third_party/websocketpp/websocketpp/utf8_validator.hpp index 528ede18..c057da62 100644 --- a/src/third_party/websocketpp/websocketpp/utf8_validator.hpp +++ b/src/third_party/websocketpp/websocketpp/utf8_validator.hpp @@ -31,6 +31,8 @@ #include +#include + namespace websocketpp { namespace utf8_validator { diff --git a/src/third_party/websocketpp/websocketpp/utilities.hpp b/src/third_party/websocketpp/websocketpp/utilities.hpp index 07745661..747f1199 100644 --- a/src/third_party/websocketpp/websocketpp/utilities.hpp +++ b/src/third_party/websocketpp/websocketpp/utilities.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 @@ struct ci_less : std::binary_function { : public std::binary_function { 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 { diff --git a/src/third_party/websocketpp/websocketpp/version.hpp b/src/third_party/websocketpp/websocketpp/version.hpp index e6c9cbf7..b88cc110 100644 --- a/src/third_party/websocketpp/websocketpp/version.hpp +++ b/src/third_party/websocketpp/websocketpp/version.hpp @@ -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