
/*---------------------------------------------------------------------------*/
/* webserver.h                                                               */
/* copyright (c) innovaphone 2015                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/


#define INIT_LOCAL_SOCKET_DONE          0x0001
#define INIT_TLS_SOCKET_DONE            0x0002
#define INIT_TLSV6_SOCKET_DONE          0x0004
#define INIT_TCP_SOCKET_DONE            0x0008
#define INIT_TCPV6_SOCKET_DONE          0x0010
#define SHUTDOWN_INITIALIZED            0x0020

#define INIT_DONE                       (INIT_LOCAL_SOCKET_DONE | INIT_TLS_SOCKET_DONE | INIT_TLSV6_SOCKET_DONE | INIT_TCP_SOCKET_DONE | INIT_TCPV6_SOCKET_DONE)

#define WEBSERVER_SOCKET_BIND_FAILED    0

class UWebserver {
public:
    UWebserver() {}
    virtual ~UWebserver() {}
    virtual void WebserverInitDone(int tcpPort, int tlsPort) = 0;
};

class Webserver : public USocket, public UTimer, public IInstanceLog, public UAppServiceSocket, public UIoExec {
private:
    class IIoMux * const iomux;
    class UWebserver * user;
    class AppServiceSocket serviceSocket;
    app_arguments_t * args;
    class ITimer timerCleanUpDigestHosts;
    class ITimer timerReconnectSockets;
    class ITimer timerPingWebsocketConnections;
    class ISocketProvider * socketProvider;
    class ISocketProvider * socketProviderTLS;
    class ISocketProvider * socketProviderLocalSocket;
    class ISocketContext * tlsContext;
    class ISocket * serverSocket;
    class ISocket * serverSocketTLS;
    class ISocket * serverSocketv6;
    class ISocket * serverSocketTLSv6;
    class ISocket * serverSocketLocal;

    class HTTPDigestHostManager * digestHostManager;
    class btree * httpConnections;
    class HTTPVfsEntry * vfsRoot;
    istd::list<class HTTPDataSource> dataSourceList;
    istd::list<class UWebserverWebsocket> websocketList;
    class UWebserverWebsocket * curWSToPing;
    int pingCnt;
    ulong64 pingStartTS;

    dword lastConnectionID;
    
    bool localSocketBindDone;
    bool tcpSocketBindDone;
    bool tlsSocketBindDone;
    bool tcpSocketv6BindDone;
    bool tlsSocketv6BindDone;
    bool portsInUse;
    bool bindLocalFailed;
    bool bindv4Failed;
    bool bindv6Failed;
    bool shutDown;

    bool ipFilter;
    istd::list<class HTTPAllowedAddr> ipFilterAllowedAddrs;

    void BindSockets();
    void BindSocketTCPv6();
    void BindSocketTLSv6();
    class HTTPVfsEntry * GetVfsEntryForPath(const char * path, class HTTPRegisteredPath *& rpOut);
    dword GetNextConnectionID();
    void DecodeAppPassword();

    // overrides of IInstanceLogs
    const char * GetAppName() const override { return "webserver"; }
    const char * GetAppDomain() const override { return ""; }

    // overrides UAppServiceSocket
    void AppServiceShutdown() override;
    void AppServiceShutdownTimeout() override;
    void AppStart(AppInstanceArgs * args) override {};
    void UpdateServerCertificate(const char * cert) override {};
    void AppServiceApps(istd::list<AppServiceApp> * appList) override {};
    void AppStop(const char * appName, const char * appDomain) override {};
    void AppSetLogFlags(const char * appName, const char * appDomain, ulong64 logFlags) override { this->logFlags = logFlags; };
    void PingNextWebsocketConnection();
    virtual void IoExec(void * execContext) override;
    void TryClose();
    void CheckSocketState();

public:
    Webserver(IIoMux * const iomux,
              ISocketProvider * const socketProvider,
              ISocketProvider * const socketProviderTLS,
              ISocketProvider * const socketProviderLocalSocket,
              app_arguments_t * args);
	virtual ~Webserver();
    void Init(class UWebserver * user = nullptr);

    void RegisterConnection(class HTTPConnection * session);
    void ReleaseConnection(class HTTPConnection * session);
    class HTTPDataSource * GetDataSourceForPath(const char * path, class HTTPRegisteredPath *& rpOut);
    void ReleaseDataSource(class HTTPDataSource * dataSource);

    word RegisterToVFS(class HTTPDataSource * dataSource, class HTTPRegisteredPath * path, bool isWebsocketListen, class HTTPVfsEntry *& vfsOut);
    void RemoveFromVFS(class HTTPDataSource * dataSource);
    class HTTPConnection * GetConnectionByID(dword connectionID);
    void RegisterWebsocketForPing(class UWebserverWebsocket * websocket);
    void UnregisterWebsocketForPing(class UWebserverWebsocket * websocket);

    class HTTPDigestHostManager * GetDigestHostManager();

    void SetServerCertificate(const char * password, const byte * cert, size_t certLen, const char * hostName);
    
    void SetIpFilter(bool enabled);
    void AddAllowedAddr(const char * addr);
    void RemoveAllowedAddrs();

    virtual void SocketBindResult(ISocket * const socket, const char * localAddr, word localPort) override;
    virtual void SocketListenResult(ISocket * const socket, const char * remoteAddr, word remotePort) override;
    virtual void SocketShutdown(ISocket * const socket, shutdownreason_t reason) override;
    virtual void SocketSendResult(ISocket * const socket) override;
    virtual void SocketRecvResult(ISocket * const socket, void * buf, size_t len) override;
    virtual void SocketRecvCanceled(ISocket * const socket, void * buf) override;

    virtual void TimerOnTimeout(ITimer * timer) override;

    bool IsClosing() { return this->shutDown; }

    const char * GetDefaultPath();
    const char * GetPassword();

    class IIoMux * IoMux() { return this->iomux; }
};

class HTTPAllowedAddr : public istd::listElement<HTTPAllowedAddr> {

public:
    HTTPAllowedAddr(const char * addr);
    ~HTTPAllowedAddr();
    char * addr;
};
