blob: 4629a4329a15c8bb94e770b5374dced274f8309b [file] [log] [blame]
// Copyright (c) 2013, Matt Godbolt
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#pragma once
#include "seasocks/ServerImpl.h"
#include "seasocks/WebSocket.h"
#include <sys/types.h>
#include <atomic>
#include <cstdint>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace seasocks {
class Connection;
class Logger;
class PageHandler;
class Request;
class Response;
class Server : private ServerImpl {
public:
Server(std::shared_ptr<Logger> logger);
virtual ~Server();
void addPageHandler(std::shared_ptr<PageHandler> handler);
void addWebSocketHandler(const char* endpoint, std::shared_ptr<WebSocket::Handler> handler,
bool allowCrossOriginRequests = false);
// If we haven't heard anything ever on a connection for this long, kill it.
// This is possibly caused by bad WebSocket implementation in Chrome.
void setLameConnectionTimeoutSeconds(int seconds);
// Sets the maximum number of TCP level keepalives that we can miss before
// we let the OS consider the connection dead. We configure keepalives every second,
// so this is also the minimum number of seconds it takes to notice a badly-behaved
// dead connection, e.g. a laptop going into sleep mode or a hard-crashed machine.
// A value of 0 disables keep alives, which is the default.
void setMaxKeepAliveDrops(int maxKeepAliveDrops);
// Serves static content from the given port on the current thread, until terminate is called.
// Roughly equivalent to startListening(port); setStaticPath(staticPath); loop();
// Returns whether exiting was expected.
bool serve(const char* staticPath, int port);
// Starts listening on a given interface (in host order) and port.
// Returns true if all was ok.
bool startListening(uint32_t ipInHostOrder, int port);
// Starts listening on a port on all interfaces.
// Returns true if all was ok.
bool startListening(int port);
// Sets the path to server static content from.
void setStaticPath(const char* staticPath);
// Loop (until terminate called from another thread).
// Returns true if terminate() was used to exit the loop, false if there
// was an error.
bool loop();
// Runs a single iteration of the main loop, blocking for a given time.
// Returns immediately if terminate() has been called. Must be consistently
// called from the same thread. Returns an enum describing why it returned.
enum class PollResult {
Continue,
Terminated,
Error,
};
PollResult poll(int millisToBlock);
// Returns a file descriptor that can be polled for changes (e.g. by
// placing it in an epoll set. The poll() method above only need be called
// when this file descriptor is readable.
int fd() const { return _epollFd; }
// Terminate any loop() or poll(). May be called from any thread.
void terminate();
class Runnable {
public:
virtual ~Runnable() {}
virtual void run() = 0;
};
// Execute a task on the SeaSocks thread.
void execute(std::shared_ptr<Runnable> runnable);
private:
// From ServerImpl
virtual void remove(Connection* connection) override;
virtual bool subscribeToWriteEvents(Connection* connection) override;
virtual bool unsubscribeFromWriteEvents(Connection* connection) override;
virtual const std::string& getStaticPath() const override { return _staticPath; }
virtual std::shared_ptr<WebSocket::Handler> getWebSocketHandler(const char* endpoint) const override;
virtual bool isCrossOriginAllowed(const std::string &endpoint) const override;
virtual std::shared_ptr<Response> handle(const Request &request) override;
virtual std::string getStatsDocument() const override;
virtual void checkThread() const override;
bool makeNonBlocking(int fd) const;
bool configureSocket(int fd) const;
void handleAccept();
std::shared_ptr<Runnable> popNextRunnable();
void processEventQueue();
void shutdown();
void checkAndDispatchEpoll(int epollMillis);
void handlePipe();
enum NewState { KeepOpen, Close };
NewState handleConnectionEvents(Connection* connection, uint32_t events);
// Connections, mapped to initial connection time.
std::map<Connection*, time_t> _connections;
std::shared_ptr<Logger> _logger;
int _listenSock;
int _epollFd;
int _eventFd;
int _maxKeepAliveDrops;
int _lameConnectionTimeoutSeconds;
time_t _nextDeadConnectionCheck;
struct WebSocketHandlerEntry {
std::shared_ptr<WebSocket::Handler> handler;
bool allowCrossOrigin;
};
typedef std::unordered_map<std::string, WebSocketHandlerEntry> WebSocketHandlerMap;
WebSocketHandlerMap _webSocketHandlerMap;
std::list<std::shared_ptr<PageHandler>> _pageHandlers;
std::mutex _pendingRunnableMutex;
std::list<std::shared_ptr<Runnable>> _pendingRunnables;
pid_t _threadId;
std::string _staticPath;
std::atomic<bool> _terminate;
std::atomic<bool> _expectedTerminate;
};
} // namespace seasocks