summaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/websocket/websocket_test.cpp
blob: db031b247b476ea3cf3a068f2e27c3d87a39dcfa (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2017 Yahoo Holdings. 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 <thread>
#include <chrono>

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(60000);
    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(); }