From 8f542a6f116078d2bdcf27185ce2ec6c0a62e4eb Mon Sep 17 00:00:00 2001 From: lganzzzo Date: Mon, 14 Jan 2019 02:42:49 +0200 Subject: [PATCH] WebSocket API draft --- web/client/HttpRequestExecutor.cpp | 4 +- web/client/WebSocketConnector.cpp | 63 +++++++++++++++++ web/client/WebSocketConnector.hpp | 70 +++++++++++++++++++ web/protocol/http/incoming/Response.hpp | 13 +--- .../{Connector.cpp => Handshaker.cpp} | 28 ++++---- .../{Connector.hpp => Handshaker.hpp} | 21 +++--- web/server/WebSocketConnectionHandler.hpp | 2 - 7 files changed, 167 insertions(+), 34 deletions(-) create mode 100644 web/client/WebSocketConnector.cpp create mode 100644 web/client/WebSocketConnector.hpp rename web/protocol/websocket/{Connector.cpp => Handshaker.cpp} (80%) rename web/protocol/websocket/{Connector.hpp => Handshaker.hpp} (73%) diff --git a/web/client/HttpRequestExecutor.cpp b/web/client/HttpRequestExecutor.cpp index 584e26a2..198dcf26 100644 --- a/web/client/HttpRequestExecutor.cpp +++ b/web/client/HttpRequestExecutor.cpp @@ -127,7 +127,7 @@ HttpRequestExecutor::execute(const String& method, return Response::createShared(result.startingLine.statusCode, result.startingLine.description.toString(), - result.headers, bodyStream, m_bodyDecoder, connection); + result.headers, bodyStream, m_bodyDecoder); } @@ -214,7 +214,7 @@ oatpp::async::Action HttpRequestExecutor::executeAsync(oatpp::async::AbstractCor return _return(Response::createShared(result.startingLine.statusCode, result.startingLine.description.toString(), - result.headers, bodyStream, m_bodyDecoder, m_connection)); + result.headers, bodyStream, m_bodyDecoder)); } diff --git a/web/client/WebSocketConnector.cpp b/web/client/WebSocketConnector.cpp new file mode 100644 index 00000000..e816048f --- /dev/null +++ b/web/client/WebSocketConnector.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi, + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#include "WebSocketConnector.hpp" + +#include "oatpp/web/protocol/websocket/Handshaker.hpp" + +namespace oatpp { namespace web { namespace client { + +std::shared_ptr WebSocketConnector::connectAndHandshake(const oatpp::String& path) { + + auto connection = m_connectionProvider->getConnection(); + auto connectionHandle = std::make_shared(connection); + + protocol::websocket::Handshaker::Headers headers; + protocol::websocket::Handshaker::clientsideHandshake(headers); + + auto response = m_requestExecutor.execute("GET", path, headers, nullptr, connectionHandle); + auto res = protocol::websocket::Handshaker::clientsideConfirmHandshake(headers, response); + if(res == protocol::websocket::Handshaker::STATUS_OK) { + return connection; + } else if(res == protocol::websocket::Handshaker::STATUS_SERVER_ERROR) { + throw std::runtime_error("[oatpp::web::client::WebSocketConnector::connectAndHandshake()]: Server responded with invalid code"); + } else if(res == protocol::websocket::Handshaker::STATUS_SERVER_WRONG_KEY) { + throw std::runtime_error("[oatpp::web::client::WebSocketConnector::connectAndHandshake()]: Server wrong handshake key"); + } else if(res == protocol::websocket::Handshaker::STATUS_UNKNOWN_PROTOCOL_SUGGESTED) { + throw std::runtime_error("[oatpp::web::client::WebSocketConnector::connectAndHandshake()]: Server response contains unexpected headers"); + } else { + throw std::runtime_error("[oatpp::web::client::WebSocketConnector::connectAndHandshake()]: Unknown error"); + } + +} + +WebSocketConnector::WebSocket WebSocketConnector::connect(const oatpp::String& path) { + return WebSocket(connectAndHandshake(path), true); +} + +std::shared_ptr WebSocketConnector::connectShared(const oatpp::String& path) { + return std::make_shared(connectAndHandshake(path), true); +} + +}}} diff --git a/web/client/WebSocketConnector.hpp b/web/client/WebSocketConnector.hpp new file mode 100644 index 00000000..532101c8 --- /dev/null +++ b/web/client/WebSocketConnector.hpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * + * Project _____ __ ____ _ _ + * ( _ ) /__\ (_ _)_| |_ _| |_ + * )(_)( /(__)\ )( (_ _)(_ _) + * (_____)(__)(__)(__) |_| |_| + * + * + * Copyright 2018-present, Leonid Stryzhevskyi, + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ***************************************************************************/ + +#ifndef oatpp_web_client_WebSocketConnector_hpp +#define oatpp_web_client_WebSocketConnector_hpp + +#include "./HttpRequestExecutor.hpp" +#include "oatpp/web/protocol/websocket/WebSocket.hpp" + +namespace oatpp { namespace web { namespace client { + +class WebSocketConnector { +public: + typedef protocol::websocket::WebSocket WebSocket; + typedef oatpp::data::stream::IOStream IOStream; +private: + std::shared_ptr m_connectionProvider; + HttpRequestExecutor m_requestExecutor; +private: + std::shared_ptr connectAndHandshake(const oatpp::String& path); +public: + + WebSocketConnector(const std::shared_ptr& connectionProvider) + : m_connectionProvider(connectionProvider) + , m_requestExecutor(connectionProvider) + {} + +public: + + static std::shared_ptr createShared(const std::shared_ptr& connectionProvider) { + return std::make_shared(connectionProvider); + } + + /** + * Connect to server, do websocket-handshake and return WebSocket + * (Blocking call) + */ + WebSocket connect(const oatpp::String& path); + + /** + * Same as connect() but return std::shared_ptr + */ + std::shared_ptr connectShared(const oatpp::String& path); + +}; + +}}} + +#endif /* oatpp_web_client_WebSocketConnector_hpp */ diff --git a/web/protocol/http/incoming/Response.hpp b/web/protocol/http/incoming/Response.hpp index f0c998c9..8c6aaed3 100644 --- a/web/protocol/http/incoming/Response.hpp +++ b/web/protocol/http/incoming/Response.hpp @@ -54,14 +54,12 @@ public: const oatpp::String& statusDescription, const http::Protocol::Headers& headers, const std::shared_ptr& bodyStream, - const std::shared_ptr& bodyDecoder, - const std::shared_ptr& connection) + const std::shared_ptr& bodyDecoder) : m_statusCode(statusCode) , m_statusDescription(statusDescription) , m_headers(headers) , m_bodyStream(bodyStream) , m_bodyDecoder(bodyDecoder) - , m_connection(connection) {} public: @@ -69,9 +67,8 @@ public: const oatpp::String& statusDescription, const http::Protocol::Headers& headers, const std::shared_ptr& bodyStream, - const std::shared_ptr& bodyDecoder, - const std::shared_ptr& connection) { - return Shared_Incoming_Response_Pool::allocateShared(statusCode, statusDescription, headers, bodyStream, bodyDecoder, connection); + const std::shared_ptr& bodyDecoder) { + return Shared_Incoming_Response_Pool::allocateShared(statusCode, statusDescription, headers, bodyStream, bodyDecoder); } v_int32 getStatusCode() const { @@ -93,10 +90,6 @@ public: std::shared_ptr getBodyDecoder() const { return m_bodyDecoder; } - - std::shared_ptr getConnection() const { - return m_connection; - } void streamBody(const std::shared_ptr& toStream) const { m_bodyDecoder->decode(m_headers, m_bodyStream, toStream); diff --git a/web/protocol/websocket/Connector.cpp b/web/protocol/websocket/Handshaker.cpp similarity index 80% rename from web/protocol/websocket/Connector.cpp rename to web/protocol/websocket/Handshaker.cpp index 53217e57..5b544279 100644 --- a/web/protocol/websocket/Connector.cpp +++ b/web/protocol/websocket/Handshaker.cpp @@ -22,19 +22,19 @@ * ***************************************************************************/ -#include "Connector.hpp" +#include "Handshaker.hpp" #include "oatpp/algorithm/SHA1.hpp" #include "oatpp/encoding/Base64.hpp" namespace oatpp { namespace web { namespace protocol { namespace websocket { -const char* const Connector::MAGIC_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +const char* const Handshaker::MAGIC_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; -thread_local std::mt19937 Connector::RANDOM_GENERATOR (std::random_device{}()); -thread_local std::uniform_int_distribution Connector::RANDOM_DISTRIBUTION (0, 255); +thread_local std::mt19937 Handshaker::RANDOM_GENERATOR (std::random_device{}()); +thread_local std::uniform_int_distribution Handshaker::RANDOM_DISTRIBUTION (0, 255); -oatpp::String Connector::generateKey() { +oatpp::String Handshaker::generateKey() { v_int32 keySize = 16; oatpp::String key(keySize); for(v_int32 i = 0; i < keySize; i ++) { @@ -43,7 +43,7 @@ oatpp::String Connector::generateKey() { return oatpp::encoding::Base64::encode(key); } -oatpp::String Connector::getHeader(const Headers& headers, const oatpp::data::share::StringKeyLabelCI_FAST& key) { +oatpp::String Handshaker::getHeader(const Headers& headers, const oatpp::data::share::StringKeyLabelCI_FAST& key) { auto it = headers.find(key); if(it != headers.end()) { @@ -54,7 +54,7 @@ oatpp::String Connector::getHeader(const Headers& headers, const oatpp::data::sh } -std::shared_ptr Connector::serversideHandshake(const Headers& requestHeaders, const std::shared_ptr& connectionUpgradeHandler) { +std::shared_ptr Handshaker::serversideHandshake(const Headers& requestHeaders, const std::shared_ptr& connectionUpgradeHandler) { auto version = getHeader(requestHeaders, "Sec-WebSocket-Version"); auto upgrade = getHeader(requestHeaders, oatpp::web::protocol::http::Header::UPGRADE); @@ -89,14 +89,14 @@ std::shared_ptr Connector::serversideHandshake(cons } -void Connector::clientsideHandshake(Headers& requestHeaders) { +void Handshaker::clientsideHandshake(Headers& requestHeaders) { requestHeaders[oatpp::web::protocol::http::Header::UPGRADE] = "websocket"; requestHeaders[oatpp::web::protocol::http::Header::CONNECTION] = oatpp::web::protocol::http::Header::Value::CONNECTION_UPGRADE; requestHeaders["Sec-WebSocket-Version"] = "13"; requestHeaders["Sec-WebSocket-Key"] = generateKey(); } -std::shared_ptr Connector::clientConnect(const Headers& clientHandshakeHeaders, const std::shared_ptr& serverResponse) { +v_int32 Handshaker::clientsideConfirmHandshake(const Headers& clientHandshakeHeaders, const std::shared_ptr& serverResponse) { if(serverResponse->getStatusCode() == 101) { @@ -119,15 +119,19 @@ std::shared_ptr Connector::clientConnect(const Headers& clientHandsha auto clientWebsocketAccept = oatpp::encoding::Base64::encode(sha1.finalBinary()); if(clientWebsocketAccept == websocketAccept) { - return std::make_shared(serverResponse->getConnection(), true); + return STATUS_OK; + } else { + return STATUS_SERVER_WRONG_KEY; } + } else { + return STATUS_UNKNOWN_PROTOCOL_SUGGESTED; } + } else { + return STATUS_SERVER_ERROR; } - return nullptr; - } }}}} diff --git a/web/protocol/websocket/Connector.hpp b/web/protocol/websocket/Handshaker.hpp similarity index 73% rename from web/protocol/websocket/Connector.hpp rename to web/protocol/websocket/Handshaker.hpp index 5ea56550..8a2cdaf9 100644 --- a/web/protocol/websocket/Connector.hpp +++ b/web/protocol/websocket/Handshaker.hpp @@ -22,8 +22,8 @@ * ***************************************************************************/ -#ifndef oatpp_web_protocol_websocket_Connector_hpp -#define oatpp_web_protocol_websocket_Connector_hpp +#ifndef oatpp_web_protocol_websocket_Handshaker_hpp +#define oatpp_web_protocol_websocket_Handshaker_hpp #include "./WebSocket.hpp" @@ -36,18 +36,21 @@ namespace oatpp { namespace web { namespace protocol { namespace websocket { -class Connector { +class Handshaker { public: static const char* const MAGIC_UUID; +public: + static constexpr v_int32 STATUS_OK = 0; + static constexpr v_int32 STATUS_SERVER_ERROR = 1; // Server response-code != 101 + static constexpr v_int32 STATUS_SERVER_WRONG_KEY = 2; // Server response "Sec-WebSocket-Accept" header is wrong + static constexpr v_int32 STATUS_UNKNOWN_PROTOCOL_SUGGESTED = 3; // Server's response contains unexpected headers values private: /* Random used to generate "Sec-WebSocket-Key" */ static thread_local std::mt19937 RANDOM_GENERATOR; static thread_local std::uniform_int_distribution RANDOM_DISTRIBUTION; public: typedef oatpp::web::protocol::http::outgoing::Response OutgoingResponse; - typedef oatpp::web::protocol::http::outgoing::Request OutgoingRequest; typedef oatpp::web::protocol::http::incoming::Response IncomingResponse; - typedef oatpp::web::protocol::http::incoming::Request IncomingRequest; typedef oatpp::web::protocol::http::Protocol::Headers Headers; typedef oatpp::network::server::ConnectionHandler ConnectionHandler; private: @@ -66,12 +69,14 @@ public: static void clientsideHandshake(Headers& requestHeaders); /** - * + * Check if client's handshake corresponds to server's handshake + * returns one of values + * (STATUS_OK=0, STATUS_SERVER_ERROR=1, STATUS_SERVER_WRONG_KEY=2, STATUS_UNKNOWN_PROTOCOL_SUGGESTED=3); */ - static std::shared_ptr clientConnect(const Headers& clientHandshakeHeaders, const std::shared_ptr& serverResponse); + static v_int32 clientsideConfirmHandshake(const Headers& clientHandshakeHeaders, const std::shared_ptr& serverResponse); }; }}}} -#endif /* oatpp_web_protocol_websocket_Connector_hpp */ +#endif /* oatpp_web_protocol_websocket_Handshaker_hpp */ diff --git a/web/server/WebSocketConnectionHandler.hpp b/web/server/WebSocketConnectionHandler.hpp index 349ebbfe..223e8a9f 100644 --- a/web/server/WebSocketConnectionHandler.hpp +++ b/web/server/WebSocketConnectionHandler.hpp @@ -32,8 +32,6 @@ namespace oatpp { namespace web { namespace server { class WebSocketConnectionHandler : public oatpp::network::server::ConnectionHandler { public: - - void handleConnection(const std::shared_ptr& connection) override { }