
/*---------------------------------------------------------------------------*/
/* http_method_handler.h                                                     */
/* copyright (c) innovaphone 2016                                            */
/*                                                                           */
/*---------------------------------------------------------------------------*/

struct http_field_item_t {
    char * fieldName;
    char * fieldValue;
    http_field_item_t * next;
};


typedef enum {
    HTTP_INIT_NONE,
    HTTP_INIT_AUTHENTICATE,
    HTTP_INIT_ERROR,
    HTTP_INIT_CRITICAL_ERROR,
    HTTP_INIT_DONE
} http_method_init_t;


typedef enum {
    AUTH_IN_PROCESS,
    AUTH_VERIFING_USER,
    AUTH_DONE,
    AUTH_FAILED,
    AUTH_ERROR
} auth_state_t;


class UHTTPMethodHandler {
public:
    virtual ~UHTTPMethodHandler() {}
    virtual dword GetConnectionID() = 0;
    virtual class HTTPAuthDigest * GetAuthenticationObject() = 0;
    virtual class HTTPDigestHostManager * GetDigestHostManager() = 0;
    virtual void SetAuthenticationObject(HTTPAuthDigest * authDigest) = 0;
    virtual void HTTPHandlerShutdown(class HTTPDataSource * const dataSource, bool closeConnection, const HTTPLib::http_result_t* resultMsg = nullptr) = 0;
    virtual bool HandleControlMessage(class HTTPHeader * resultHeader, dword message, class ExtendedHeader * additionalInfo = nullptr) = 0;
    virtual void RegisterWebsocketForPing(class UWebserverWebsocket * websocket) = 0;
    virtual void UnregisterWebsocketForPing(class UWebserverWebsocket * websocket) = 0;
};


class HTTPMethodHandler {
private:
    enum class consume_data_t {
        NONE,
        AUTHENTICATE,
        CANCEL_REQUEST
    };

    const char * handlerName;
    UHTTPMethodHandler * const user;
    ISocket * const remoteSocket;

    HTTPHeader * resultHeader;
    HTTPDataSource * dataSource;
    bool authenticationStarted;
    bool shutdownHandler;
    bool shutdownSocket;
    bool needSendHeader;
    bool sendDataChunkEncoded;
    bool firstChunkSent;

    consume_data_t needConsumeData;
    wsr_cancel_type_t cancelReason;

    bool requestIsChunkEncoded;
    int  chunkReadState;
    bool chunkRemoveNextCLRF;
    size_t chunkBytesToRead;
    ulong64 dataToSend;
    ulong64 requestContentLength;
    ulong64 totalDataReceived;

    byte * restDataBuffer;
    size_t restDataSize;
    byte * restDataBufPtr;
    byte * recvBuffer;
    ulong64 recvBufferSize; // Is ulong64 because of the dataSize that can be > 32bit - but recvBufferSize never will be greater then 64k because of limitations in the webserver logic.
    bool recvPartitial;
    byte * chunkRecvBufferPtr;

    const char * appRequestPath;
    size_t appRequestPathLen;
    class HTTPRegisteredPath * pathObjForRequest;

    char * fieldItemBuffer;
    http_field_item_t * fieldItemsStart;
    http_field_item_t * fieldItemsEnd;

    std::list<int> sendCountStack;

    auth_state_t IsAuthenticating(const HTTPParser * parser, HTTPRegisteredPath * rp, bool asyncInitCall);
    void StartAuthentication(const HTTPParser * parser, HTTPAuthDigest * authDigest);
    void AdjustRestBufferSize(size_t bytesHandled);

    void ChunkSendRecvBuffer(size_t len);
    void ChunkRecvNextPart();
    bool ChunkGetNextChunkSize();
    bool ChunkRemoveCurCLRF();

    bool RecvChunked();
    void RecvStreamed();

    void AddFieldsToHeader();
    void SetHeaderFields(class ExtendedHeader & extHeader);
    void SendRecvBufferToDataSource(size_t len);

    void CheckDataConsumingState();
    void EnableDataConsuming(consume_data_t reason) { this->needConsumeData = reason; }

protected:
    IInstanceLog * const log;
    connection_flags_t connFlags;
    http_method_init_t handlerInitState;
    bool dataSendingActive;
	dword statusCode;
	std::string statusDescription;

    UHTTPMethodHandler * User() { return user; }
    ISocket * Socket() { return remoteSocket; }
    HTTPHeader * ResultHeader() { return resultHeader; }
    class HTTPRegisteredPath * GetRegisteredPathForRequest() { return pathObjForRequest; }
    bool RequestIsChunkEncoded() { return this->requestIsChunkEncoded; }
    void EnableChunkEncodedSend() { this->sendDataChunkEncoded = true; }
    bool SendingChunckEncodedEnabled() { return this->sendDataChunkEncoded; }
    void ShutdownSocketOnClose() { shutdownSocket = true; }

    const char * RequestPath() { return this->appRequestPathLen == 0 ? "/" : this->appRequestPath; }
    size_t RequestPathLen() { return this->appRequestPathLen == 0 ? 1 : this->appRequestPathLen; }
    const char * RegisteredPathForRequest();
    size_t RegisteredPathForRequestLen();
	std::string GetStatusCodeStr(const HTTPLib::http_result_t & defaultStatus);

    void IncSendCount(int incBy = 1, bool createNew = true);
    void SetRestDataBuffer(byte * buffer, size_t size, size_t addDataToSend)
    {
        restDataBufPtr = restDataBuffer = buffer;
        restDataSize = size;
        dataToSend += addDataToSend;
    }

    virtual void SetResultMIMEType(wsr_type_t type);
    virtual void OnPrepareHeader() = 0;
    virtual void OnSendMetaData(dword info, class ExtendedHeader & extHeader) = 0;
    virtual void OnSend(dword info, byte * buffer, size_t size);
    virtual void OnRecv(dword size);
    virtual void OnHandlerClose(dword info, class ExtendedHeader & extHeader);
    virtual http_method_init_t OnHandlerInit(const class HTTPParser * parser, byte * buffer, size_t size) { return HTTP_INIT_DONE; }
    virtual void OnRequestUserPasswordResult(class ExtendedHeader & extHeader);
    virtual void Init(const class HTTPParser * parser, byte * buffer, size_t size, bool asyncInitCall);
    virtual void HandleCancel(dword info, class ExtendedHeader & extHeader);

public:
    HTTPMethodHandler(const char * handlerName, UHTTPMethodHandler * const user, class HTTPRegisteredPath * reqPathObj, class HTTPDataSource * dataSource, ISocket * const remoteSocket, IInstanceLog * const log, connection_flags_t connFlags);
    virtual ~HTTPMethodHandler();

    const char * Name() { return this->handlerName; }
	
    virtual void Init(const class HTTPParser * parser, byte * buffer, size_t size);
    virtual void SocketSendResult();
    virtual void SocketRecvResult(byte * buffer, size_t size);
    virtual void SocketShutdown(shutdownreason_t reason);
    virtual void AppResponse(dword id, dword info, byte * buffer, size_t size);

    // Because the HTTPWebsocketHandler also works as ISocket proxy (and doing this, implements ISocket::Shutdown()), we canot
    // name the function Shutdown() here...
    virtual void ShutdownHandler();
    virtual void InvalidateDatasource();

    HTTPDataSource * DataSource() { return dataSource; }

    bool IsPassTrough();
    http_method_init_t InitState() { return this->handlerInitState; }

    bool RequestIsChunkedEncoded() { return this->requestIsChunkEncoded; }
    ulong64 RequestContentLength() { return this->requestContentLength; }
    bool RequestDeliveredData() { return this->RequestIsChunkedEncoded() || this->RequestContentLength() > 0; }
    bool IsConsumingData() { return this->needConsumeData != consume_data_t::NONE; }
};


inline const HTTPLib::http_result_t * WebDAVResult2HTTPResult(ws_webdav_result_t result)
{
    switch (result)
    {
    case WEBDAV_RESULT_OK:                   return &HTTPLib::HTTP_200;
    case WEBDAV_RESULT_CREATED:              return &HTTPLib::HTTP_201;
    case WEBDAV_RESULT_NO_CONTENT:           return &HTTPLib::HTTP_204;
    case WEBDAV_RESULT_MULTIPLE_STATUS:      return &HTTPLib::HTTP_207;
    case WEBDAV_RESULT_FORBIDDEN:            return &HTTPLib::HTTP_403;
    case WEBDAV_RESULT_METHOD_NOT_ALLOWED:   return &HTTPLib::HTTP_405;
    case WEBDAV_RESULT_CONFLICT:             return &HTTPLib::HTTP_409;
    case WEBDAV_RESULT_PRECONDITION_FAILED:  return &HTTPLib::HTTP_412;
    case WEBDAV_RESULT_LOCKED:               return &HTTPLib::HTTP_423;
    case WEBDAV_RESULT_BAD_GATEWAY:          return &HTTPLib::HTTP_502;
    case WEBDAV_RESULT_INSUFFICIENT_STORAGE: return &HTTPLib::HTTP_507;
    }

    return NULL;
}
