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

class HTTPRegisteredPath : public istd::listElement<HTTPRegisteredPath> {
private:
    typedef enum {
        AT_NONE         = 0x00,
        AT_HTTP_LISTEN  = 0x01,
        AT_WEBSOCKET    = 0x02,
        AT_PASSTHROUGH  = 0x04
    } access_type_t;

    size_t pathlen;
    char * path;
    bool needAuth;
    char * authUser;
    char * authPwd;
    access_type_t accessType;

public:
    HTTPRegisteredPath(char * path, size_t pathLen)
    {
        this->path = NULL;
        this->needAuth = false;
        this->authUser = NULL;
        this->authPwd = NULL;
        this->pathlen = pathLen;
        if (pathLen > 0) {
            this->path = static_cast<char *>(malloc(this->pathlen + 1));
            memcpy(this->path, path, this->pathlen);
            this->path[this->pathlen] = '\0';
        }
        this->accessType = AT_NONE;
    }

    ~HTTPRegisteredPath()
    {
        free(path);
        free(authUser);
        free(authPwd);
    }

    void EnableHTTPListen() { this->accessType = access_type_t(this->accessType | AT_HTTP_LISTEN); }
    void EnableWebsocketSupport() { this->accessType = access_type_t(this->accessType | AT_WEBSOCKET); }
    void EnablePassthrough() { this->accessType = AT_PASSTHROUGH; }
    void SetNeedAuth(bool flag) { this->needAuth = flag; }
    void SetAuthUser(const char * authUser)
    {
        if (authUser != nullptr) this->needAuth = true;
        free(this->authUser);
        this->authUser = _strdup(authUser);
    }
    void SetAuthPwd(const char * authPwd)
    {
        free(this->authPwd);
        this->authPwd = _strdup(authPwd);
    }
    bool NeedAuthentication() { return this->needAuth; }
    bool DynamicAuthenticationEnabled() { return this->needAuth && this->authUser == nullptr; }
    const char * GetPath() { return path; }
    size_t GetPathLen() { return this->pathlen; }
    const char * GetAuthUser() { return authUser; }
    const char * GetAuthPwd() { return authPwd; }
    bool NeedToAuthenticate() { return authUser != NULL; }
    bool IsNoAccessTypeSet() { return this->accessType == AT_NONE; }
    bool IsHTTPListening() { return (this->accessType & AT_HTTP_LISTEN) == AT_HTTP_LISTEN; }
    bool SupportsWebsocket() { return (this->accessType & AT_WEBSOCKET) == AT_WEBSOCKET; }
    bool IsPassThrough() { return (this->accessType & AT_PASSTHROUGH) == AT_PASSTHROUGH; }
};


class HTTPDataSource : public USocket, public istd::listElement<HTTPDataSource>  {
protected:
    class Webserver * const webserver;
    IInstanceLog * const log;
    char * webRoot;
    uint8_t protocolVersion;
    ISocket * remoteSocket;
    void * buf;
    IWebserverMessage * webserverMessageSend;
    IWebserverMessage * webserverMessageRecv;
    class SetCertificateHandler * setCertificateHandler;

    class HTTPVfsEntry * httpPathRoot;
    class HTTPVfsEntry * websocketPath;
    class HTTPVfsEntry * passThroughPath;

    istd::list<HTTPRegisteredPath> registeredPathList;

    int state;
    size_t messageDataSize;

    istd::list<HTTPConnection> registeredConnections;

    void RegisterForPath(char * registerData, size_t registerDataLen);
    void RegisterPassthroughForPath(char * path, size_t pathLen);
    void HandleWebsocketListen(char * path, size_t pathLen);
    void HandleSetAuthConfig(byte * data, size_t dataSize);

public:
    HTTPDataSource(class Webserver * const webserver);
    virtual ~HTTPDataSource();
    
    virtual HTTPRegisteredPath * GetPathObject(const char * requestedPath, const char * reqPathEnd);
    const char * GetRoot() { return this->webRoot; }

    virtual void SocketAcceptComplete(ISocket * const socket) 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;

    void AddConnectionUsingDataSource(class HTTPConnection * connection);
    virtual void RemoveConnectionUsingDataSource(class HTTPConnection * connection);
    
    void Shutdown()
    {
        this->remoteSocket->Shutdown();
    }

    virtual void SendMessage(const char * name, dword connectionID, ws_message_t message, dword gp = 0, const byte * buffer = nullptr, size_t bufSize = 0)
    {
        webserverMessageSend->Send(name, connectionID, message, gp, buffer, bufSize);
    }

    void SendWebsocketMessage(dword connectionID, ws_msg_websocket_t message, ws_websocket_flags_t flags, const byte * data, size_t len)
    {
        webserverMessageSend->WebsocketSend(connectionID, message, flags, data, len);
    }

    uint8_t ProtocolVersion() { return this->protocolVersion; }
};
