diff options
author | Håvard Pettersen <havardpe@oath.com> | 2021-12-16 15:28:06 +0000 |
---|---|---|
committer | Håvard Pettersen <havardpe@oath.com> | 2021-12-16 15:38:14 +0000 |
commit | e6cf9db3f8815a1823dbf9233b70a10f8b1476c7 (patch) | |
tree | b98a20c4df52ec49fd566492f7928061162dbd04 /vespalib/src | |
parent | 9dd324c8de1a1662b2ee9e4bba2e0e273509d6df (diff) |
remove experimental websocket code
Diffstat (limited to 'vespalib/src')
26 files changed, 0 insertions, 1207 deletions
diff --git a/vespalib/src/tests/websocket/.gitignore b/vespalib/src/tests/websocket/.gitignore deleted file mode 100644 index 379d76b3ece..00000000000 --- a/vespalib/src/tests/websocket/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/websocket_server -vespalib_websocket_test_app -vespalib_websocket_server_app diff --git a/vespalib/src/tests/websocket/CMakeLists.txt b/vespalib/src/tests/websocket/CMakeLists.txt deleted file mode 100644 index 8581523a98e..00000000000 --- a/vespalib/src/tests/websocket/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -vespa_add_executable(vespalib_websocket_test_app TEST - SOURCES - websocket_test.cpp - DEPENDS - vespalib -) -vespa_add_test(NAME vespalib_websocket_test_app COMMAND vespalib_websocket_test_app) -vespa_add_executable(vespalib_websocket_server_app - SOURCES - websocket_server.cpp - DEPENDS - vespalib -) diff --git a/vespalib/src/tests/websocket/favicon.ico b/vespalib/src/tests/websocket/favicon.ico Binary files differdeleted file mode 100644 index 22f61482bd0..00000000000 --- a/vespalib/src/tests/websocket/favicon.ico +++ /dev/null diff --git a/vespalib/src/tests/websocket/index.html b/vespalib/src/tests/websocket/index.html deleted file mode 100644 index 144d42be14d..00000000000 --- a/vespalib/src/tests/websocket/index.html +++ /dev/null @@ -1,5 +0,0 @@ -<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> -<html> -<h1>Websocket server</h1> -<a href="test.html">run test</a> -</html> diff --git a/vespalib/src/tests/websocket/test.html b/vespalib/src/tests/websocket/test.html deleted file mode 100644 index f898cfb8da3..00000000000 --- a/vespalib/src/tests/websocket/test.html +++ /dev/null @@ -1,70 +0,0 @@ -<!DOCTYPE html> -<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> - -<meta charset="utf-8" /> - -<title>WebSocket Test</title> - -<script language="javascript" type="text/javascript"> - - var wsUri = "ws://[SELF]/echo"; - var output; - - function init() - { - output = document.getElementById("output"); - testWebSocket(); - } - - function testWebSocket() - { - websocket = new WebSocket(wsUri); - websocket.onopen = function(evt) { onOpen(evt) }; - websocket.onclose = function(evt) { onClose(evt) }; - websocket.onmessage = function(evt) { onMessage(evt) }; - websocket.onerror = function(evt) { onError(evt) }; - } - - function onOpen(evt) - { - writeToScreen("CONNECTED"); - doSend("WebSocket rocks"); - } - - function onClose(evt) - { - writeToScreen("DISCONNECTED"); - } - - function onMessage(evt) - { - writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>'); - websocket.close(); - } - - function onError(evt) - { - writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data); - } - - function doSend(message) - { - writeToScreen("SENT: " + message); - websocket.send(message); - } - - function writeToScreen(message) - { - var pre = document.createElement("p"); - pre.style.wordWrap = "break-word"; - pre.innerHTML = message; - output.appendChild(pre); - } - - window.addEventListener("load", init, false); - -</script> - -<h2>WebSocket Test</h2> - -<div id="output"></div> diff --git a/vespalib/src/tests/websocket/websocket_server.cpp b/vespalib/src/tests/websocket/websocket_server.cpp deleted file mode 100644 index 7a9cf46cb5f..00000000000 --- a/vespalib/src/tests/websocket/websocket_server.cpp +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/websocket/websocket_server.h> -#include <vespa/vespalib/util/host_name.h> -#include <vespa/vespalib/util/signalhandler.h> -#include <vespa/vespalib/io/mapped_file_input.h> -#include <thread> -#include <chrono> - -using namespace vespalib; - -vespalib::string read_file(const vespalib::string &file_name) { - return MappedFileInput(file_name).get().make_string(); -} - -vespalib::string find_content_type(const vespalib::string &file_name) { - if (ends_with(file_name, ".html")) { - return "text/html"; - } - if (ends_with(file_name, ".js")) { - return "text/javascript"; - } - if (ends_with(file_name, ".ico")) { - return "image/x-icon"; - } - return "text/plain"; -} - -int main(int, char **) { - ws::WebsocketServer::StaticRepo repo; - for (vespalib::string file_name: { "index.html", "test.html", "favicon.ico" }) { - vespalib::string content = read_file(file_name); - vespalib::string content_type = find_content_type(file_name); - if (!content.empty()) { - fprintf(stderr, "loaded file: %s as content %s\n", file_name.c_str(), content_type.c_str()); - repo.emplace("/" + file_name, ws::WebsocketServer::StaticPage{content_type, content}); - } - } - ws::WebsocketServer server(0, std::move(repo)); - int port = server.port(); - SignalHandler::INT.hook(); - fprintf(stderr, "running websocket server at http://%s:%d/index.html\n", - HostName::get().c_str(), port); - fprintf(stderr, "use ^C (SIGINT) to exit\n"); - while (!SignalHandler::INT.check()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - fprintf(stderr, "exiting...\n"); - kill(getpid(), SIGTERM); - return 0; -} diff --git a/vespalib/src/tests/websocket/websocket_test.cpp b/vespalib/src/tests/websocket/websocket_test.cpp deleted file mode 100644 index 5b346df4243..00000000000 --- a/vespalib/src/tests/websocket/websocket_test.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -#include <vespa/vespalib/testkit/test_kit.h> -#include <vespa/vespalib/net/socket_spec.h> -#include <vespa/vespalib/websocket/handler.h> -#include <vespa/vespalib/websocket/acceptor.h> -#include <vespa/vespalib/websocket/key.h> -#include <vespa/vespalib/websocket/buffer.h> -#include <vespa/vespalib/util/gate.h> - -using namespace vespalib; -using namespace vespalib::ws; - -template <typename T> -struct Receptor : vespalib::ws::Handler<T> { - std::unique_ptr<T> obj; - vespalib::Gate gate; - ~Receptor(); - void handle(std::unique_ptr<T> t) override { - obj = std::move(t); - gate.countDown(); - } -}; - -template <typename T> -Receptor<T>::~Receptor() = default; - -vespalib::string read_bytes(Socket &socket, size_t wanted_bytes) { - char tmp[64]; - vespalib::string result; - while (result.size() < wanted_bytes) { - size_t read_size = std::min(sizeof(tmp), wanted_bytes - result.size()); - size_t read_result = socket.read(tmp, read_size); - if (static_cast<ssize_t>(read_result) <= 0) { - return result; - } - result.append(tmp, read_result); - } - return result; -} - -void verify_socket_io(bool is_server, Socket &socket) { - vespalib::string server_message = "hello, this is the server speaking"; - vespalib::string client_message = "please pick up, I need to talk to you"; - if(is_server) { - socket.write(server_message.data(), server_message.size()); - vespalib::string read = read_bytes(socket, client_message.size()); - EXPECT_EQUAL(client_message, read); - } else { - socket.write(client_message.data(), client_message.size()); - vespalib::string read = read_bytes(socket, server_message.size()); - EXPECT_EQUAL(server_message, read); - } -} - -void verify_socket_io_async(Socket &server, Socket &client) { - std::thread server_thread(verify_socket_io, true, std::ref(server)); - std::thread client_thread(verify_socket_io, false, std::ref(client)); - server_thread.join(); - client_thread.join(); -} - -void check_buffer_stats(const Buffer &buffer, size_t dead, size_t used, size_t free) { - EXPECT_EQUAL(dead, buffer.dead()); - EXPECT_EQUAL(used, buffer.used()); - EXPECT_EQUAL(free, buffer.free()); -} - -TEST("require that basic reserve/commit/obtain/evict buffer cycle works") { - Buffer buffer; - check_buffer_stats(buffer, 0, 0, 0); - char *a = buffer.reserve(1); - check_buffer_stats(buffer, 0, 0, 1); - *a = 'x'; - buffer.commit(1); - check_buffer_stats(buffer, 0, 1, 0); - EXPECT_EQUAL('x', *buffer.obtain()); - check_buffer_stats(buffer, 0, 1, 0); - buffer.evict(1); - check_buffer_stats(buffer, 1, 0, 0); -} - -TEST("require that buffer moves contained data when more space is needed") { - Buffer buffer; - memcpy(buffer.reserve(3), "xyz", 3); - buffer.commit(3); - EXPECT_EQUAL('x', *buffer.obtain()); - buffer.evict(1); - EXPECT_EQUAL('y', *buffer.obtain()); - check_buffer_stats(buffer, 1, 2, 0); - buffer.reserve(1); - check_buffer_stats(buffer, 0, 2, 1); - EXPECT_EQUAL('y', *buffer.obtain()); - buffer.evict(1); - EXPECT_EQUAL('z', *buffer.obtain()); - check_buffer_stats(buffer, 1, 1, 1); - buffer.reserve(3); - check_buffer_stats(buffer, 0, 1, 3); - EXPECT_EQUAL('z', *buffer.obtain()); -} - -TEST("require that an acceptor can accept connections asynchronously") { - Receptor<Socket> server; - Acceptor acceptor(0, server); - Socket::UP client = SimpleSocket::connect(SocketSpec::from_port(acceptor.port())); - server.gate.await(60s); - ASSERT_TRUE(server.obj.get() != nullptr); - ASSERT_TRUE(client.get() != nullptr); - TEST_DO(verify_socket_io_async(*server.obj, *client)); -} - -TEST("require that websocket accept tokens are generated correctly") { - vespalib::string key("dGhlIHNhbXBsZSBub25jZQ=="); - vespalib::string accept_token("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="); - EXPECT_EQUAL(accept_token, Key::accept(key)); -} - -TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/vespalib/src/vespa/vespalib/CMakeLists.txt b/vespalib/src/vespa/vespalib/CMakeLists.txt index 738e0c6c766..765ed0d5634 100644 --- a/vespalib/src/vespa/vespalib/CMakeLists.txt +++ b/vespalib/src/vespa/vespalib/CMakeLists.txt @@ -24,7 +24,6 @@ vespa_add_library(vespalib $<TARGET_OBJECTS:vespalib_vespalib_time> $<TARGET_OBJECTS:vespalib_vespalib_trace> $<TARGET_OBJECTS:vespalib_vespalib_util> - $<TARGET_OBJECTS:vespalib_vespalib_websocket> INSTALL lib64 DEPENDS ${VESPA_GCC_LIB} diff --git a/vespalib/src/vespa/vespalib/websocket/CMakeLists.txt b/vespalib/src/vespa/vespalib/websocket/CMakeLists.txt deleted file mode 100644 index 1aee013d2bc..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -vespa_add_library(vespalib_vespalib_websocket OBJECT - SOURCES - acceptor.cpp - buffer.cpp - connection.cpp - frame.cpp - handler.cpp - key.cpp - request.cpp - websocket_server.cpp - DEPENDS -) diff --git a/vespalib/src/vespa/vespalib/websocket/README b/vespalib/src/vespa/vespalib/websocket/README deleted file mode 100644 index 5ecb11cbb12..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/README +++ /dev/null @@ -1,22 +0,0 @@ -NOTE: - -(0) if you use this code and something breaks; it is your own fault. - -(1) this code is experimental and will probably undergo large changes -before it is ready for production use. - -(2) please do not use this code in your applications just yet. - -(3) if you have any questions/suggestions about the code, please -contact havardpe@yahoo-inc.com - -(4) the goal of this library: -- accept websocket connections for application resources -- serve simple HTTP GET requests (read only) -- negotiate custom binary connections (fs4 packet protocol, fnet rpc) - -(5) what to build on top: -- Remote Slime Message Passing over websockets (next-gen rpc) -- back-end state API serving -- json (text) binding to slime messaging (rpc from browser javascript) -- HTTP tunneling of application data (cross-colo feeding) diff --git a/vespalib/src/vespa/vespalib/websocket/acceptor.cpp b/vespalib/src/vespa/vespalib/websocket/acceptor.cpp deleted file mode 100644 index a8fbb0e4231..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/acceptor.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "acceptor.h" -#include <vespa/vespalib/net/socket_spec.h> -#include <functional> - -namespace vespalib::ws { - -void -Acceptor::accept_main(Handler<Socket> &socket_handler) -{ - while (!_is_closed) { - SocketHandle handle = _server_socket.accept(); - if (handle.valid()) { - socket_handler.handle(std::make_unique<SimpleSocket>(std::move(handle))); - } - } -} - -Acceptor::Acceptor(int port_in, Handler<Socket> &socket_handler) - : _server_socket(port_in), - _is_closed(false), - _accept_thread(&Acceptor::accept_main, this, std::ref(socket_handler)) -{ -} - -Acceptor::~Acceptor() -{ - _server_socket.shutdown(); - _is_closed = true; - _accept_thread.join(); -} - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/acceptor.h b/vespalib/src/vespa/vespalib/websocket/acceptor.h deleted file mode 100644 index 711c9684161..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/acceptor.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#pragma once - -#include "handler.h" -#include <vespa/vespalib/net/socket.h> -#include <vespa/vespalib/net/server_socket.h> -#include <thread> -#include <atomic> - -namespace vespalib { -namespace ws { - -class Acceptor { -private: - ServerSocket _server_socket; - std::atomic<bool> _is_closed; - std::thread _accept_thread; - - void accept_main(Handler<Socket> &socket_handler); - -public: - Acceptor(int port_in, Handler<Socket> &socket_handler); - ~Acceptor(); - int port() { return _server_socket.address().port(); } -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/buffer.cpp b/vespalib/src/vespa/vespalib/websocket/buffer.cpp deleted file mode 100644 index c35fd4a3597..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/buffer.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "buffer.h" - -namespace vespalib::ws { - -void -Buffer::ensure_free(size_t bytes) -{ - memmove(&_data[0], &_data[_read_pos], used()); - _write_pos -= _read_pos; - _read_pos = 0; - if (free() < bytes) { - _data.resize(_write_pos + bytes); - } -} - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/buffer.h b/vespalib/src/vespa/vespalib/websocket/buffer.h deleted file mode 100644 index 382c60d0b0c..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/buffer.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include <vespa/vespalib/stllike/string.h> -#include <vector> - -namespace vespalib { -namespace ws { - -class Buffer -{ -private: - std::vector<char> _data; - size_t _read_pos; - size_t _write_pos; - void ensure_free(size_t bytes); -public: - Buffer() : _data(), _read_pos(0), _write_pos(0) {} - void clear() { - _read_pos = 0; - _write_pos = 0; - } - size_t dead() const { return _read_pos; } - size_t used() const { return (_write_pos - _read_pos); } - size_t free() const { return (_data.size() - _write_pos); } - bool has_next() const { return (used() > 0); } - char next() { return _data[_read_pos++]; } - void push(char value) { - *reserve(1) = value; - commit(1); - } - const char *obtain() const { return &_data[_read_pos]; } - void evict(size_t bytes) { _read_pos += bytes; } - char *reserve(size_t bytes) { - if (free() < bytes) { - ensure_free(bytes); - } - return &_data[_write_pos]; - } - void commit(size_t bytes) { _write_pos += bytes; } -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/connection.cpp b/vespalib/src/vespa/vespalib/websocket/connection.cpp deleted file mode 100644 index 173839f376b..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/connection.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "connection.h" -#include <vespa/vespalib/util/size_literals.h> -#include <cstdarg> -#include <cassert> - -namespace vespalib::ws { - -namespace { - -void stripCR(vespalib::string &dst) { - if (!dst.empty() && dst[dst.size() - 1] == '\r') { - dst.resize(dst.size() - 1); - } -} - -Frame::Type type_from_opcode(char opcode) { - switch (opcode) { - case 0x0: return Frame::Type::NONE; - case 0x1: return Frame::Type::TEXT; - case 0x2: return Frame::Type::DATA; - case 0x8: return Frame::Type::PING; - case 0x9: return Frame::Type::PONG; - case 0xa: return Frame::Type::CLOSE; - default: return Frame::Type::INVALID; - } -} - -char opcode_from_type(Frame::Type type) { - switch (type) { - case Frame::Type::NONE: return 0x0; - case Frame::Type::TEXT: return 0x1; - case Frame::Type::DATA: return 0x2; - case Frame::Type::PING: return 0x8; - case Frame::Type::PONG: return 0x9; - case Frame::Type::CLOSE: return 0xa; - default: return 0xf; - } -} - -} // namespace vespalib::ws::<unnamed> - - -Connection::Connection(Socket::UP socket) - : _socket(std::move(socket)), - _input(), - _output() -{ -} - -bool -Connection::fill_input(size_t min_bytes) -{ - while (_input.used() < min_bytes) { - size_t max_read = (8_Ki); - char *ptr = _input.reserve(max_read); - ssize_t read_res = _socket->read(ptr, max_read); - if (read_res > 0) { - _input.commit(read_res); - } else { - return false; - } - } - return true; -} - -bool -Connection::read_line(string &dst) -{ - dst.clear(); - for (int c = read_byte(); c >= 0; c = read_byte()) { - if (c != '\n') { - dst.push_back(c); - } else { - stripCR(dst); - return true; - } - } - return !dst.empty(); -} - -bool -Connection::read_frame(Frame &frame) -{ - if (!fill_input(2)) { - return false; - } - char h1 = _input.next(); - char h2 = _input.next(); - frame.type = type_from_opcode(h1 & 0x0f); - frame.last = ((h1 & 0x80) != 0); - frame.payload.clear(); - size_t len = (h2 & 0x7f); - if (len > 125) { - size_t len_bytes = (len == 127) ? 8 : 2; - if (!fill_input(len_bytes)) { - return false; - } - len = 0; - for (size_t i = 0; i < len_bytes; ++i) { - len = (len << 8) + (_input.next() & 0xff); - } - } - char mask[4]; - bool use_mask = ((h2 & 0x80) != 0); - if (use_mask) { - if (!fill_input(4)) { - return false; - } - for (size_t i = 0; i < 4; ++i) { - mask[i] = _input.next(); - } - } - if (!fill_input(len)) { - return false; - } - const char *src = _input.obtain(); - char *dst = frame.payload.reserve(len); - if (use_mask) { - for (size_t i = 0; i < len; ++i) { - dst[i] = (src[i] ^ mask[i & 0x3]); - } - } else { - memcpy(dst, src, len); - } - frame.payload.commit(len); - _input.evict(len); - return true; -} - -void -Connection::write_frame(const Frame &frame) -{ - size_t len = frame.payload.used(); - bool large_len = (len > 125); - bool huge_len = (len > 0xffFF); - char h1 = opcode_from_type(frame.type); - if (frame.last) { - h1 |= 0x80; - } - char h2 = (large_len) ? 126 : len; - if (huge_len) { - ++h2; - } - _output.push(h1); - _output.push(h2); - if (large_len) { - size_t len_bytes = (huge_len) ? 8 : 2; - size_t len_src = len; - char *len_dst = _output.reserve(len_bytes); - for (size_t i = len_bytes; i > 0; --i) { - len_dst[i - 1] = (len_src & 0xff); - len_src >>= 8; - } - _output.commit(len_bytes); - } - char *dst = _output.reserve(len); - memcpy(dst, frame.payload.obtain(), len); - _output.commit(len); -} - -void -Connection::printf(const char *fmt, ...) -{ - char *dst = _output.reserve(256); - int space = _output.free(); - int size; - va_list ap; - va_start(ap, fmt); - size = vsnprintf(dst, space, fmt, ap); - va_end(ap); - assert(size >= 0); - if (size >= space) { - space = size + 1; - dst = _output.reserve(space); - va_start(ap, fmt); - size = vsnprintf(dst, space, fmt, ap); - va_end(ap); - assert((size + 1) == space); - } - _output.commit(size); -} - -void -Connection::write(const char *data, size_t len) -{ - char *dst = _output.reserve(len); - memcpy(dst, data, len); - _output.commit(len); -} - -bool -Connection::flush() -{ - while (_output.used() > 0) { - ssize_t write_res = _socket->write(_output.obtain(), _output.used()); - if (write_res > 0) { - _output.evict(write_res); - } else { - return false; - } - } - return true; -} - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/connection.h b/vespalib/src/vespa/vespalib/websocket/connection.h deleted file mode 100644 index fdfcd227f98..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/connection.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include <vespa/vespalib/net/socket.h> -#include "buffer.h" -#include "frame.h" - -namespace vespalib { -namespace ws { - -class Connection -{ -private: - Socket::UP _socket; - Buffer _input; - Buffer _output; - - bool fill_input(size_t min_bytes); - -public: - typedef std::unique_ptr<Connection> UP; - explicit Connection(Socket::UP socket); - - int read_byte() { - if (!_input.has_next()) { - if (!fill_input(1)) { - return -1; - } - } - return (_input.next() & 0xff); - } - - bool read_line(vespalib::string &dst); - - bool read_frame(Frame &frame); - - void write_frame(const Frame &frame); - - void printf(const char *fmt, ...) -#ifdef __GNUC__ - // Add printf format checks with gcc - __attribute__ ((format (printf,2,3))) -#endif - ; - - void write(const char *data, size_t len); - - bool flush(); -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/frame.cpp b/vespalib/src/vespa/vespalib/websocket/frame.cpp deleted file mode 100644 index d742d95698b..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/frame.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "frame.h" - -namespace vespalib::ws { - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/frame.h b/vespalib/src/vespa/vespalib/websocket/frame.h deleted file mode 100644 index 17a9d2008e8..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/frame.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include <memory> -#include <vespa/vespalib/stllike/string.h> -#include "buffer.h" - -namespace vespalib { -namespace ws { - -struct Frame { - enum class Type { NONE, TEXT, DATA, PING, PONG, CLOSE, INVALID }; - Type type; - bool last; - Buffer payload; - Frame() : type(Type::INVALID), last(true), payload() {} -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/handler.cpp b/vespalib/src/vespa/vespalib/websocket/handler.cpp deleted file mode 100644 index bbff65447d8..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/handler.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "handler.h" - -namespace vespalib::ws { - -namespace { - -struct DummyItem {}; - -} // namespace vespalib::ws::<unnamed> - -template struct Handler<DummyItem>; - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/handler.h b/vespalib/src/vespa/vespalib/websocket/handler.h deleted file mode 100644 index 8919f4bfdcc..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/handler.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include <memory> - -namespace vespalib { -namespace ws { - -/** - * A Handler is a component to whom you can pass an object. - **/ -template <typename T> -struct Handler -{ - virtual void handle(std::unique_ptr<T> obj) = 0; - virtual ~Handler() {} -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/key.cpp b/vespalib/src/vespa/vespalib/websocket/key.cpp deleted file mode 100644 index 0760c10fe31..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/key.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "key.h" -#include <vespa/vespalib/util/sha1.h> - -namespace vespalib::ws { - -namespace { - -const char *base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - -char id(int value) { return base64_chars[value & 0x3f]; } -vespalib::string encode64(const char *data, size_t len) { - uint8_t tmp[3]; - vespalib::string result; - for (size_t i = 0; i < len; i += 3) { - tmp[0] = data[i]; - tmp[1] = (i + 1 < len) ? data[i + 1] : 0; - tmp[2] = (i + 2 < len) ? data[i + 2] : 0; - result.append(id(tmp[0] >> 2)); - result.append(id((tmp[0] << 4) | (tmp[1] >> 4))); - result.append((i + 1 < len) ? id((tmp[1] << 2) | (tmp[2] >> 6)) : '='); - result.append((i + 2 < len) ? id(tmp[2]) : '='); - } - return result; -} - -} // namespace vespalib::ws::<unnamed> - -vespalib::string -Key::create() -{ - return "dGhlIHNhbXBsZSBub25jZQ=="; -} - -vespalib::string -Key::accept(const vespalib::string &key) -{ - char hash[20]; - vespalib::string hash_input(key); - hash_input.append("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); - Sha1::hash(hash_input.data(), hash_input.size(), hash, 20); - return encode64(hash, 20); -} - -} // namespace vespalib::ws diff --git a/vespalib/src/vespa/vespalib/websocket/key.h b/vespalib/src/vespa/vespalib/websocket/key.h deleted file mode 100644 index 76062594f38..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/key.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include <vespa/vespalib/stllike/string.h> - -namespace vespalib { -namespace ws { - -struct Key -{ - /** - * Create a new random key that can be used by a client to request - * a version 13 websocket connection upgrade. - **/ - static vespalib::string create(); - - /** - * Generate a version 13 websocket handshake accept token for a - * client key. - **/ - static vespalib::string accept(const vespalib::string &key); -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/request.cpp b/vespalib/src/vespa/vespalib/websocket/request.cpp deleted file mode 100644 index 44b05d9947e..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/request.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "request.h" -#include <cassert> -#include <algorithm> - -namespace vespalib::ws { - -namespace { - -void split(vespalib::stringref str, vespalib::stringref sep, - std::vector<vespalib::string> &dst) -{ - dst.clear(); - vespalib::string tmp; - for (size_t i = 0; i < str.size(); ++i) { - if (sep.find(str[i]) != vespalib::string::npos) { - if (!tmp.empty()) { - dst.push_back(tmp); - tmp.clear(); - } - } else { - tmp.push_back(str[i]); - } - } - if (!tmp.empty()) { - dst.push_back(tmp); - } -} - -} // namespace vespalib::ws::<unnamed> - -Request::Request() { } -Request::~Request() { } - -bool -Request::handle_header(vespalib::string &header_name, - const vespalib::string &header_line) -{ - assert(!header_line.empty()); - size_t pos = 0; - size_t end = header_line.size(); - bool continuation = (header_line[0] == ' ') || (header_line[0] == '\t'); - if (!continuation) { - pos = header_line.find(":"); - if (pos == vespalib::string::npos) { - return false; - } else { - header_name.assign(header_line, 0, pos++); - std::transform(header_name.begin(), header_name.end(), - header_name.begin(), ::tolower); - } - } - while ((pos < end) && (isspace(header_line[pos]))) { - ++pos; // strip leading whitespace - } - while ((pos < end) && (isspace(header_line[end - 1]))) { - --end; // strip trailing whitespace - } - if (header_name.empty()) { - return false; - } - auto header_insert_result = _headers.insert(std::make_pair(header_name, vespalib::string())); - bool header_found = !header_insert_result.second; - vespalib::string &header_value = header_insert_result.first->second; - if (!header_found) { - header_value.assign(header_line, pos, end - pos); - } else { - if (continuation) { - header_value.push_back(' '); - } else { // duplicate header - header_value.push_back(','); - } - header_value.append(header_line.data() + pos, end - pos); - } - return true; -} - -bool -Request::read_header(Connection &conn) -{ - vespalib::string line; - vespalib::string header_name_space; - std::vector<vespalib::string> parts; - if (!conn.read_line(line)) { - return false; - } - split(line, "\t ", parts); - if (parts.size() != 3) { - return false; - } - _method = parts[0]; - _uri = parts[1]; - _version = parts[2]; - while (conn.read_line(line)) { - if (line.empty()) { - fprintf(stderr, "request: %s %s %s\n", - _method.c_str(), _uri.c_str(), _version.c_str()); - for (const auto &h: _headers) { - fprintf(stderr, "request: '%s' -> '%s'\n", - h.first.c_str(), h.second.c_str()); - } - return true; // done - } - if (!handle_header(header_name_space, line)) { - return false; // malformed header - } - } - return false; // incomplete headers -} - -const vespalib::string & -Request::get_header(const vespalib::string &name) const -{ - auto pos = _headers.find(name); - if (pos == _headers.end()) { - return _empty; - } - return pos->second; -} - -bool -Request::has_connection_token(const vespalib::string &token) const -{ - std::vector<vespalib::string> tokens; - split(get_header("connection"), ",\t ", tokens); - for (const auto &t: tokens) { - if (strcasecmp(t.c_str(), token.c_str()) == 0) { - return true; - } - } - return false; -} - -bool -Request::is_ws_upgrade() const -{ - return ((strcasecmp(get_header("upgrade").c_str(), "websocket") == 0) && - has_connection_token("upgrade")); -} - -} diff --git a/vespalib/src/vespa/vespalib/websocket/request.h b/vespalib/src/vespa/vespalib/websocket/request.h deleted file mode 100644 index fb4dc25b0ab..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/request.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - - -#pragma once - -#include "connection.h" -#include <vespa/vespalib/stllike/string.h> -#include <map> -#include <vector> - -namespace vespalib { -namespace ws { - -class Request -{ -private: - vespalib::string _method; - vespalib::string _uri; - vespalib::string _version; - std::map<vespalib::string, vespalib::string> _headers; - vespalib::string _empty; - - bool handle_header(vespalib::string &header_name, - const vespalib::string &header_line); - -public: - Request(); - ~Request(); - bool read_header(Connection &conn); - bool is_get() const { return _method == "GET"; } - const vespalib::string &get_header(const vespalib::string &name) const; - bool has_connection_token(const vespalib::string &token) const; - bool is_ws_upgrade() const; - const vespalib::string &uri() const { return _uri; } -}; - -} // namespace vespalib::ws -} // namespace vespalib diff --git a/vespalib/src/vespa/vespalib/websocket/websocket_server.cpp b/vespalib/src/vespa/vespalib/websocket/websocket_server.cpp deleted file mode 100644 index dc9b38cd87c..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/websocket_server.cpp +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#include "websocket_server.h" -#include "connection.h" -#include "request.h" -#include "key.h" -#include <vespa/vespalib/util/stringfmt.h> -#include <vespa/vespalib/util/host_name.h> - -namespace vespalib::ws { - -namespace { - -vespalib::string magic = "[SELF]"; - -void respond_static(Connection &conn, const WebsocketServer::StaticPage &page, const vespalib::string &self) { - conn.printf("HTTP/1.1 200 OK\r\n"); - conn.printf("Connection: close\r\n"); - conn.printf("Content-Type: %s\r\n", page.content_type.c_str()); - conn.printf("\r\n"); - size_t pos = 0; - while (pos < page.content.size()) { - size_t next = page.content.find(magic, pos); - if (next == vespalib::string::npos) { - conn.write(page.content.data() + pos, page.content.size() - pos); - pos = page.content.size(); - } else { - conn.write(page.content.data() + pos, next - pos); - conn.write(self.data(), self.size()); - pos = next + magic.size(); - } - } - conn.flush(); -} - -void respond_error(Connection &conn, int code, const vespalib::string &message) { - conn.printf("HTTP/1.1 %d %s\r\n", code, message.c_str()); - conn.printf("Connection: close\r\n"); - conn.printf("Content-Type: text/html\r\n"); - conn.printf("\r\n"); - conn.printf("<html><body><h2>%d %s</h2></body></html>", - code, message.c_str()); - conn.flush(); -} - -void respond_upgrade(Connection &conn, const vespalib::string &accept_token) { - conn.printf("HTTP/1.1 101 Switching Protocols\r\n"); - conn.printf("Upgrade: websocket\r\n"); - conn.printf("Connection: Upgrade\r\n"); - conn.printf("Sec-WebSocket-Accept: %s\r\n", accept_token.c_str()); - conn.printf("\r\n"); - conn.flush(); -} - -void respond_upgrade_failed(Connection &conn) { - conn.printf("HTTP/1.1 400 Upgrade Failed\r\n"); - conn.printf("Connection: close\r\n"); - conn.printf("Sec-WebSocket-Version: 13\r\n"); - conn.printf("\r\n"); - conn.flush(); -} - -const char *name_from_type(Frame::Type type) { - switch (type) { - case Frame::Type::NONE: return "NONE"; - case Frame::Type::TEXT: return "TEXT"; - case Frame::Type::DATA: return "DATA"; - case Frame::Type::PING: return "PING"; - case Frame::Type::PONG: return "PONG"; - case Frame::Type::CLOSE: return "CLOSE"; - default: return "INVALID"; - } -} - -void handle_echo(Connection &conn) { - fprintf(stderr, "server: got ws connection\n"); - Frame frame; - bool done = false; - while (!done && conn.read_frame(frame)) { - fprintf(stderr, "server: got frame of type %s with size %zu\n", - name_from_type(frame.type), frame.payload.used()); - if (frame.type == Frame::Type::TEXT) { - fprintf(stderr, "server: got text: %s\n", - vespalib::string(frame.payload.obtain(), - frame.payload.used()).c_str()); - } - if (frame.type == Frame::Type::INVALID) { - break; - } - if (frame.type == Frame::Type::PONG) { - continue; - } - if (frame.type == Frame::Type::PING) { - frame.type = Frame::Type::PONG; - } - if (frame.type == Frame::Type::CLOSE) { - done = true; - } - conn.write_frame(frame); - conn.flush(); - } - fprintf(stderr, "server: closing ws connection\n"); -} - -void handle_upgrade(Connection &conn, Request &req) { - const vespalib::string version = req.get_header("sec-websocket-version"); - vespalib::string accept_token = Key::accept(req.get_header("sec-websocket-key")); - if (version == "13") { - respond_upgrade(conn, accept_token); - handle_echo(conn); - } else { - respond_upgrade_failed(conn); - } -} - -} // namespace vespalib::ws::<unnamed> - -WebsocketServer::StaticPage::StaticPage(const vespalib::string & type, const vespalib::string & content_in) - : content_type(type), - content(content_in) -{} -WebsocketServer::StaticPage::StaticPage(const StaticPage &) = default; -WebsocketServer::StaticPage & WebsocketServer::StaticPage::operator = (const StaticPage &) = default; -WebsocketServer::StaticPage::~StaticPage() = default; - -WebsocketServer::WebsocketServer(int port_in, StaticRepo &&repo) - : _acceptor(port_in, *this), - _static_repo(std::move(repo)), - _self(make_string("%s:%d", HostName::get().c_str(), _acceptor.port())) -{ -} - -WebsocketServer::~WebsocketServer() = default; - -void -WebsocketServer::handle(std::unique_ptr<Socket> socket) -{ - Connection conn(std::move(socket)); - Request req; - if (!req.read_header(conn)) { - respond_error(conn, 400, "Bad Request"); - return; - } - if (!req.is_get()) { - respond_error(conn, 501, "Not Implemented"); - return; - } - if (req.is_ws_upgrade()) { - if (req.uri() == "/echo") { - handle_upgrade(conn, req); - } else { - respond_error(conn, 404, "Not Found"); - } - } else { - auto page = _static_repo.find(req.uri()); - if (page != _static_repo.end()) { - respond_static(conn, page->second, _self); - } else { - respond_error(conn, 404, "Not Found"); - } - } -} - -} diff --git a/vespalib/src/vespa/vespalib/websocket/websocket_server.h b/vespalib/src/vespa/vespalib/websocket/websocket_server.h deleted file mode 100644 index f30daafb726..00000000000 --- a/vespalib/src/vespa/vespalib/websocket/websocket_server.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - -#pragma once - -#include "handler.h" -#include "acceptor.h" -#include <vespa/vespalib/stllike/string.h> -#include <map> - -namespace vespalib::ws { - -class WebsocketServer : public Handler<Socket> { -public: - struct StaticPage { - StaticPage(const vespalib::string & type, const vespalib::string & content_in); - StaticPage(const StaticPage &); - StaticPage & operator = (const StaticPage &); - StaticPage(StaticPage &&) = default; - StaticPage & operator = (StaticPage &&) = default; - ~StaticPage(); - vespalib::string content_type; - vespalib::string content; - }; - typedef std::map<vespalib::string, StaticPage> StaticRepo; - -private: - Acceptor _acceptor; - StaticRepo _static_repo; - vespalib::string _self; - -public: - WebsocketServer(int port_in, StaticRepo &&repo = StaticRepo()); - ~WebsocketServer() override; - void handle(std::unique_ptr<Socket> socket) override; - int port() { return _acceptor.port(); } -}; - -} // namespace vespalib::ws |