blob: 5fdce47e58683b9986b44badc575ca6ff7625c59 [file] [log] [blame]
Austin Schuh9d823002019-04-14 12:53:17 -07001// Copyright (c) 2013-2017, Matt Godbolt
Austin Schuh24adb6b2015-09-06 17:37:40 -07002// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// Redistributions of source code must retain the above copyright notice, this
8// list of conditions and the following disclaimer.
9//
10// Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13//
14// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24// POSSIBILITY OF SUCH DAMAGE.
25
26// An extraordinarily simple test which presents a web page with some buttons.
27// Clicking on the numbered button increments the number, which is visible to
28// other connected clients. WebSockets are used to do this: by the rather
29// suspicious means of sending raw JavaScript commands to be executed on other
30// clients.
31
32// Same as ws_test, but uses the poll() method and a separate epoll set to
33// demonstrate how Seasocks can be used with another polling system.
34
35#include "seasocks/PrintfLogger.h"
36#include "seasocks/Server.h"
37#include "seasocks/StringUtil.h"
38#include "seasocks/WebSocket.h"
39#include "seasocks/util/Json.h"
40
41#include <cstring>
42#include <iostream>
43#include <memory>
44#include <set>
45#include <sstream>
46#include <string>
47#include <fcntl.h>
48#include <unistd.h>
49#include <sys/epoll.h>
50
51using namespace seasocks;
Austin Schuh24adb6b2015-09-06 17:37:40 -070052
Austin Schuh9d823002019-04-14 12:53:17 -070053class MyHandler : public WebSocket::Handler {
Austin Schuh24adb6b2015-09-06 17:37:40 -070054public:
Austin Schuh9d823002019-04-14 12:53:17 -070055 explicit MyHandler(Server* server)
56 : _server(server), _currentValue(0) {
Austin Schuh24adb6b2015-09-06 17:37:40 -070057 setValue(1);
58 }
59
Austin Schuh9d823002019-04-14 12:53:17 -070060 virtual void onConnect(WebSocket* connection) override {
Austin Schuh24adb6b2015-09-06 17:37:40 -070061 _connections.insert(connection);
62 connection->send(_currentSetValue.c_str());
Austin Schuh9d823002019-04-14 12:53:17 -070063 std::cout << "Connected: " << connection->getRequestUri()
64 << " : " << formatAddress(connection->getRemoteAddress())
65 << "\nCredentials: " << *(connection->credentials()) << "\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -070066 }
67
Austin Schuh9d823002019-04-14 12:53:17 -070068 virtual void onData(WebSocket* connection, const char* data) override {
Austin Schuh24adb6b2015-09-06 17:37:40 -070069 if (0 == strcmp("die", data)) {
70 _server->terminate();
71 return;
72 }
73 if (0 == strcmp("close", data)) {
Austin Schuh9d823002019-04-14 12:53:17 -070074 std::cout << "Closing..\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -070075 connection->close();
Austin Schuh9d823002019-04-14 12:53:17 -070076 std::cout << "Closed.\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -070077 return;
78 }
79
Austin Schuh9d823002019-04-14 12:53:17 -070080 const int value = std::stoi(data) + 1;
Austin Schuh24adb6b2015-09-06 17:37:40 -070081 if (value > _currentValue) {
82 setValue(value);
Austin Schuh9d823002019-04-14 12:53:17 -070083 for (auto c : _connections) {
84 c->send(_currentSetValue.c_str());
Austin Schuh24adb6b2015-09-06 17:37:40 -070085 }
86 }
87 }
88
Austin Schuh9d823002019-04-14 12:53:17 -070089 virtual void onDisconnect(WebSocket* connection) override {
Austin Schuh24adb6b2015-09-06 17:37:40 -070090 _connections.erase(connection);
Austin Schuh9d823002019-04-14 12:53:17 -070091 std::cout << "Disconnected: " << connection->getRequestUri()
92 << " : " << formatAddress(connection->getRemoteAddress()) << "\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -070093 }
94
95private:
Austin Schuh9d823002019-04-14 12:53:17 -070096 std::set<WebSocket*> _connections;
Austin Schuh24adb6b2015-09-06 17:37:40 -070097 Server* _server;
98 int _currentValue;
Austin Schuh9d823002019-04-14 12:53:17 -070099 std::string _currentSetValue;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700100
101 void setValue(int value) {
102 _currentValue = value;
103 _currentSetValue = makeExecString("set", _currentValue);
104 }
105};
106
Austin Schuh9d823002019-04-14 12:53:17 -0700107int main(int /*argc*/, const char* /*argv*/[]) {
108 auto logger = std::make_shared<PrintfLogger>(Logger::Level::Debug);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700109
110 Server server(logger);
111
Austin Schuh9d823002019-04-14 12:53:17 -0700112 auto handler = std::make_shared<MyHandler>(&server);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700113 server.addWebSocketHandler("/ws", handler);
114 server.setStaticPath("src/ws_test_web");
115 if (!server.startListening(9090)) {
Austin Schuh9d823002019-04-14 12:53:17 -0700116 std::cerr << "couldn't start listening\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700117 return 1;
118 }
119 int myEpoll = epoll_create(10);
Austin Schuh9d823002019-04-14 12:53:17 -0700120 epoll_event wakeSeasocks = {EPOLLIN | EPOLLOUT | EPOLLERR, {&server}};
Austin Schuh24adb6b2015-09-06 17:37:40 -0700121 epoll_ctl(myEpoll, EPOLL_CTL_ADD, server.fd(), &wakeSeasocks);
122
123 // Also poll stdin
Austin Schuh9d823002019-04-14 12:53:17 -0700124 epoll_event wakeStdin = {EPOLLIN, {nullptr}};
Austin Schuh24adb6b2015-09-06 17:37:40 -0700125 epoll_ctl(myEpoll, EPOLL_CTL_ADD, STDIN_FILENO, &wakeStdin);
126 auto prevFlags = fcntl(STDIN_FILENO, F_GETFL, 0);
127 fcntl(STDIN_FILENO, F_SETFL, prevFlags | O_NONBLOCK);
128
Austin Schuh9d823002019-04-14 12:53:17 -0700129 std::cout << "Will echo anything typed in stdin: " << std::flush;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700130 while (true) {
131 constexpr auto maxEvents = 2;
132 epoll_event events[maxEvents];
133 auto res = epoll_wait(myEpoll, events, maxEvents, -1);
134 if (res < 0) {
Austin Schuh9d823002019-04-14 12:53:17 -0700135 std::cerr << "epoll returned an error\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700136 return 1;
137 }
138 for (auto i = 0; i < res; ++i) {
139 if (events[i].data.ptr == &server) {
140 auto seasocksResult = server.poll(0);
Austin Schuh9d823002019-04-14 12:53:17 -0700141 if (seasocksResult == Server::PollResult::Terminated)
142 return 0;
143 if (seasocksResult == Server::PollResult::Error)
144 return 1;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700145 } else if (events[i].data.ptr == nullptr) {
146 // Echo stdin to stdout to show we can read from that too.
147 for (;;) {
148 char buf[1024];
149 auto numRead = ::read(STDIN_FILENO, buf, sizeof(buf));
150 if (numRead < 0) {
151 if (errno != EWOULDBLOCK && errno != EAGAIN) {
Austin Schuh9d823002019-04-14 12:53:17 -0700152 std::cerr << "Error reading stdin\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700153 return 1;
154 }
155 break;
156 } else if (numRead > 0) {
157 auto written = write(STDOUT_FILENO, buf, numRead);
158 if (written != numRead) {
Austin Schuh9d823002019-04-14 12:53:17 -0700159 std::cerr << "Truncated write\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700160 }
161 } else if (numRead == 0) {
Austin Schuh9d823002019-04-14 12:53:17 -0700162 std::cerr << "EOF on stdin\n";
Austin Schuh24adb6b2015-09-06 17:37:40 -0700163 return 0;
164 }
165 }
166 }
167 }
168 }
169 return 0;
170}