net/Socket.hpp | 3 ++ net/WebSocketHandler.hpp | 59 +++++++++++++++++++++++++++++++++++++++++--- net/loolnb.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++--- wsd/LOOLWSD.cpp | 50 +++---------------------------------- 4 files changed, 121 insertions(+), 53 deletions(-)
New commits: commit 0aa1b7c468ad999688b8ea8b96f7b5baea2b13a7 Author: Jan Holesovsky <ke...@collabora.com> Date: Thu Mar 2 10:39:24 2017 +0100 nb: It seems uppercase 'S' is preferred in Sec-WebSocket-Accept. Change-Id: I6cc86f26cf7e3c9370e5d534877bfeeb6607f5a4 diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index 55f0048..feae544 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -334,7 +334,7 @@ protected: oss << "HTTP/1.1 101 Switching Protocols\r\n" << "Upgrade: websocket\r\n" << "Connection: Upgrade\r\n" - << "Sec-Websocket-Accept: " << PublicComputeAccept::doComputeAccept(wsKey) << "\r\n" + << "Sec-WebSocket-Accept: " << PublicComputeAccept::doComputeAccept(wsKey) << "\r\n" << "\r\n"; socket->send(oss.str()); commit 0f0fb31e98432a7cb99a81e30136b24a1f43441e Author: Jan Holesovsky <ke...@collabora.com> Date: Thu Mar 2 10:38:49 2017 +0100 nb: Make the loolnb / clientnb work again. The more testing the better :-) Change-Id: Ibe4249f18109d50b06e1fa35e6d0fef67f9b3643 diff --git a/net/Socket.hpp b/net/Socket.hpp index 55f264a..026a414 100644 --- a/net/Socket.hpp +++ b/net/Socket.hpp @@ -569,8 +569,11 @@ protected: std::mutex _writeMutex; // To be able to access _inBuffer and _outBuffer. + // TODO we probably need accessors to the _inBuffer & _outBuffer + // instead of this many friends... friend class WebSocketHandler; friend class ClientRequestDispatcher; + friend class SimpleResponseClient; }; namespace HttpHelper diff --git a/net/WebSocketHandler.hpp b/net/WebSocketHandler.hpp index b55c4f2..55f0048 100644 --- a/net/WebSocketHandler.hpp +++ b/net/WebSocketHandler.hpp @@ -14,12 +14,18 @@ #include "Log.hpp" #include "Socket.hpp" +#include <Poco/Net/HTTPRequest.h> +#include <Poco/Net/WebSocket.h> + class WebSocketHandler : public SocketHandlerInterface { +protected: // The socket that owns us (we can't own it). std::weak_ptr<StreamSocket> _socket; + std::vector<char> _wsPayload; bool _shuttingDown; + enum class WSState { HTTP, WS } _wsState; enum class WSFrameMask : unsigned char { @@ -27,16 +33,21 @@ class WebSocketHandler : public SocketHandlerInterface Mask = 0x80 }; + public: WebSocketHandler() : - _shuttingDown(false) + _shuttingDown(false), + _wsState(WSState::HTTP) { } - WebSocketHandler(const std::weak_ptr<StreamSocket>& socket) : - _shuttingDown(false) + /// Upgrades itself to a websocket directly. + WebSocketHandler(const std::weak_ptr<StreamSocket>& socket, const Poco::Net::HTTPRequest& request) : + _socket(socket), + _shuttingDown(false), + _wsState(WSState::HTTP) { - onConnect(socket); + upgradeToWebSocket(request); } /// Implementation of the SocketHandlerInterface. @@ -289,6 +300,46 @@ protected: virtual void handleMessage(bool /*fin*/, WSOpCode /*code*/, std::vector<char> &/*data*/) { } + +private: + /// To make the protected 'computeAccept' accessible. + class PublicComputeAccept : public Poco::Net::WebSocket + { + public: + static std::string doComputeAccept(const std::string &key) + { + return computeAccept(key); + } + }; + +protected: + /// Upgrade the http(s) connection to a websocket. + void upgradeToWebSocket(const Poco::Net::HTTPRequest& req) + { + LOG_TRC("Upgrading to WebSocket"); + assert(_wsState == WSState::HTTP); + + auto socket = _socket.lock(); + if (socket == nullptr) + throw std::runtime_error("Invalid socket while upgrading to WebSocket. Request: " + req.getURI()); + + // create our websocket goodness ... + const int wsVersion = std::stoi(req.get("Sec-WebSocket-Version", "13")); + const std::string wsKey = req.get("Sec-WebSocket-Key", ""); + const std::string wsProtocol = req.get("Sec-WebSocket-Protocol", "chat"); + // FIXME: other sanity checks ... + LOG_INF("WebSocket version " << wsVersion << " key '" << wsKey << "'."); + + std::ostringstream oss; + oss << "HTTP/1.1 101 Switching Protocols\r\n" + << "Upgrade: websocket\r\n" + << "Connection: Upgrade\r\n" + << "Sec-Websocket-Accept: " << PublicComputeAccept::doComputeAccept(wsKey) << "\r\n" + << "\r\n"; + + socket->send(oss.str()); + _wsState = WSState::WS; + } }; #endif diff --git a/net/loolnb.cpp b/net/loolnb.cpp index f568821..6623bc0 100644 --- a/net/loolnb.cpp +++ b/net/loolnb.cpp @@ -20,7 +20,6 @@ #include <Poco/MemoryStream.h> #include <Poco/Net/SocketAddress.h> -#include <Poco/Net/HTTPRequest.h> #include <Poco/Util/ServerApplication.h> #include <Poco/StringTokenizer.h> #include <Poco/Runnable.h> @@ -46,6 +45,63 @@ public: { } + virtual void handleIncomingMessage() override + { + LOG_TRC("incoming WebSocket message"); + if (_wsState == WSState::HTTP) + { + auto socket = _socket.lock(); + + int number = 0; + Poco::MemoryInputStream message(&socket->_inBuffer[0], socket->_inBuffer.size()); + Poco::Net::HTTPRequest req; + req.read(message); + + // if we succeeded - remove that from our input buffer + // FIXME: We should check if this is GET or POST. For GET, we only + // can have a single request (headers only). For POST, we can/should + // use Poco HTMLForm to parse the post message properly. + // Otherwise, we should catch exceptions from the previous read/parse + // and assume we don't have sufficient data, so we wait some more. + socket->_inBuffer.clear(); + + LOG_DBG("URI: " << req.getURI()); + + Poco::StringTokenizer tokens(req.getURI(), "/?"); + if (tokens.count() == 4) + { + std::string subpool = tokens[2]; + number = std::stoi(tokens[3]); + + // complex algorithmic core: + number = number + 1; + + std::string numberString = std::to_string(number); + std::ostringstream oss; + oss << "HTTP/1.1 200 OK\r\n" + << "Date: Once, Upon a time GMT\r\n" // Mon, 27 Jul 2009 12:28:53 GMT + << "Server: madeup string (Linux)\r\n" + << "Content-Length: " << numberString.size() << "\r\n" + << "Content-Type: text/plain\r\n" + << "Connection: Closed\r\n" + << "\r\n" + << numberString; + ; + std::string str = oss.str(); + socket->_outBuffer.insert(socket->_outBuffer.end(), str.begin(), str.end()); + return; + } + else if (tokens.count() == 2 && tokens[1] == "ws") + { + + upgradeToWebSocket(req); + return; + } + } + + WebSocketHandler::handleIncomingMessage(); + } + virtual void handleMessage(const bool fin, const WSOpCode code, std::vector<char> &data) override { std::cerr << "Message: fin? " << fin << " code " << code << " data size " << data.size(); @@ -182,7 +238,7 @@ public: { std::shared_ptr<Socket> create(const int fd) override { - return std::make_shared<StreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new SimpleResponseClient }); + return StreamSocket::create<StreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new SimpleResponseClient }); } }; @@ -191,7 +247,7 @@ public: { std::shared_ptr<Socket> create(const int fd) override { - return std::make_shared<SslStreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new SimpleResponseClient }); + return StreamSocket::create<SslStreamSocket>(fd, std::unique_ptr<SocketHandlerInterface>{ new SimpleResponseClient }); } }; diff --git a/wsd/LOOLWSD.cpp b/wsd/LOOLWSD.cpp index 73f5454..9229168 100644 --- a/wsd/LOOLWSD.cpp +++ b/wsd/LOOLWSD.cpp @@ -2580,8 +2580,7 @@ static std::shared_ptr<ClientSession> createNewClientSession(const WebSocketHand class ClientRequestDispatcher : public SocketHandlerInterface { public: - ClientRequestDispatcher() : - _wsState(WSState::HTTP) + ClientRequestDispatcher() { } @@ -3125,7 +3124,7 @@ private: LOG_INF("Client WS request" << request.getURI() << ", url: " << url); // First Upgrade. - WebSocketHandler ws = upgradeToWebSocket(request); + WebSocketHandler ws(_socket, request); if (_connectionNum > MAX_CONNECTIONS) { @@ -3206,7 +3205,8 @@ private: LOG_CHECK_RET(docBroker && "Null DocumentBroker instance", ); const auto docKey = docBroker->getDocKey(); - WebSocketHandler ws(_socket); + WebSocketHandler ws; + ws.onConnect(_socket); try { // Connection terminated. Destroy session. @@ -3281,54 +3281,12 @@ private: } } - /// Upgrade the http(s) connection to a websocket. - WebSocketHandler upgradeToWebSocket(const Poco::Net::HTTPRequest& req) - { - LOG_TRC("Upgrading to WebSocket"); - assert(_wsState == WSState::HTTP); - - auto socket = _socket.lock(); - if (socket == nullptr) - throw std::runtime_error("Invalid socket while upgrading to WebSocket. Request: " + req.getURI()); - - // create our websocket goodness ... - const int wsVersion = std::stoi(req.get("Sec-WebSocket-Version", "13")); - const std::string wsKey = req.get("Sec-WebSocket-Key", ""); - const std::string wsProtocol = req.get("Sec-WebSocket-Protocol", "chat"); - // FIXME: other sanity checks ... - LOG_INF("WebSocket version " << wsVersion << " key '" << wsKey << "'."); - - std::ostringstream oss; - oss << "HTTP/1.1 101 Switching Protocols\r\n" - << "Upgrade: websocket\r\n" - << "Connection: Upgrade\r\n" - << "Sec-Websocket-Accept: " << PublicComputeAccept::doComputeAccept(wsKey) << "\r\n" - << "\r\n"; - - socket->send(oss.str()); - _wsState = WSState::WS; - - // Create a WS wrapper to use for sending the client status. - return WebSocketHandler(socket); - } - - /// To make the protected 'computeAccept' accessible. - class PublicComputeAccept : public Poco::Net::WebSocket - { - public: - static std::string doComputeAccept(const std::string &key) - { - return computeAccept(key); - } - }; - private: // The socket that owns us (we can't own it). std::weak_ptr<StreamSocket> _socket; std::shared_ptr<ClientSession> _clientSession; std::string _id; size_t _connectionNum; - enum class WSState { HTTP, WS } _wsState; }; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits