/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "COOLWebSocket.hpp" #include #include using namespace COOLProtocol; using Poco::Net::AcceptCertificateHandler; using Poco::Net::Context; #if ENABLE_SSL using Poco::Net::HTTPSClientSession; #else using Poco::Net::HTTPClientSession; #endif using Poco::Net::HTTPRequest; using Poco::Net::HTTPResponse; using Poco::Net::InvalidCertificateHandler; using Poco::Net::SSLManager; using Poco::Net::WebSocket; using Poco::Net::WebSocketException; using Poco::Runnable; using Poco::SharedPtr; using Poco::TemporaryFile; using Poco::URI; using Poco::Util::Application; bool EnableExperimental = false; static bool closeExpected = false; static std::mutex coutMutex; constexpr auto Name = "connect "; /// Prints incoming data from a COOLWebSocket. class Output : public Runnable { public: Output(COOLWebSocket& ws) : _ws(ws) { } void run() override { int flags; int n; try { do { char buffer[100000]; n = _ws.receiveFrame(buffer, sizeof(buffer), flags, Name); if (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE) { { std::unique_lock lock(coutMutex); std::cout << "Got " << COOLWebSocket::getAbbreviatedFrameDump(buffer, n, flags) << std::endl; } std::string firstLine = getFirstLine(buffer, n); StringVector tokens(StringVector::tokenize(firstLine, ' ')); if (std::getenv("DISPLAY") != nullptr && tokens.equals(0, "tile:")) { TemporaryFile pngFile; std::ofstream pngStream(pngFile.path(), std::ios::binary); pngStream.write(buffer + firstLine.size() + 1, n - firstLine.size() - 1); pngStream.close(); if (std::system((std::string("display ") + pngFile.path()).c_str()) == -1) { // Not worth it to display a warning, this is just a throwaway test program, and // the developer running it surely notices if nothing shows up... } } } } while (n > 0 && (flags & WebSocket::FRAME_OP_BITMASK) != WebSocket::FRAME_OP_CLOSE); { std::unique_lock lock(coutMutex); std::cout << "CLOSE frame received" << std::endl; } if (!closeExpected) Util::forcedExit(EX_SOFTWARE); } catch (WebSocketException& exc) { std::unique_lock lock(coutMutex); std::cout << "Got exception " << exc.message() << std::endl; } } private: COOLWebSocket& _ws; }; /// Program for interactive or scripted testing of a cool server. class Connect: public Poco::Util::Application { public: Connect() : #if ENABLE_SSL _uri("https://127.0.0.1:" + std::to_string(DEFAULT_CLIENT_PORT_NUMBER) + "/ws") #else _uri("http://127.0.0.1:" + std::to_string(DEFAULT_CLIENT_PORT_NUMBER) + "/ws") #endif { } protected: int main(const std::vector& args) override { if (args.size() < 1) { LOG_ERR("Usage: connect documentURI [serverURI]"); return EX_USAGE; } if (args.size() > 1) _uri = URI(args[1]); #if ENABLE_SSL Poco::Net::initializeSSL(); SharedPtr invalidCertHandler = new AcceptCertificateHandler(false); Context::Params sslParams; Context::Ptr sslContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, sslParams); SSLManager::instance().initializeClient(nullptr, invalidCertHandler, sslContext); HTTPSClientSession cs(_uri.getHost(), _uri.getPort()); #else HTTPClientSession cs(_uri.getHost(), _uri.getPort()); #endif std::string encodedUri; URI::encode(args[0], ":/?", encodedUri); HTTPRequest request(HTTPRequest::HTTP_GET, "/cool/" + encodedUri + "/ws"); HTTPResponse response; COOLWebSocket ws(cs, request, response); ws.setReceiveTimeout(0); std::thread thread([&ws]{Output(ws).run();}); while (true) { std::string line; std::getline(std::cin, line); if (std::cin.eof()) { break; } else if (line.find("sleep ") == 0) { // Accept an input line "sleep " that makes us sleep a number of seconds. long sleepTime = std::stol(line.substr(std::string("sleep").length())); { std::unique_lock lock(coutMutex); std::cout << "Sleeping " << sleepTime << " seconds" << std::endl; } std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); } else if (line == "exit") { // While hacking on COOL and editing input files for this program back and forth it // is a good idea to be able to add an enforced exit in the middle of the input // file. { std::unique_lock lock(coutMutex); std::cout << "Exiting" << std::endl; } break; } else if (line.find('#') == 0) { // Also comments can be useful in input files for this program } else { { std::unique_lock lock(coutMutex); std::cout << "Sending: '" << line << '\'' << std::endl; } ws.sendFrame(line.c_str(), line.size()); } } { std::unique_lock lock(coutMutex); std::cout << "Shutting down websocket" << std::endl; } closeExpected = true; ws.shutdown(Name); thread.join(); return EX_OK; } private: URI _uri; }; namespace Util { void alertAllUsers(const std::string& cmd, const std::string& kind) { std::cout << "error: cmd=" << cmd << " kind=" << kind << std::endl; } } // coverity[root_function] : don't warn about uncaught exceptions POCO_APP_MAIN(Connect) /* vim:set shiftwidth=4 softtabstop=4 expandtab: */