blob: 63e54abb96a44092aebb92ec64cf1234f94bf87e [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#pragma once
27
28#include "seasocks/ServerImpl.h"
29#include "seasocks/WebSocket.h"
30
31#include <sys/types.h>
32
33#include <atomic>
34#include <cstdint>
Austin Schuh9d823002019-04-14 12:53:17 -070035#include <functional>
Austin Schuh24adb6b2015-09-06 17:37:40 -070036#include <list>
37#include <map>
38#include <memory>
39#include <mutex>
40#include <string>
41#include <unordered_map>
42
43namespace seasocks {
44
45class Connection;
46class Logger;
47class PageHandler;
48class Request;
49class Response;
50
51class Server : private ServerImpl {
52public:
Austin Schuh9d823002019-04-14 12:53:17 -070053 explicit Server(std::shared_ptr<Logger> logger);
Austin Schuh24adb6b2015-09-06 17:37:40 -070054 virtual ~Server();
55
56 void addPageHandler(std::shared_ptr<PageHandler> handler);
57
58 void addWebSocketHandler(const char* endpoint, std::shared_ptr<WebSocket::Handler> handler,
Austin Schuh9d823002019-04-14 12:53:17 -070059 bool allowCrossOriginRequests = false);
Austin Schuh24adb6b2015-09-06 17:37:40 -070060
61 // Serves static content from the given port on the current thread, until terminate is called.
62 // Roughly equivalent to startListening(port); setStaticPath(staticPath); loop();
63 // Returns whether exiting was expected.
64 bool serve(const char* staticPath, int port);
65
66 // Starts listening on a given interface (in host order) and port.
67 // Returns true if all was ok.
68 bool startListening(uint32_t ipInHostOrder, int port);
69
70 // Starts listening on a port on all interfaces.
71 // Returns true if all was ok.
72 bool startListening(int port);
73
Austin Schuh9d823002019-04-14 12:53:17 -070074 // Starts listening on a unix domain socket.
75 // Returns true if all was ok.
76 bool startListeningUnix(const char* socketPath);
77
Austin Schuh24adb6b2015-09-06 17:37:40 -070078 // Sets the path to server static content from.
79 void setStaticPath(const char* staticPath);
80
81 // Loop (until terminate called from another thread).
82 // Returns true if terminate() was used to exit the loop, false if there
83 // was an error.
84 bool loop();
85
86 // Runs a single iteration of the main loop, blocking for a given time.
87 // Returns immediately if terminate() has been called. Must be consistently
88 // called from the same thread. Returns an enum describing why it returned.
89 enum class PollResult {
90 Continue,
91 Terminated,
92 Error,
93 };
94 PollResult poll(int millisToBlock);
95
96 // Returns a file descriptor that can be polled for changes (e.g. by
97 // placing it in an epoll set. The poll() method above only need be called
98 // when this file descriptor is readable.
Austin Schuh9d823002019-04-14 12:53:17 -070099 int fd() const {
100 return _epollFd;
101 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700102
103 // Terminate any loop() or poll(). May be called from any thread.
104 void terminate();
105
Austin Schuh9d823002019-04-14 12:53:17 -0700106 // If we haven't heard anything ever on a connection for this long, kill it.
107 // This is possibly caused by bad WebSocket implementation in Chrome.
108 void setLameConnectionTimeoutSeconds(int seconds);
109
110 // Sets the maximum number of TCP level keepalives that we can miss before
111 // we let the OS consider the connection dead. We configure keepalives every second,
112 // so this is also the minimum number of seconds it takes to notice a badly-behaved
113 // dead connection, e.g. a laptop going into sleep mode or a hard-crashed machine.
114 // A value of 0 disables keep alives, which is the default.
115 void setMaxKeepAliveDrops(int maxKeepAliveDrops);
116
117 // Set the maximum amount of data we'll buffer for a client before we close the
118 // connection assuming the client can't keep up with the data rate. Default
119 // is available here too.
120 static constexpr size_t DefaultClientBufferSize = 16 * 1024 * 1024u;
121 void setClientBufferSize(size_t bytesToBuffer);
122 size_t clientBufferSize() const override {
123 return _clientBufferSize;
124 }
125
126 void setPerMessageDeflateEnabled(bool enabled);
127 bool getPerMessageDeflateEnabled() {
128 return _perMessageDeflateEnabled;
129 }
130
Austin Schuh24adb6b2015-09-06 17:37:40 -0700131 class Runnable {
132 public:
Austin Schuh9d823002019-04-14 12:53:17 -0700133 virtual ~Runnable() = default;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700134 virtual void run() = 0;
135 };
Austin Schuh9d823002019-04-14 12:53:17 -0700136 // Execute a task on the Seasocks thread.
Austin Schuh24adb6b2015-09-06 17:37:40 -0700137 void execute(std::shared_ptr<Runnable> runnable);
Austin Schuh9d823002019-04-14 12:53:17 -0700138 using Executable = std::function<void()>;
139 void execute(Executable toExecute);
Austin Schuh24adb6b2015-09-06 17:37:40 -0700140
141private:
142 // From ServerImpl
143 virtual void remove(Connection* connection) override;
144 virtual bool subscribeToWriteEvents(Connection* connection) override;
145 virtual bool unsubscribeFromWriteEvents(Connection* connection) override;
Austin Schuh9d823002019-04-14 12:53:17 -0700146 virtual const std::string& getStaticPath() const override {
147 return _staticPath;
148 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700149 virtual std::shared_ptr<WebSocket::Handler> getWebSocketHandler(const char* endpoint) const override;
Austin Schuh9d823002019-04-14 12:53:17 -0700150 virtual bool isCrossOriginAllowed(const std::string& endpoint) const override;
151 virtual std::shared_ptr<Response> handle(const Request& request) override;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700152 virtual std::string getStatsDocument() const override;
153 virtual void checkThread() const override;
Austin Schuh9d823002019-04-14 12:53:17 -0700154 virtual Server& server() override {
155 return *this;
156 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700157
158 bool makeNonBlocking(int fd) const;
159 bool configureSocket(int fd) const;
160 void handleAccept();
Austin Schuh24adb6b2015-09-06 17:37:40 -0700161 void processEventQueue();
Austin Schuh9d823002019-04-14 12:53:17 -0700162 void runExecutables();
Austin Schuh24adb6b2015-09-06 17:37:40 -0700163
164 void shutdown();
165
166 void checkAndDispatchEpoll(int epollMillis);
167 void handlePipe();
Austin Schuh9d823002019-04-14 12:53:17 -0700168 enum class NewState { KeepOpen,
169 Close };
Austin Schuh24adb6b2015-09-06 17:37:40 -0700170 NewState handleConnectionEvents(Connection* connection, uint32_t events);
171
172 // Connections, mapped to initial connection time.
173 std::map<Connection*, time_t> _connections;
174 std::shared_ptr<Logger> _logger;
175 int _listenSock;
176 int _epollFd;
177 int _eventFd;
178 int _maxKeepAliveDrops;
179 int _lameConnectionTimeoutSeconds;
Austin Schuh9d823002019-04-14 12:53:17 -0700180 size_t _clientBufferSize;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700181 time_t _nextDeadConnectionCheck;
182
Austin Schuh9d823002019-04-14 12:53:17 -0700183 // Compression settings
184 bool _perMessageDeflateEnabled = false;
185
Austin Schuh24adb6b2015-09-06 17:37:40 -0700186 struct WebSocketHandlerEntry {
187 std::shared_ptr<WebSocket::Handler> handler;
188 bool allowCrossOrigin;
189 };
190 typedef std::unordered_map<std::string, WebSocketHandlerEntry> WebSocketHandlerMap;
191 WebSocketHandlerMap _webSocketHandlerMap;
192
193 std::list<std::shared_ptr<PageHandler>> _pageHandlers;
194
Austin Schuh9d823002019-04-14 12:53:17 -0700195 std::mutex _pendingExecutableMutex;
196 std::list<Executable> _pendingExecutables;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700197
198 pid_t _threadId;
199
200 std::string _staticPath;
201 std::atomic<bool> _terminate;
202 std::atomic<bool> _expectedTerminate;
203};
204
Austin Schuh9d823002019-04-14 12:53:17 -0700205} // namespace seasocks