From 5ca75411e97ad3be7db4c33568be2cbc16a67538 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Wed, 27 Sep 2017 17:19:26 -0600 Subject: [PATCH 01/17] Silencing visual studo warning C4309, https://stackoverflow.com/questions/15467837 --- client_ws.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client_ws.hpp b/client_ws.hpp index 3580ea7e..98495abb 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -199,11 +199,11 @@ namespace SimpleWeb { size_t num_bytes; if(length > 0xffff) { num_bytes = 8; - send_stream->put(static_cast(127 + 128)); + send_stream->put(static_cast(/*127 + 128*/ 0xFEu)); } else { num_bytes = 2; - send_stream->put(static_cast(126 + 128)); + send_stream->put(static_cast(/*126 + 128*/ 0xFEu)); } for(size_t c = num_bytes - 1; c != static_cast(-1); c--) From 04c3b6b252e568425ad605889b1a9940c9aa9ee2 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Wed, 27 Sep 2017 17:22:47 -0600 Subject: [PATCH 02/17] Silencing visual studio warning C4297, conversion from size_t to int --- crypto.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto.hpp b/crypto.hpp index 697d8f3e..70e18870 100644 --- a/crypto.hpp +++ b/crypto.hpp @@ -212,8 +212,8 @@ namespace SimpleWeb { static std::string pbkdf2(const std::string &password, const std::string &salt, int iterations, int key_size) noexcept { std::string key; key.resize(static_cast(key_size)); - PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), password.size(), - reinterpret_cast(salt.c_str()), salt.size(), iterations, + PKCS5_PBKDF2_HMAC_SHA1(password.c_str(), static_cast(password.size()), + reinterpret_cast(salt.c_str()), static_cast(salt.size()), iterations, key_size, reinterpret_cast(&key[0])); return key; } From cbdf24058c4a46af2bd15e170ba1bf817b586f9f Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Wed, 27 Sep 2017 17:57:19 -0600 Subject: [PATCH 03/17] silenced visual studio error C4244, possible loss of data --- client_ws.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client_ws.hpp b/client_ws.hpp index 98495abb..a720346d 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -216,7 +216,7 @@ namespace SimpleWeb { send_stream->put(static_cast(mask[c])); for(size_t c = 0; c < length; c++) - send_stream->put(message_stream->get() ^ mask[c % 4]); + send_stream->put(static_cast(message_stream->get() ^ mask[c % 4])); auto self = this->shared_from_this(); strand.post([self, send_stream, callback]() { @@ -234,7 +234,7 @@ namespace SimpleWeb { auto send_stream = std::make_shared(); - send_stream->put(status >> 8); + send_stream->put(static_cast(status >> 8)); send_stream->put(status % 256); *send_stream << reason; @@ -310,8 +310,8 @@ namespace SimpleWeb { void stop() noexcept { { std::unique_lock lock(connection_mutex); - if(connection) - connection->close(); + if(_connection) + _connection->close(); } if(internal_io_service) @@ -333,7 +333,7 @@ namespace SimpleWeb { unsigned short port; std::string path; - std::shared_ptr connection; + std::shared_ptr _connection; std::mutex connection_mutex; std::shared_ptr handler_runner; @@ -521,8 +521,8 @@ namespace SimpleWeb { if((connection->message->fin_rsv_opcode & 0x0f) == 8) { int status = 0; if(connection->message->length >= 2) { - unsigned char byte1 = connection->message->get(); - unsigned char byte2 = connection->message->get(); + unsigned char byte1 = static_cast(connection->message->get()); + unsigned char byte2 = static_cast(connection->message->get()); status = (byte1 << 8) + byte2; } @@ -590,7 +590,7 @@ namespace SimpleWeb { protected: void connect() override { std::unique_lock lock(connection_mutex); - auto connection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service)); + auto connection = this->_connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service)); lock.unlock(); asio::ip::tcp::resolver::query query(host, std::to_string(port)); auto resolver = std::make_shared(*io_service); From fcb887759a67e10a81a475173889f6a19a0f6ebc Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 09:30:49 -0600 Subject: [PATCH 04/17] silencing visual studio warning C4244: Conversion from int to unsigned char, possible loss of data --- server_ws.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server_ws.hpp b/server_ws.hpp index a3ed60d4..8efea32b 100644 --- a/server_ws.hpp +++ b/server_ws.hpp @@ -264,8 +264,8 @@ namespace SimpleWeb { auto send_stream = std::make_shared(); - send_stream->put(status >> 8); - send_stream->put(status % 256); + send_stream->put(static_cast(status >> 8)); + send_stream->put(static_cast(status % 256)); *send_stream << reason; @@ -619,15 +619,15 @@ namespace SimpleWeb { std::ostream message_data_out_stream(&message->streambuf); for(size_t c = 0; c < length; c++) { - message_data_out_stream.put(raw_message_data.get() ^ mask[c % 4]); + message_data_out_stream.put(static_cast(raw_message_data.get() ^ mask[c % 4])); } // If connection close if((fin_rsv_opcode & 0x0f) == 8) { int status = 0; if(length >= 2) { - unsigned char byte1 = message->get(); - unsigned char byte2 = message->get(); + unsigned char byte1 = static_cast(message->get()); + unsigned char byte2 = static_cast(message->get()); status = (byte1 << 8) + byte2; } From a3a24113e99f49846d5d1317e8e447a40bbc5938 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 09:49:18 -0600 Subject: [PATCH 05/17] silencing visual studio warning C4458: \'endpoint hides class member\' --- server_ws.hpp | 78 +++++++++++++++++++++++++-------------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/server_ws.hpp b/server_ws.hpp index 8efea32b..0bc2bdb5 100644 --- a/server_ws.hpp +++ b/server_ws.hpp @@ -371,17 +371,17 @@ namespace SimpleWeb { if(io_service->stopped()) io_service->reset(); - asio::ip::tcp::endpoint endpoint; + asio::ip::tcp::endpoint local_endpoint; if(config.address.size() > 0) - endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port); + local_endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port); else - endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port); + local_endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v4(), config.port); if(!acceptor) acceptor = std::unique_ptr(new asio::ip::tcp::acceptor(*io_service)); - acceptor->open(endpoint.protocol()); + acceptor->open(local_endpoint.protocol()); acceptor->set_option(asio::socket_base::reuse_address(config.reuse_address)); - acceptor->bind(endpoint); + acceptor->bind(local_endpoint); acceptor->listen(); accept(); @@ -516,14 +516,14 @@ namespace SimpleWeb { } } - void read_message(const std::shared_ptr &connection, Endpoint &endpoint) const { - asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &endpoint](const error_code &ec, size_t bytes_transferred) { + void read_message(const std::shared_ptr &connection, Endpoint &open_endpoint) const { + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &open_endpoint](const error_code &ec, size_t bytes_transferred) { auto lock = connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { if(bytes_transferred == 0) { // TODO: why does this happen sometimes? - read_message(connection, endpoint); + read_message(connection, open_endpoint); return; } std::istream stream(&connection->read_buffer); @@ -538,7 +538,7 @@ namespace SimpleWeb { if(first_bytes[1] < 128) { const std::string reason("message from client not masked"); connection->send_close(1002, reason); - connection_close(connection, endpoint, 1002, reason); + connection_close(connection, open_endpoint, 1002, reason); return; } @@ -546,7 +546,7 @@ namespace SimpleWeb { if(length == 126) { // 2 next bytes is the size of content - asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(2), [this, connection, &open_endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { auto lock = connection->handler_runner->continue_lock(); if(!lock) return; @@ -562,15 +562,15 @@ namespace SimpleWeb { for(size_t c = 0; c < num_bytes; c++) length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); - read_message_content(connection, length, endpoint, fin_rsv_opcode); + read_message_content(connection, length, open_endpoint, fin_rsv_opcode); } else - connection_error(connection, endpoint, ec); + connection_error(connection, open_endpoint, ec); }); } else if(length == 127) { // 8 next bytes is the size of content - asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(8), [this, connection, &endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(8), [this, connection, &open_endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { auto lock = connection->handler_runner->continue_lock(); if(!lock) return; @@ -586,22 +586,22 @@ namespace SimpleWeb { for(size_t c = 0; c < num_bytes; c++) length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); - read_message_content(connection, length, endpoint, fin_rsv_opcode); + read_message_content(connection, length, open_endpoint, fin_rsv_opcode); } else - connection_error(connection, endpoint, ec); + connection_error(connection, open_endpoint, ec); }); } else - read_message_content(connection, length, endpoint, fin_rsv_opcode); + read_message_content(connection, length, open_endpoint, fin_rsv_opcode); } else - connection_error(connection, endpoint, ec); + connection_error(connection, open_endpoint, ec); }); } - void read_message_content(const std::shared_ptr &connection, size_t length, Endpoint &endpoint, unsigned char fin_rsv_opcode) const { - asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(4 + length), [this, connection, length, &endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { + void read_message_content(const std::shared_ptr &connection, size_t length, Endpoint &msg_endpoint, unsigned char fin_rsv_opcode) const { + asio::async_read(*connection->socket, connection->read_buffer, asio::transfer_exactly(4 + length), [this, connection, length, &msg_endpoint, fin_rsv_opcode](const error_code &ec, size_t /*bytes_transferred*/) { auto lock = connection->handler_runner->continue_lock(); if(!lock) return; @@ -633,7 +633,7 @@ namespace SimpleWeb { auto reason = message->string(); connection->send_close(status, reason); - connection_close(connection, endpoint, status, reason); + connection_close(connection, msg_endpoint, status, reason); return; } else { @@ -643,58 +643,58 @@ namespace SimpleWeb { auto empty_send_stream = std::make_shared(); connection->send(empty_send_stream, nullptr, fin_rsv_opcode + 1); } - else if(endpoint.on_message) { + else if(msg_endpoint.on_message) { connection->cancel_timeout(); connection->set_timeout(); - endpoint.on_message(connection, message); + msg_endpoint.on_message(connection, message); } // Next message - read_message(connection, endpoint); + read_message(connection, msg_endpoint); } } else - connection_error(connection, endpoint, ec); + connection_error(connection, msg_endpoint, ec); }); } - void connection_open(const std::shared_ptr &connection, Endpoint &endpoint) const { + void connection_open(const std::shared_ptr &connection, Endpoint &open_endpoint) const { connection->cancel_timeout(); connection->set_timeout(); { - std::unique_lock lock(endpoint.connections_mutex); - endpoint.connections.insert(connection); + std::unique_lock lock(open_endpoint.connections_mutex); + open_endpoint.connections.insert(connection); } - if(endpoint.on_open) - endpoint.on_open(connection); + if(open_endpoint.on_open) + open_endpoint.on_open(connection); } - void connection_close(const std::shared_ptr &connection, Endpoint &endpoint, int status, const std::string &reason) const { + void connection_close(const std::shared_ptr &connection, Endpoint &close_endpoint, int status, const std::string &reason) const { connection->cancel_timeout(); connection->set_timeout(); { - std::unique_lock lock(endpoint.connections_mutex); - endpoint.connections.erase(connection); + std::unique_lock lock(close_endpoint.connections_mutex); + close_endpoint.connections.erase(connection); } - if(endpoint.on_close) - endpoint.on_close(connection, status, reason); + if(close_endpoint.on_close) + close_endpoint.on_close(connection, status, reason); } - void connection_error(const std::shared_ptr &connection, Endpoint &endpoint, const error_code &ec) const { + void connection_error(const std::shared_ptr &connection, Endpoint &err_endpoint, const error_code &ec) const { connection->cancel_timeout(); connection->set_timeout(); { - std::unique_lock lock(endpoint.connections_mutex); - endpoint.connections.erase(connection); + std::unique_lock lock(err_endpoint.connections_mutex); + err_endpoint.connections.erase(connection); } - if(endpoint.on_error) - endpoint.on_error(connection, ec); + if(err_endpoint.on_error) + err_endpoint.on_error(connection, ec); } }; From c721c4614a1ce81d401adf45da7f4d2221352b66 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 11:24:39 -0600 Subject: [PATCH 06/17] added sample project, works on windows --- sample/.gitignore | 1 + sample/CMakeLists.txt | 80 ++++++++++++++++++++++++++++ sample/readme.md | 45 ++++++++++++++++ sample/sample_client.cpp | 103 ++++++++++++++++++++++++++++++++++++ sample/sample_server.cpp | 110 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+) create mode 100644 sample/.gitignore create mode 100644 sample/CMakeLists.txt create mode 100644 sample/readme.md create mode 100644 sample/sample_client.cpp create mode 100644 sample/sample_server.cpp diff --git a/sample/.gitignore b/sample/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/sample/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt new file mode 100644 index 00000000..2c733e41 --- /dev/null +++ b/sample/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required (VERSION 2.8) +project (Simple-WebSocket-Sample) +get_filename_component(SAMPLE_ROOT ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) + +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /WX") +elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra -Wsign-conversion") +endif() + + + +set(BOOST_COMPONENTS system coroutine context thread) +# Late 2017 TODO: remove the following checks and always use std::regex +if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BOOST_REGEX") + endif() +endif() + +if (WIN32) + + # prereq: populate $env:BoostRoot and $env:BoostVer to tell cmake where boost is + set(BOOST_ROOT "$ENV{BoostRoot}") + set(BOOST_LIBRARYDIR "$ENV{BoostRoot}/lib") + set(BOOST_INCLUDEDIR "$ENV{BoostRoot}/include/boost-$ENV{SkyBoostVer}") + add_definitions(-DBOOST_ALL_DYN_LINK) + set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex) + + file(GLOB boostDlls "$ENV{BoostRoot}/lib/*.dll") + foreach(boostDll ${boostDlls}) + file(TO_CMAKE_PATH "${boostDll}" correctedBoostDll) + get_filename_component(boostFileName ${correctedBoostDll} NAME) + if(boostFileName MATCHES ".*boost_(chrono|system|regex|system|thread|coroutine|context).*\\.dll$") + configure_file( "${correctedBoostDll}" "${CMAKE_BINARY_DIR}" COPYONLY) + endif() + endforeach() + + # prereq: populate $env:OpenSSLRoot to tell cmake where OpenSSL is + set(OPENSSL_ROOT_DIR "$ENV{OpenSSLRoot}") + set(OPENSSL_INCLUDE_DIR "$ENV{OpenSSLRoot}/include") + +endif() + +find_package(Boost 1.54.0 COMPONENTS ${BOOST_COMPONENTS} REQUIRED) +include_directories(SYSTEM ${Boost_INCLUDE_DIR}) +include_directories(SYSTEM ${Boost_INCLUDE_DIR}) + +if(APPLE) + set(OPENSSL_ROOT_DIR "/usr/local/opt/openssl") +endif() + +#TODO: add requirement for version 1.0.1g (can it be done in one line?) +find_package(OpenSSL REQUIRED) +include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) + +find_package(Threads REQUIRED) + +include_directories(.) + +include_directories("${SAMPLE_ROOT}/..") + + +add_executable(sample_client sample_client.cpp) +set_target_properties(sample_client PROPERTIES LINKER_LANGUAGE CXX) +target_link_libraries(sample_client ${Boost_LIBRARIES}) +target_link_libraries(sample_client ${OPENSSL_CRYPTO_LIBRARY}) +target_link_libraries(sample_client ${CMAKE_THREAD_LIBS_INIT}) + +add_executable(sample_server sample_server.cpp) +set_target_properties(sample_server PROPERTIES LINKER_LANGUAGE CXX) +target_link_libraries(sample_server ${Boost_LIBRARIES}) +target_link_libraries(sample_server ${OPENSSL_LIBRARIES}) +target_link_libraries(sample_server ${CMAKE_THREAD_LIBS_INIT}) + +if(MSYS) + target_link_libraries(sample_client ws2_32 wsock32) + target_link_libraries(sample_server ws2_32 wsock32) +endif() diff --git a/sample/readme.md b/sample/readme.md new file mode 100644 index 00000000..38ea148b --- /dev/null +++ b/sample/readme.md @@ -0,0 +1,45 @@ +Simple-WebSocket-Sample +======================= + +This sample project contains two executables which allow the user to control the connections and message flow for Simple-WebSocket-Server. It might be useful for testing interoperability with other websockets implementations. + +The following actions are supported. + +### sample_server + + s : Start server + t : sTop server + m : send Message to all clients + q : Quit + +### sample_client + + s : Set up connection + l : cLose connection + c : stop Client + m : send Message + q : Quit + +## Usage + +Run one server and as many clients as you like. Type the letter for the desired action and hit enter. + +## Building + + +#### Windows + +Populate the following environmentla variables + +| variable | value | +|:--|:--| +| BoostRoot | C:\path\to\Boost | +| BoostVer | 1_62 | +| OpenSSLRoot | C:\path\to\OpenSSL | + +Specify the correct generator in your call to cmake + + mkdir /build + cd build + cmake .. -G "Visual Studio 15 2017 Win64" + diff --git a/sample/sample_client.cpp b/sample/sample_client.cpp new file mode 100644 index 00000000..29cba0b9 --- /dev/null +++ b/sample/sample_client.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include "boost/thread.hpp" +#include "client_ws.hpp" +typedef SimpleWeb::SocketClient WsClient; + +using namespace std; + +int main(int, char**) +{ + shared_ptr client; + shared_ptr _connection; + boost::thread client_thread; + + cout << "s : Set up connection" << endl + << "l : cLose connection" << endl + << "c : stop Client" << endl + << "m : send Message" << endl + << "q : Quit" << endl; + + string line; + while (line != "q") + { + getline(cin, line); + + if (line == "s") + { + client = std::make_shared("localhost:8081/some/http/resource"); + + client->on_open = [&](shared_ptr connection) + { + _connection = connection; + cout << "Client Started & Connection " << connection << " Opened" << endl; + }; + + client->on_close = [&](shared_ptr connection, int code, const string& reason) + { + _connection = nullptr; + cout << "Closed Connection" << connection << " with code: " << code << endl << "Reason: " << reason << endl; + }; + + client->on_error = [](shared_ptr connection, const boost::system::error_code& code) + { + cout << "Error (code " << code << ")" << endl << "Message: " << code.message() << endl; + }; + + client->on_message = [](shared_ptr connection, shared_ptr message) + { + cout << "Server Message: " << message->string() << endl; + }; + + client_thread = boost::thread([&client]() + { + client->start(); + }); + cout << "Connection started" << endl; + } + else if (line == "c") + { + if (client != nullptr) + { + client->stop(); + client = nullptr; + cout << "Stopped Client" << endl; + } + else + { + cout << "Client Already Stopped" << endl; + } + + } + else if (line == "l") + { + if (_connection != nullptr) + { + _connection->send_close(10, "Word to your moms, I came to drop bombs, I got more rhymes than the bible's got psalms.", [](const boost::system::error_code code) + { + cout << "Error on send_close Code: " << code + << " Message: " << code.message() << endl; + }); + cout << "Closed connection with message" << endl; + } + else + { + cout << "Connection already closed" << endl; + } + } + + else if (line == "m") + { + auto msg = std::make_shared(); + *msg << "It's tricky to rock a rhyme to rock a rhyme that's right on time it's tricky!"; + _connection->send(msg); + cout << "Message sent" << endl; + } + } + if (client != nullptr) + { + client->stop(); + client_thread.join(); + } +} diff --git a/sample/sample_server.cpp b/sample/sample_server.cpp new file mode 100644 index 00000000..20b665ad --- /dev/null +++ b/sample/sample_server.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include "server_ws.hpp" +typedef SimpleWeb::SocketServer WsServer; + +using namespace std; + +int main(int, char**) +{ + shared_ptr server; + thread server_thread; + + cout << "s : Start server" << endl + << "t : sTop server" << endl + << "m : send Message to all clients" << endl + << "q : Quit" << endl; + + string line; + while (line != "q") + { + getline(cin, line); + + if (line == "s") + { + server = make_shared(); + server->config.port = 8081; + + auto& tunnel = server->endpoint["/some/http/resource"]; + tunnel.on_message = [&server](shared_ptr connection, shared_ptr message) + { + auto message_str = message->string(); + cout << "Client Message: " << message_str << endl; + + auto sendThisBack = make_shared(); + *sendThisBack << "[echo] " << message_str; + connection->send(sendThisBack, [](const boost::system::error_code code) + { + if (code) + { + cout << "Error while responding: " << code << ", error message: " << code.message() << endl; + } + }); + }; + + tunnel.on_open = [](shared_ptr connection) + { + cout << "Opened Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address + << ":" << connection->remote_endpoint_port << endl; + }; + + tunnel.on_close = [](shared_ptr connection, int status, const string& reason) + { + cout << "Closed Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address + << ":" << connection->remote_endpoint_port << endl; + + //See RFC 6455 7.4.1. for status codes + cout << "ConnectsTo code: " << status << " Reason: " << reason << endl; + }; + + tunnel.on_error = [](shared_ptr connection, const boost::system::error_code& code) + { + //See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings + cout << "Error in connection " << (size_t)connection.get() << ". " << "Error: " << code + << ", error message: " << code.message() << endl; + }; + + server_thread = thread([&server]() + { + server->start(); + }); + cout << "Server started" << endl; + } + else if (line == "t") + { + server->stop(); + server_thread.join(); + server = nullptr; + cout << "Server stopped" << endl; + } + else if (line == "m") + { + int i = 0; + for (auto connection : server->get_connections()) + { + i++; + auto msg = make_shared(); + *msg << "This is for the kids whippin' up some home-cook, spittin' 86 bars f'n no hook.."; + cout << "sending message..."; + connection->send(msg, [&](const boost::system::error_code code) + { + if (code) + { + cout << "Error while sending to connnection: " << i << " Code: " << code + << " Message: " << code.message() << endl; + } + }); + cout << "sent" << endl; + } + } + } + + if (server != nullptr) + { + server->stop(); + server_thread.join(); + } +} From d800e979a68535cf7faa5d46812940efa924fb9f Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 12:33:51 -0600 Subject: [PATCH 07/17] prettied up output and README --- README.md | 6 ++++++ sample/{readme.md => README.md} | 11 ++++++++++- sample/sample_client.cpp | 20 ++++++++++---------- sample/sample_server.cpp | 21 ++++++++++----------- 4 files changed, 36 insertions(+), 22 deletions(-) rename sample/{readme.md => README.md} (80%) diff --git a/README.md b/README.md index 98bb187f..1aa34f91 100644 --- a/README.md +++ b/README.md @@ -40,12 +40,16 @@ cd .. #### Run server and client examples + ### WS + ```sh ./build/ws_examples ``` +See also: [Simple-WebSocket-Sample](sample/README.md) + ### WSS Before running the WSS-examples, an RSA private key (server.key) and an SSL certificate (server.crt) must be created. Follow, for instance, the instructions given here (for a self-signed certificate): http://www.akadia.com/services/ssh_test_certificate.html @@ -54,3 +58,5 @@ Then: ``` ./build/wss_examples ``` + + diff --git a/sample/readme.md b/sample/README.md similarity index 80% rename from sample/readme.md rename to sample/README.md index 38ea148b..587d35d6 100644 --- a/sample/readme.md +++ b/sample/README.md @@ -26,6 +26,8 @@ Run one server and as many clients as you like. Type the letter for the desired ## Building +The sample uses [Simple-WebSocket-Server](../README.md) (duh). You'll need its dependencies installed. + #### Windows @@ -39,7 +41,14 @@ Populate the following environmentla variables Specify the correct generator in your call to cmake - mkdir /build + mkdir build cd build cmake .. -G "Visual Studio 15 2017 Win64" +Open `build/Simple_WebSocket_Sample.sln` and build it + +#### Linux + + mkdir build + cd build + cmake .. diff --git a/sample/sample_client.cpp b/sample/sample_client.cpp index 29cba0b9..e0f50c05 100644 --- a/sample/sample_client.cpp +++ b/sample/sample_client.cpp @@ -31,30 +31,30 @@ int main(int, char**) client->on_open = [&](shared_ptr connection) { _connection = connection; - cout << "Client Started & Connection " << connection << " Opened" << endl; + cout << "Client Started & Connection " << (size_t)connection.get() << " Opened" << endl << endl; }; client->on_close = [&](shared_ptr connection, int code, const string& reason) { _connection = nullptr; - cout << "Closed Connection" << connection << " with code: " << code << endl << "Reason: " << reason << endl; + cout << "Closed Connection " << (size_t)connection.get() << "(" << code << ")" << endl << " Reason: " << reason << endl << endl; }; client->on_error = [](shared_ptr connection, const boost::system::error_code& code) { - cout << "Error (code " << code << ")" << endl << "Message: " << code.message() << endl; + cout << "Error in Connection " << (size_t)connection.get() << "(" << code << ")" << endl << " Code: " << code.message() << endl << endl; }; client->on_message = [](shared_ptr connection, shared_ptr message) { - cout << "Server Message: " << message->string() << endl; + cout << "Server Message on Connection " << (size_t)connection.get() << endl << " Message: " << message->string() << endl << endl; }; client_thread = boost::thread([&client]() { client->start(); }); - cout << "Connection started" << endl; + cout << "Connection started" << endl << endl; } else if (line == "c") { @@ -62,11 +62,11 @@ int main(int, char**) { client->stop(); client = nullptr; - cout << "Stopped Client" << endl; + cout << "Stopped Client" << endl << endl; } else { - cout << "Client Already Stopped" << endl; + cout << "Client Already Stopped" << endl << endl; } } @@ -79,11 +79,11 @@ int main(int, char**) cout << "Error on send_close Code: " << code << " Message: " << code.message() << endl; }); - cout << "Closed connection with message" << endl; + cout << "Closed connection " << (size_t)_connection.get() << " with message" << endl << endl; } else { - cout << "Connection already closed" << endl; + cout << "Connection already closed" << endl << endl; } } @@ -92,7 +92,7 @@ int main(int, char**) auto msg = std::make_shared(); *msg << "It's tricky to rock a rhyme to rock a rhyme that's right on time it's tricky!"; _connection->send(msg); - cout << "Message sent" << endl; + cout << "Message sent" << endl << endl; } } if (client != nullptr) diff --git a/sample/sample_server.cpp b/sample/sample_server.cpp index 20b665ad..0203bd40 100644 --- a/sample/sample_server.cpp +++ b/sample/sample_server.cpp @@ -32,7 +32,8 @@ int main(int, char**) tunnel.on_message = [&server](shared_ptr connection, shared_ptr message) { auto message_str = message->string(); - cout << "Client Message: " << message_str << endl; + cout << "Client Message from connection " << (size_t)connection.get() << endl + << " Message: " << message_str << endl << endl; auto sendThisBack = make_shared(); *sendThisBack << "[echo] " << message_str; @@ -40,31 +41,29 @@ int main(int, char**) { if (code) { - cout << "Error while responding: " << code << ", error message: " << code.message() << endl; + cout << " Error while responding: " << code << ", error message: " << code.message() << endl << endl; } }); }; tunnel.on_open = [](shared_ptr connection) { - cout << "Opened Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address - << ":" << connection->remote_endpoint_port << endl; + cout << "Opened Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address << ":" << connection->remote_endpoint_port << endl << endl; }; tunnel.on_close = [](shared_ptr connection, int status, const string& reason) { - cout << "Closed Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address - << ":" << connection->remote_endpoint_port << endl; + cout << "Closed Connection: " << (size_t)connection.get() << " from: " << connection->remote_endpoint_address << ":" << connection->remote_endpoint_port << endl; //See RFC 6455 7.4.1. for status codes - cout << "ConnectsTo code: " << status << " Reason: " << reason << endl; + cout << " Code: " << status << " Reason: " << reason << endl << endl; }; tunnel.on_error = [](shared_ptr connection, const boost::system::error_code& code) { //See http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference.html, Error Codes for error code meanings - cout << "Error in connection " << (size_t)connection.get() << ". " << "Error: " << code - << ", error message: " << code.message() << endl; + cout << "Error in connection " << (size_t)connection.get() << endl; + cout << " Code: " << code << ", Message: " << code.message() << endl << endl; }; server_thread = thread([&server]() @@ -88,7 +87,7 @@ int main(int, char**) i++; auto msg = make_shared(); *msg << "This is for the kids whippin' up some home-cook, spittin' 86 bars f'n no hook.."; - cout << "sending message..."; + cout << "Sending Message..." << endl; connection->send(msg, [&](const boost::system::error_code code) { if (code) @@ -97,7 +96,7 @@ int main(int, char**) << " Message: " << code.message() << endl; } }); - cout << "sent" << endl; + cout << " ...sent" << endl; } } } From b9e72278e8775a95a676607f6750d0ded507e506 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 12:34:28 -0600 Subject: [PATCH 08/17] silenced a gcc warning about conversion from unsigned char to int --- client_ws.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client_ws.hpp b/client_ws.hpp index a720346d..d93d3a17 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -199,11 +199,11 @@ namespace SimpleWeb { size_t num_bytes; if(length > 0xffff) { num_bytes = 8; - send_stream->put(static_cast(/*127 + 128*/ 0xFEu)); + send_stream->put(static_cast(/*127 + 128*/ 0xFEu)); } else { num_bytes = 2; - send_stream->put(static_cast(/*126 + 128*/ 0xFEu)); + send_stream->put(static_cast(/*126 + 128*/ 0xFEu)); } for(size_t c = num_bytes - 1; c != static_cast(-1); c--) From c96f03826b85866738c0dc7545f226d8149aa929 Mon Sep 17 00:00:00 2001 From: MatrixManAtYrService Date: Thu, 28 Sep 2017 12:38:06 -0600 Subject: [PATCH 09/17] Readme formatting --- sample/README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sample/README.md b/sample/README.md index 587d35d6..130b9d67 100644 --- a/sample/README.md +++ b/sample/README.md @@ -1,24 +1,24 @@ Simple-WebSocket-Sample ======================= -This sample project contains two executables which allow the user to control the connections and message flow for Simple-WebSocket-Server. It might be useful for testing interoperability with other websockets implementations. +This project contains two executables which allow the user to control the connections and message flow for Simple-WebSocket-Server. It might be useful for testing interoperability with other websockets implementations. The following actions are supported. -### sample_server +### sample_server controls - s : Start server - t : sTop server - m : send Message to all clients - q : Quit + s : Start server + t : sTop server + m : send Message to all clients + q : Quit -### sample_client +### sample_client controls - s : Set up connection - l : cLose connection - c : stop Client - m : send Message - q : Quit + s : Set up connection + l : cLose connection + c : stop Client + m : send Message + q : Quit ## Usage From 9c246bd99ce4302f3b790c6f7b8612f213a0eae9 Mon Sep 17 00:00:00 2001 From: MatrixManAtYrService Date: Thu, 28 Sep 2017 12:46:54 -0600 Subject: [PATCH 10/17] More documentation --- sample/README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/sample/README.md b/sample/README.md index 130b9d67..9c409d2b 100644 --- a/sample/README.md +++ b/sample/README.md @@ -3,8 +3,6 @@ Simple-WebSocket-Sample This project contains two executables which allow the user to control the connections and message flow for Simple-WebSocket-Server. It might be useful for testing interoperability with other websockets implementations. -The following actions are supported. - ### sample_server controls s : Start server @@ -22,8 +20,19 @@ The following actions are supported. ## Usage -Run one server and as many clients as you like. Type the letter for the desired action and hit enter. - +Run one server and as many clients as you like. Type the letter for the desired action and hit enter. A typical session might look like this: + +| sample_client | sample_server | Effect | +| :-----------: | :------------:|:-------| +| | **S**tart | The server starts listening for connections | +| **S**tart | | The client connects to the server | +| **M**essage | | The client sends a message to the server (the server will respond with an echo) | +| | **M**essage | The server sends a message to all connected clients (they will not respond) | +| c**L**ose | | The client disconnects with a message | +| | s**T**op | The server stops listening | +| s**T**op | | The client cleans itself up | +| **Q**uit | | The client quits | +| | **Q**uit | The server quits | ## Building The sample uses [Simple-WebSocket-Server](../README.md) (duh). You'll need its dependencies installed. @@ -31,7 +40,7 @@ The sample uses [Simple-WebSocket-Server](../README.md) (duh). You'll need its #### Windows -Populate the following environmentla variables +Populate the following environmentla variables: | variable | value | |:--|:--| @@ -39,13 +48,13 @@ Populate the following environmentla variables | BoostVer | 1_62 | | OpenSSLRoot | C:\path\to\OpenSSL | -Specify the correct generator in your call to cmake +Specify the correct generator in your call to cmake, this example uses 2017 with a 64 bit build: mkdir build cd build cmake .. -G "Visual Studio 15 2017 Win64" -Open `build/Simple_WebSocket_Sample.sln` and build it +Open in your IDE of choice `build/Simple_WebSocket_Sample.sln` and build it. #### Linux From 988d57e6540802b9f939ff596ac8d8caddb66af5 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 13:16:13 -0600 Subject: [PATCH 11/17] adding Sec-WebSocket-Protocol support --- client_ws.hpp | 9 +++++++++ sample/sample_client.cpp | 4 +++- server_ws.hpp | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/client_ws.hpp b/client_ws.hpp index d93d3a17..66550819 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -333,6 +333,8 @@ namespace SimpleWeb { unsigned short port; std::string path; + std::string protocol = ""; + std::shared_ptr _connection; std::mutex connection_mutex; @@ -387,6 +389,8 @@ namespace SimpleWeb { auto nonce_base64 = std::make_shared(Crypto::Base64::encode(nonce)); request << "Sec-WebSocket-Key: " << *nonce_base64 << "\r\n"; request << "Sec-WebSocket-Version: 13\r\n"; + if (protocol != "") + request << "Sec-WebSocket-Protocol: " << protocol << "\r\n"; request << "\r\n"; connection->message = std::shared_ptr(new Message()); @@ -587,6 +591,11 @@ namespace SimpleWeb { public: SocketClient(const std::string &server_port_path) noexcept : SocketClientBase::SocketClientBase(server_port_path, 80){}; + void SetProtocol(std::string theProtocol) + { + SocketClientBase::protocol = theProtocol; + } + protected: void connect() override { std::unique_lock lock(connection_mutex); diff --git a/sample/sample_client.cpp b/sample/sample_client.cpp index e0f50c05..73757b5e 100644 --- a/sample/sample_client.cpp +++ b/sample/sample_client.cpp @@ -26,7 +26,9 @@ int main(int, char**) if (line == "s") { - client = std::make_shared("localhost:8081/some/http/resource"); + client = std::make_shared("172.20.110.36:9500"); + + client->SetProtocol("sky-controller"); // optional client->on_open = [&](shared_ptr connection) { diff --git a/server_ws.hpp b/server_ws.hpp index 0bc2bdb5..2d52fcfd 100644 --- a/server_ws.hpp +++ b/server_ws.hpp @@ -149,10 +149,25 @@ namespace SimpleWeb { static auto ws_magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; auto sha1 = Crypto::sha1(header_it->second + ws_magic_string); + auto proto_it = header.find("Sec-WebSocket-Key"); + + bool protocolSpecified = false; + std::string protocol; + proto_it = header.find("Sec-WebSocket-Protocol"); + if (proto_it != header.end()) + { + protocolSpecified = true; + protocol = proto_it->second; + } + handshake << "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"; handshake << "Upgrade: websocket\r\n"; handshake << "Connection: Upgrade\r\n"; handshake << "Sec-WebSocket-Accept: " << Crypto::Base64::encode(sha1) << "\r\n"; + if (protocolSpecified) + { + handshake << "Sec-WebSocket-Protocol: " << protocol << "\r\n"; + } handshake << "\r\n"; return true; From e2a7650bed940013361b1f55c8ba18a1c4d45f2b Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 15:49:05 -0600 Subject: [PATCH 12/17] Now accepting 101 headers from the server for the handshake response whether they say "Web Socket Protocol Handshake" or "Switching Protocols" or anything else. --- client_ws.hpp | 2 +- sample/sample_client.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client_ws.hpp b/client_ws.hpp index 66550819..b338c346 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -410,7 +410,7 @@ namespace SimpleWeb { return; if(!ec) { if(!ResponseMessage::parse(*connection->message, connection->http_version, connection->status_code, connection->header) || - connection->status_code != "101 Web Socket Protocol Handshake") { + connection->status_code.substr(0, 3) != "101") { this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error)); return; } diff --git a/sample/sample_client.cpp b/sample/sample_client.cpp index 73757b5e..3c93e836 100644 --- a/sample/sample_client.cpp +++ b/sample/sample_client.cpp @@ -26,7 +26,8 @@ int main(int, char**) if (line == "s") { - client = std::make_shared("172.20.110.36:9500"); + client = std::make_shared("localhost:8081/some/http/resource"); + client->SetProtocol("sky-controller"); // optional From 7a4cc3dd1e83eca7953be8a99cb03e882af89cc9 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 16:08:50 -0600 Subject: [PATCH 13/17] generalizing protocol string --- sample/sample_client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/sample_client.cpp b/sample/sample_client.cpp index 3c93e836..2a9b78e2 100644 --- a/sample/sample_client.cpp +++ b/sample/sample_client.cpp @@ -29,7 +29,7 @@ int main(int, char**) client = std::make_shared("localhost:8081/some/http/resource"); - client->SetProtocol("sky-controller"); // optional + client->SetProtocol("some-protocol"); // optional client->on_open = [&](shared_ptr connection) { From 6c0bffe609e08b8595f40bf7316e441a991e1abe Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 16:46:03 -0600 Subject: [PATCH 14/17] silencing visual studio error C4458: declaration of connection hides class member --- client_ws.hpp | 180 +++++++++++++++++++++--------------------- client_wss.hpp | 36 ++++----- sample/CMakeLists.txt | 12 +++ 3 files changed, 120 insertions(+), 108 deletions(-) diff --git a/client_ws.hpp b/client_ws.hpp index b338c346..33514028 100644 --- a/client_ws.hpp +++ b/client_ws.hpp @@ -310,8 +310,8 @@ namespace SimpleWeb { void stop() noexcept { { std::unique_lock lock(connection_mutex); - if(_connection) - _connection->close(); + if(connection) + connection->close(); } if(internal_io_service) @@ -335,7 +335,7 @@ namespace SimpleWeb { std::string protocol = ""; - std::shared_ptr _connection; + std::shared_ptr connection; std::mutex connection_mutex; std::shared_ptr handler_runner; @@ -365,8 +365,8 @@ namespace SimpleWeb { virtual void connect() = 0; - void handshake(const std::shared_ptr &connection) { - connection->read_remote_endpoint_data(); + void handshake(const std::shared_ptr &new_connection) { + new_connection->read_remote_endpoint_data(); auto write_buffer = std::make_shared(); @@ -393,67 +393,67 @@ namespace SimpleWeb { request << "Sec-WebSocket-Protocol: " << protocol << "\r\n"; request << "\r\n"; - connection->message = std::shared_ptr(new Message()); + new_connection->message = std::shared_ptr(new Message()); - connection->set_timeout(config.timeout_request); - asio::async_write(*connection->socket, *write_buffer, [this, connection, write_buffer, nonce_base64](const error_code &ec, size_t /*bytes_transferred*/) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + new_connection->set_timeout(config.timeout_request); + asio::async_write(*new_connection->socket, *write_buffer, [this, new_connection, write_buffer, nonce_base64](const error_code &ec, size_t /*bytes_transferred*/) { + new_connection->cancel_timeout(); + auto lock = new_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { - connection->set_timeout(this->config.timeout_request); - asio::async_read_until(*connection->socket, connection->message->streambuf, "\r\n\r\n", [this, connection, nonce_base64](const error_code &ec, size_t /*bytes_transferred*/) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + new_connection->set_timeout(this->config.timeout_request); + asio::async_read_until(*new_connection->socket, new_connection->message->streambuf, "\r\n\r\n", [this, new_connection, nonce_base64](const error_code &ec, size_t /*bytes_transferred*/) { + new_connection->cancel_timeout(); + auto lock = new_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { - if(!ResponseMessage::parse(*connection->message, connection->http_version, connection->status_code, connection->header) || - connection->status_code.substr(0, 3) != "101") { - this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error)); + if(!ResponseMessage::parse(*new_connection->message, new_connection->http_version, new_connection->status_code, new_connection->header) || + new_connection->status_code.substr(0, 3) != "101") { + this->connection_error(new_connection, make_error_code::make_error_code(errc::protocol_error)); return; } - auto header_it = connection->header.find("Sec-WebSocket-Accept"); + auto header_it = new_connection->header.find("Sec-WebSocket-Accept"); static auto ws_magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - if(header_it != connection->header.end() && + if(header_it != new_connection->header.end() && Crypto::Base64::decode(header_it->second) == Crypto::sha1(*nonce_base64 + ws_magic_string)) { - this->connection_open(connection); - read_message(connection); + this->connection_open(new_connection); + read_message(new_connection); } else - this->connection_error(connection, make_error_code::make_error_code(errc::protocol_error)); + this->connection_error(new_connection, make_error_code::make_error_code(errc::protocol_error)); } else - this->connection_error(connection, ec); + this->connection_error(new_connection, ec); }); } else - this->connection_error(connection, ec); + this->connection_error(new_connection, ec); }); } - void read_message(const std::shared_ptr &connection) { - asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(2), [this, connection](const error_code &ec, size_t bytes_transferred) { - auto lock = connection->handler_runner->continue_lock(); + void read_message(const std::shared_ptr &msg_connection) { + asio::async_read(*msg_connection->socket, msg_connection->message->streambuf, asio::transfer_exactly(2), [this, msg_connection](const error_code &ec, size_t bytes_transferred) { + auto lock = msg_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { if(bytes_transferred == 0) { // TODO: This might happen on server at least, might also happen here - this->read_message(connection); + this->read_message(msg_connection); return; } std::vector first_bytes; first_bytes.resize(2); - connection->message->read(reinterpret_cast(&first_bytes[0]), 2); + msg_connection->message->read(reinterpret_cast(&first_bytes[0]), 2); - connection->message->fin_rsv_opcode = first_bytes[0]; + msg_connection->message->fin_rsv_opcode = first_bytes[0]; // Close connection if masked message from server (protocol error) if(first_bytes[1] >= 128) { const std::string reason("message from server masked"); - connection->send_close(1002, reason); - this->connection_close(connection, 1002, reason); + msg_connection->send_close(1002, reason); + this->connection_close(msg_connection, 1002, reason); return; } @@ -461,123 +461,123 @@ namespace SimpleWeb { if(length == 126) { // 2 next bytes is the size of content - asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(2), [this, connection](const error_code &ec, size_t /*bytes_transferred*/) { - auto lock = connection->handler_runner->continue_lock(); + asio::async_read(*msg_connection->socket, msg_connection->message->streambuf, asio::transfer_exactly(2), [this, msg_connection](const error_code &ec, size_t /*bytes_transferred*/) { + auto lock = msg_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { std::vector length_bytes; length_bytes.resize(2); - connection->message->read(reinterpret_cast(&length_bytes[0]), 2); + msg_connection->message->read(reinterpret_cast(&length_bytes[0]), 2); size_t length = 0; size_t num_bytes = 2; for(size_t c = 0; c < num_bytes; c++) length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); - connection->message->length = length; - this->read_message_content(connection); + msg_connection->message->length = length; + this->read_message_content(msg_connection); } else - this->connection_error(connection, ec); + this->connection_error(msg_connection, ec); }); } else if(length == 127) { // 8 next bytes is the size of content - asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(8), [this, connection](const error_code &ec, size_t /*bytes_transferred*/) { - auto lock = connection->handler_runner->continue_lock(); + asio::async_read(*msg_connection->socket, msg_connection->message->streambuf, asio::transfer_exactly(8), [this, msg_connection](const error_code &ec, size_t /*bytes_transferred*/) { + auto lock = msg_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { std::vector length_bytes; length_bytes.resize(8); - connection->message->read(reinterpret_cast(&length_bytes[0]), 8); + msg_connection->message->read(reinterpret_cast(&length_bytes[0]), 8); size_t length = 0; size_t num_bytes = 8; for(size_t c = 0; c < num_bytes; c++) length += static_cast(length_bytes[c]) << (8 * (num_bytes - 1 - c)); - connection->message->length = length; - this->read_message_content(connection); + msg_connection->message->length = length; + this->read_message_content(msg_connection); } else - this->connection_error(connection, ec); + this->connection_error(msg_connection, ec); }); } else { - connection->message->length = length; - this->read_message_content(connection); + msg_connection->message->length = length; + this->read_message_content(msg_connection); } } else - this->connection_error(connection, ec); + this->connection_error(msg_connection, ec); }); } - void read_message_content(const std::shared_ptr &connection) { - asio::async_read(*connection->socket, connection->message->streambuf, asio::transfer_exactly(connection->message->length), [this, connection](const error_code &ec, size_t /*bytes_transferred*/) { - auto lock = connection->handler_runner->continue_lock(); + void read_message_content(const std::shared_ptr &msg_connection) { + asio::async_read(*msg_connection->socket, msg_connection->message->streambuf, asio::transfer_exactly(msg_connection->message->length), [this, msg_connection](const error_code &ec, size_t /*bytes_transferred*/) { + auto lock = msg_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { // If connection close - if((connection->message->fin_rsv_opcode & 0x0f) == 8) { + if((msg_connection->message->fin_rsv_opcode & 0x0f) == 8) { int status = 0; - if(connection->message->length >= 2) { - unsigned char byte1 = static_cast(connection->message->get()); - unsigned char byte2 = static_cast(connection->message->get()); + if(msg_connection->message->length >= 2) { + unsigned char byte1 = static_cast(msg_connection->message->get()); + unsigned char byte2 = static_cast(msg_connection->message->get()); status = (byte1 << 8) + byte2; } - auto reason = connection->message->string(); - connection->send_close(status, reason); - this->connection_close(connection, status, reason); + auto reason = msg_connection->message->string(); + msg_connection->send_close(status, reason); + this->connection_close(msg_connection, status, reason); return; } // If ping - else if((connection->message->fin_rsv_opcode & 0x0f) == 9) { + else if((msg_connection->message->fin_rsv_opcode & 0x0f) == 9) { // Send pong auto empty_send_stream = std::make_shared(); - connection->send(empty_send_stream, nullptr, connection->message->fin_rsv_opcode + 1); + msg_connection->send(empty_send_stream, nullptr, msg_connection->message->fin_rsv_opcode + 1); } else if(this->on_message) { - connection->cancel_timeout(); - connection->set_timeout(); - this->on_message(connection, connection->message); + msg_connection->cancel_timeout(); + msg_connection->set_timeout(); + this->on_message(msg_connection, msg_connection->message); } // Next message - connection->message = std::shared_ptr(new Message()); - this->read_message(connection); + msg_connection->message = std::shared_ptr(new Message()); + this->read_message(msg_connection); } else - this->connection_error(connection, ec); + this->connection_error(msg_connection, ec); }); } - void connection_open(const std::shared_ptr &connection) const { - connection->cancel_timeout(); - connection->set_timeout(); + void connection_open(const std::shared_ptr &opening_connection) const { + opening_connection->cancel_timeout(); + opening_connection->set_timeout(); if(on_open) - on_open(connection); + on_open(opening_connection); } - void connection_close(const std::shared_ptr &connection, int status, const std::string &reason) const { - connection->cancel_timeout(); - connection->set_timeout(); + void connection_close(const std::shared_ptr &closing_connection, int status, const std::string &reason) const { + closing_connection->cancel_timeout(); + closing_connection->set_timeout(); if(on_close) - on_close(connection, status, reason); + on_close(closing_connection, status, reason); } - void connection_error(const std::shared_ptr &connection, const error_code &ec) const { - connection->cancel_timeout(); - connection->set_timeout(); + void connection_error(const std::shared_ptr &err_connection, const error_code &ec) const { + err_connection->cancel_timeout(); + err_connection->set_timeout(); if(on_error) - on_error(connection, ec); + on_error(err_connection, ec); } }; @@ -599,35 +599,35 @@ namespace SimpleWeb { protected: void connect() override { std::unique_lock lock(connection_mutex); - auto connection = this->_connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service)); + auto newConnection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service)); lock.unlock(); asio::ip::tcp::resolver::query query(host, std::to_string(port)); auto resolver = std::make_shared(*io_service); - connection->set_timeout(config.timeout_request); - resolver->async_resolve(query, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + newConnection->set_timeout(config.timeout_request); + resolver->async_resolve(query, [this, newConnection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { + newConnection->cancel_timeout(); + auto lock = newConnection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { - connection->set_timeout(this->config.timeout_request); - asio::async_connect(*connection->socket, it, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + newConnection->set_timeout(this->config.timeout_request); + asio::async_connect(*newConnection->socket, it, [this, newConnection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { + newConnection->cancel_timeout(); + auto lock = newConnection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { asio::ip::tcp::no_delay option(true); - connection->socket->set_option(option); + newConnection->socket->set_option(option); - this->handshake(connection); + this->handshake(newConnection); } else - this->connection_error(connection, ec); + this->connection_error(newConnection, ec); }); } else - this->connection_error(connection, ec); + this->connection_error(newConnection, ec); }); } }; diff --git a/client_wss.hpp b/client_wss.hpp index bf27841e..b1884261 100644 --- a/client_wss.hpp +++ b/client_wss.hpp @@ -47,43 +47,43 @@ namespace SimpleWeb { connection_lock.unlock(); asio::ip::tcp::resolver::query query(host, std::to_string(port)); auto resolver = std::make_shared(*io_service); - connection->set_timeout(config.timeout_request); - resolver->async_resolve(query, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + new_connection->set_timeout(config.timeout_request); + resolver->async_resolve(query, [this, new_connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { + new_connection->cancel_timeout(); + auto lock = new_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { - connection->set_timeout(this->config.timeout_request); - asio::async_connect(connection->socket->lowest_layer(), it, [this, connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + new_connection->set_timeout(this->config.timeout_request); + asio::async_connect(new_connection->socket->lowest_layer(), it, [this, new_connection, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { + new_connection->cancel_timeout(); + auto lock = new_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) { asio::ip::tcp::no_delay option(true); - connection->socket->lowest_layer().set_option(option); + new_connection->socket->lowest_layer().set_option(option); - SSL_set_tlsext_host_name(connection->socket->native_handle(), this->host.c_str()); + SSL_set_tlsext_host_name(new_connection->socket->native_handle(), this->host.c_str()); - connection->set_timeout(this->config.timeout_request); - connection->socket->async_handshake(asio::ssl::stream_base::client, [this, connection](const error_code &ec) { - connection->cancel_timeout(); - auto lock = connection->handler_runner->continue_lock(); + new_connection->set_timeout(this->config.timeout_request); + new_connection->socket->async_handshake(asio::ssl::stream_base::client, [this, new_connection](const error_code &ec) { + new_connection->cancel_timeout(); + auto lock = new_connection->handler_runner->continue_lock(); if(!lock) return; if(!ec) - handshake(connection); + handshake(new_connection); else - this->connection_error(connection, ec); + this->connection_error(new_connection, ec); }); } else - this->connection_error(connection, ec); + this->connection_error(new_connection, ec); }); } else - this->connection_error(connection, ec); + this->connection_error(new_connection, ec); }); } }; diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 2c733e41..6fdba2cc 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -74,7 +74,19 @@ target_link_libraries(sample_server ${Boost_LIBRARIES}) target_link_libraries(sample_server ${OPENSSL_LIBRARIES}) target_link_libraries(sample_server ${CMAKE_THREAD_LIBS_INIT}) +add_executable(ws_examples ../ws_examples.cpp) +target_link_libraries(ws_examples ${Boost_LIBRARIES}) +target_link_libraries(ws_examples ${OPENSSL_CRYPTO_LIBRARY}) +target_link_libraries(ws_examples ${CMAKE_THREAD_LIBS_INIT}) + +add_executable(wss_examples ../wss_examples.cpp) +target_link_libraries(wss_examples ${Boost_LIBRARIES}) +target_link_libraries(wss_examples ${OPENSSL_LIBRARIES}) +target_link_libraries(wss_examples ${CMAKE_THREAD_LIBS_INIT}) + if(MSYS) target_link_libraries(sample_client ws2_32 wsock32) target_link_libraries(sample_server ws2_32 wsock32) + target_link_libraries(ws_examples ws2_32 wsock32) + target_link_libraries(wss_examples ws2_32 wsock32) endif() From d4375c99fe2152500932fa140484ea2757dfab25 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 16:46:38 -0600 Subject: [PATCH 15/17] more for C4458 --- client_wss.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client_wss.hpp b/client_wss.hpp index b1884261..9dbca043 100644 --- a/client_wss.hpp +++ b/client_wss.hpp @@ -43,7 +43,7 @@ namespace SimpleWeb { void connect() override { std::unique_lock connection_lock(connection_mutex); - auto connection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service, context)); + auto new_connection = this->connection = std::shared_ptr(new Connection(handler_runner, config.timeout_idle, *io_service, context)); connection_lock.unlock(); asio::ip::tcp::resolver::query query(host, std::to_string(port)); auto resolver = std::make_shared(*io_service); From 56c09b0df7f05f18af916e5e8f9ccde10a29f683 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Thu, 28 Sep 2017 16:50:18 -0600 Subject: [PATCH 16/17] silencing visual studio warning C4297: conversion from size_t to int --- server_wss.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server_wss.hpp b/server_wss.hpp index 6b7a9e79..e15e65be 100644 --- a/server_wss.hpp +++ b/server_wss.hpp @@ -40,7 +40,7 @@ namespace SimpleWeb { session_id_context = std::to_string(config.port) + ':'; session_id_context.append(config.address.rbegin(), config.address.rend()); SSL_CTX_set_session_id_context(context.native_handle(), reinterpret_cast(session_id_context.data()), - std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH)); + static_cast(std::min(session_id_context.size(), SSL_MAX_SSL_SESSION_ID_LENGTH))); } SocketServerBase::start(); } From cf32b7adbdb4fc2b0fb745998009ecf6abb516c9 Mon Sep 17 00:00:00 2001 From: "M@ Rixman" Date: Fri, 29 Sep 2017 11:56:54 -0600 Subject: [PATCH 17/17] generalized boost version variable --- sample/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 6fdba2cc..d8498a22 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -24,7 +24,7 @@ if (WIN32) # prereq: populate $env:BoostRoot and $env:BoostVer to tell cmake where boost is set(BOOST_ROOT "$ENV{BoostRoot}") set(BOOST_LIBRARYDIR "$ENV{BoostRoot}/lib") - set(BOOST_INCLUDEDIR "$ENV{BoostRoot}/include/boost-$ENV{SkyBoostVer}") + set(BOOST_INCLUDEDIR "$ENV{BoostRoot}/include/boost-$ENV{BoostVer}") add_definitions(-DBOOST_ALL_DYN_LINK) set(BOOST_COMPONENTS ${BOOST_COMPONENTS} regex)