/*---------------------------------------------------------------------------*/
/* webserverplugin.h                                                         */
/* copyright (c) innovaphone 2015 - 2019                                     */
/*                                                                           */
/*---------------------------------------------------------------------------*/

/*****************************************************************************/
/*                                   NOTE                                    */
/*                                   ----                                    */
/*                                                                           */
/* This file will be used by the innovaphone webserver and the interface     */
/* IWebserverPlugin. There is now need to use the class or defines in your   */
/* own projects.                                                             */
/*                                                                           */
/*****************************************************************************/

// Because of a "bug" (no support of transfer for files > 4G), some changes must be made
// in the webserver and the IWebserverPlugin. To not break backward compatibility, the
// IWebserverPlugin sends a protocol version to the webserver, for him to use a different
// handling of data sizes (64bit values instead of 32 bit). This also gives us the ability
// of other version depending changes (which of course should be broke down to a
// minimum - the first solution always should be a change or enhancement without the
// need of protocol version change). However, this version will be send a general
// purpose value when sending the WSM_SET_PLUGINS_WEB_ROOT, which is the very first
// message send from the IWebserverPlugin to the webserver.

static const size_t WS_RESPONSE_CHUNKED_V0 = 0xFFFFFFFF;


// Each connection ID above CONNECTION_ID_INT_START is for internal use.
#define CONNECTION_ID_INT_START     0xFFFFFF00
#define CONNECTION_ID_INTERNAL      CONNECTION_ID_INT_START
#define CONNECTION_ID_SET_CERT      (CONNECTION_ID_INT_START + 1)

// CACHE_FILE_MAX_AGE will be the max-age of a file inside the browsers cache;
// The value will be given in seconds. Because of the macro CACHE_FILE_MAX_AGE_FIELD
// no calculation (like 5 * 60) must be given!
#define CACHE_FILE_MAX_AGE          300

#define MACRO_TO_STRING_HELPER(x)   #x
#define MACRO_TO_STRING(x)          MACRO_TO_STRING_HELPER(x)
#define CACHE_FILE_MAX_AGE_FIELD    "cache-control: max-age=" MACRO_TO_STRING(CACHE_FILE_MAX_AGE)

// The data structure for MSM_SEND_META_DATA
#define MSG_META_DATA_COMPLETE_SIZE     0
#define MSG_META_DATA_ETAG              sizeof(dword)

#define MSG_META_DATA_SIZE              36

#define MAKE_DWORD(high, low)  dword((high << 16) | word(low))
#define HI_WORD(gp)            word(gp >> 16)
#define LOW_WORD(gp)           word(gp & 0xFFFF)

//// The general-purpose-values for defining, if a connection is encrypted or not. For now only used in passthrough.
//// Can be extented to others in the future.
//#define GP_CONNECTION_NOT_ENCRYPTED  0
//#define GP_CONNECTION_IS_ENCRYPTED   1

// The general-purpose-values used for sending the certificate...
#define GP_SEND_CERT_HOST_NAME  0
#define GP_SEND_CERT_DATA       1
#define GP_SEND_CERT_DONE       2


// There are three structures used by the Webserver / IWebserverPlugin for controlling / canceling.
// Theese structures are wsr_int-type_t, wsr_cancel_type_t and ws_webdav_result_t / wsr_type_t. However,
// the values only will be processed by handlers that have need for them. But because of that, if there
// is need to make changes, make sure that the integer-values of that defines never overlap. The number
// range useable for that defines are the following:
//
// Enum                range
// ---------------------------------
// wsr_int_type_t      0x00  - 0x0F
// wsr_cancel_type_t   0x10  - 0xFF
// ws_webdav_result_t  0x100 - ... (aslo wsr_type_t; no end defined for now).
//
// That should be enough for the use cases. However, that values would be handled as 16bit integers, so
// the must never ever exceed 0xFFFF.
typedef enum {
    WSR_INT_UNDEFINED = 0,
    WSR_INT_REDIRECT,
    WSR_INT_UNCHANGED,
    WSR_INT_HTTP_VERSION_UNSUPPORTED
} wsr_int_type_t;

#define CT_TO_INTERNAL_CT(x)    wsr_cancel_type_t((x))
#define INTERNAL_CT_TO_CT(x)    wsr_int_type_t((x))

typedef enum {
    WSM_NONE,
    WSM_REGISTER,
    WSM_REGISTER_RESULT,
    WSM_REGISTER_PASSTHROUGH,
    WSM_WEBSOCKET_LISTEN,
    WSM_GET_REQUEST,
    WSM_POST_REQUEST,
    WSM_PUT_REQUEST,
    WSM_PASSTHROUGH_REQUEST,
    WSM_SEND,
    WSM_SEND_RESULT,
    WSM_SEND_META_DATA,
    WSM_RECV,
    WSM_RECV_RESULT,
    WSM_SHUTDOWN,
    WSM_SHUTDOWN_COMPLETE,
    WSM_WEBSOCKET_LISTEN_RESULT,
    WSM_WEBSOCKET_MESSAGE,
    WSM_MESSAGE_HANDLER_CLOSED,
    WSM_PROPFIND_REQUEST,
    WSM_MOVE_REQUEST,
    WSM_COPY_REQUEST,
    WSM_MKCOL_REQUEST,
    WSM_DELETE_REQUEST,
    WSM_OPTIONS_REQUEST,
    WSM_LOCK_REQUEST,
    WSM_UNLOCK_REQUEST,
    WSM_PROPPATCH_REQUEST,
    WSM_SET_CERTIFICATE,
    WSM_REQUEST_USER_PASSWORD,
    WSM_REQUEST_USER_PASSWORD_RESULT,
    WSM_SET_PLUGINS_WEB_ROOT,
    WSM_SET_AUTH_CONFIG,
    WSM_SET_AUTH_CONFIG_RESULT,
    WSM_SET_PLUGINS_WEB_ROOT_RESULT
} ws_message_t;


typedef enum {
    WS_REGISTER_DONE,               // All is ok
    WS_REGISTER_PATH_INVALID,       // The given path is invalid
    WS_REGISTER_ALREADY_REGISTERD   // A data provider already registered to that resource
} ws_register_result_t;


typedef enum {
    WS_WEBSOCKET_CONNECT_ACCEPT,
    WS_WEBSOCKET_CONNECT_REJECT,
    WS_WEBSOCKET_CONNECT_COMPLETE,
    WS_WEBSOCKET_SEND,
    WS_WEBSOCKET_SEND_RESULT,
    WS_WEBSOCKET_RECV,
    WS_WEBSOCKET_RECV_RESULT,
    WS_WEBSOCKET_CLOSE,
    WS_WEBSOCKET_CLOSE_COMPLETE
} ws_msg_websocket_t;


typedef union {
    struct {
        bool connectionIsTLS : 1;
        bool redirectToTLS : 1;
        bool connectionKeepAlive : 1;
        bool localHost : 1;
        bool sysClient : 1;
        bool eTag : 1;
    };
    dword dwordVal;
} connection_flags_t;


// NOTE: The detail data mechanism had been made to support backward compatibility. So if there is need to
// ===== add new data types, the have to be added at the end of the enum list below!
typedef enum : byte {
    DDT_REQUEST_PATH,
    DDT_REGISTERED_PATH_FOR_REQUEST,
    DDT_ETAG,
    DDT_RANGES,
    DDT_CONNECTION_FLAGS,
    DDT_HEADER_FIELDS,
    DDT_HOST,
    DDT_PATH_TO_REGISTER,
    DDT_AUTH_USER,
    DDT_AUTH_PASSWORD,
    DDT_WS_SHUTDOWN_REASON,
    DDT_TRANSFER_INFO,
    DDT_CERTIFICATE_HOST,
    DDT_CERTIFICATE_PASSWORD,
    DDT_CERTIFICATE_SIZE,
    DDT_REDIRECT_PATH,
    DDT_DYNAMIC_AUTHENTICATION,
    DDT_DATA_SIZE,
    DDT_CHUNK_ENCODED_TRANSFER,
	DDT_STATUS_CODE,
    DDT_STATUS_DESCRIPTION,
    DDT_REMOTE_ADDRESS,
    DDT_REMOTE_PORT
} extender_header_item_type_t;

#define EXTENDED_HEADER_SIZE             (sizeof(byte) + sizeof(word))   // 3 bytes of header for each detail data: 1 byte for the type id, 2 types for the data length.


typedef struct {
    wsr_type_t resourceType;
    wsr_flags_t flags;
    ulong64 dataSize;
} transfer_info_t;


typedef struct {
    char * certHostName;
    size_t certHostNameLen;
    char * certPassword;
    size_t certPasswordLen;
    dword certSize;
} certificate_header_t;

#define ALLOC_STACK_BUFFER(size, maxSize)   (size <= maxSize ? static_cast<byte *>(alloca(size)) : nullptr)

class ExtendedHeader {
private:
    class IInstanceLog * const log;
    uint16_t protocolVersion;
    char * requestPath;
    size_t requestPathLen;
    char * registeredPathForRequest;
    size_t registeredPathForRequestLen;
    char * etag;
    size_t etagLen;
    const class HTTPRange * ranges;
    size_t rangeCount;
    byte * rangesBuffer;
    connection_flags_t connectionFlags;
    char * fieldListBuffer;
    size_t fieldListBufferSize;
    char * host;
    size_t hostLen;
    char * pathToRegister;
    size_t pathToRegisterLen;
    char * authUser;
    size_t authUserLen;
    char * authPassword;
    size_t authPasswordLen;
    bool dynAuth;
    ws_close_reason_t wsCloseReason;
    transfer_info_t transferInfo;
    certificate_header_t certHeader;
    char * redirectPath;
    size_t redirectPathLen;
    ulong64 dataSize;
    bool chunkEncoded;
	dword statusCode;
	const char * statusDescription;
	size_t statusDescriptionLen;
    char remoteAddressBuffer[46];
    const char * remoteAddress;
    size_t remoteAddressLen;
    dword remotePort;

    struct {
        bool wsCloseReasonSet : 1;
        bool resourceTypeSet : 1;
        bool transferInfoSet : 1;
        bool releaseFieldListBuffer : 1;
        bool releaseBuffer : 1;
    };

    byte * buffer;
    size_t headerLength;

    byte * WriteDetailDataHeader(byte * dest, extender_header_item_type_t dt, word len);
    byte * WriteDetailData(byte * dest, extender_header_item_type_t dt, word len, const char * data);
    byte * WriteDetailData(byte * dest, extender_header_item_type_t dt, dword value);
    byte * WriteDetailData(byte * dest, extender_header_item_type_t dt, ulong64 value);
    byte * ReadDetailDataHeader(byte * buffer, extender_header_item_type_t &outType, word & outLen);

    void ClearData(ExtendedHeader & eh);
    void MoveData(ExtendedHeader & from);

public:
    ExtendedHeader(class IInstanceLog * const log, uint16_t protocolVersion);
    ExtendedHeader(const ExtendedHeader & eh) = delete;
    ExtendedHeader(ExtendedHeader && eh) noexcept : log(eh.log) { this->MoveData(eh); }
    ExtendedHeader & operator= (const ExtendedHeader & eh) = delete;
    ExtendedHeader & operator= (ExtendedHeader && eh) noexcept
    {
        this->MoveData(eh);
        this->ClearData(eh);
        return *this;
    }

    ~ExtendedHeader();

    void Encode(dword connectionID, byte * buffer = nullptr);
    void Decode(dword connectionID, byte * buffer, size_t bufferSize);
    void Reset()
    {
        if (this->releaseBuffer) free(this->buffer);
        if (this->releaseFieldListBuffer) free(this->fieldListBuffer);
        this->ClearData(*this);
    }

    const byte * Buffer() { return this->buffer; }
    size_t BufferSize() { return this->headerLength > 0 ? this->headerLength + sizeof(dword) : 0; }

    void SetConnectionFlags(connection_flags_t & connectionFlags);
    void SetRequestPath(const char * path, size_t pathLen);
    void SetRegisteredPathForRequest(const char * regPath, size_t regPathLen);
    void SetETag(const char * etag);
    void SetRequestRange(const class HTTPRange * ranges, int rangeCount);
    void SetHeaderFields(const class HTTPParser * parser);
    void SetHeaderFields(const char * headerFieldBuffer, size_t headerFieldBufferSize);
    void SetHost(const char * host, size_t hostLen);
    void SetPathToRegister(const char * path);
    void SetAuthInfos(const char * authUser, const char * authPassword);
    void SetWebsocketCloseReason(ws_close_reason_t closeReason);
    void SetTransferInfo(wsr_type_t resourceType, wsr_flags_t flags, ulong64 dataSize);
    void SetCertificateHeader(const char * certHost, const char * certPassword, dword certSize);
    void SetRedirectPath(const char * path);
    void SetDynamicAuthentication(bool dynAuth);
    void SetDataSize(ulong64 dataSize);
    void SetChunkEncodedTransfer(bool chunkEncoded);
	void SetStatusCode(dword code);
	void SetStatusDescription(const char * statusDescription);
    void SetRemoteAddress(class ISocket * socket);
	
    connection_flags_t ConnectionFlags() { return this->connectionFlags; }
    char * RequestPath() { return this->requestPath; }
    char * RegisteredPathForRequest() { return this->registeredPathForRequest; }
    char * ETag() { return this->etag; }
    size_t ETagLen() { return this->etagLen; }
    size_t RangeCount() { return this->rangeCount; }
    byte * RangesBuffer() { return this->rangesBuffer; }
    char * FieldListBuffer() { return this->fieldListBuffer; }
    size_t FieldListBufferSize() { return this->fieldListBufferSize; }
    char * Host() { return this->host; }
    char * PathToRegister() { return this->pathToRegister; }
    size_t PathToRegisterLen() { return this->pathToRegisterLen; }
    char * AuthUser() { return this->authUser; }
    size_t AuthUserLen() { return this->authUserLen; }
    char * AuthPassword() { return this->authPassword; }
    size_t AuthPasswordLen() { return this->authPasswordLen; }
    ws_close_reason_t SetWebsocketCloseReason() { return this->wsCloseReason; }
    transfer_info_t TransferInfo() { return this->transferInfo; }
    certificate_header_t CertificateHeader() { return this->certHeader; }
    char * RedirectPath() { return this->redirectPath; }
    bool DynamicAuthentication() { return this->dynAuth; }
    ulong64 DataSize() { return this->dataSize; }
    bool ChunkEncodedTransfer() { return this->chunkEncoded; }
	dword GetStatusCode() { return this->statusCode; }
	const char * GetStatusDescription() { return this->statusDescription; }
    const char * GetRemoteAddress() { return this->remoteAddress; }
    word GetRemotePort() { return static_cast<word>(this->remotePort); }
};


// The Websocket flags will be or-ed to the websocket message. This finally will
// give a word set to the GeneralPurpose field of IWebserverMessage.
typedef enum {
    WS_WEBSOCKET_FLAG_NONE          =   0x0000,
    WS_WEBSOCKET_FLAG_TEXT          =   0x8000,
    WS_WEBSOCKET_FLAG_FRAGMENTED    =   0x4000
} ws_websocket_flags_t;

inline const char * WsMessageToStr(ws_message_t msg)
{
    const char * res = "<ERROR: type unkown>";

    switch (msg)
    {
    case WSM_NONE:                  res = "WSM_NONE"; break;
    case WSM_REGISTER:              res = "WSM_REGISTER"; break;
    case WSM_REGISTER_RESULT:       res = "WSM_REGISTER_RESULT"; break;
    case WSM_REGISTER_PASSTHROUGH:  res = "WSM_REGISTER_PASSTHROUGH"; break;
    case WSM_WEBSOCKET_LISTEN:      res = "WSM_WEBSOCKET_LISTEN"; break;
    case WSM_GET_REQUEST:           res = "WSM_GET_REQUEST"; break;
    case WSM_POST_REQUEST:          res = "WSM_POST_REQUEST"; break;
    case WSM_PUT_REQUEST:           res = "WSM_PUT_REQUEST"; break;
    case WSM_PASSTHROUGH_REQUEST:   res = "WSM_PASSTHROUGH_REQUEST"; break;
    case WSM_SEND:                  res = "WSM_SEND"; break;
    case WSM_SEND_RESULT:           res = "WSM_SEND_RESULT"; break;
    case WSM_SEND_META_DATA:        res = "WSM_SEND_META_DATA"; break;
    case WSM_RECV:                  res = "WSM_RECV"; break;
    case WSM_RECV_RESULT:           res = "WSM_RECV_RESULT"; break;
    case WSM_SHUTDOWN:              res = "WSM_SHUTDOWN"; break;
    case WSM_SHUTDOWN_COMPLETE:     res = "WSM_SHUTDOWN_COMPLETE"; break;
    case WSM_WEBSOCKET_LISTEN_RESULT: res = "WSM_WEBSOCKET_LISTEN_RESULT"; break;
    case WSM_WEBSOCKET_MESSAGE:     res = "WSM_WEBSOCKET_MESSAGE"; break;
    case WSM_MESSAGE_HANDLER_CLOSED: res = "WSM_MESSAGE_HANDLER_CLOSED"; break;
    case WSM_PROPFIND_REQUEST:      res = "WSM_PROPFIND_REQUEST"; break;
    case WSM_MOVE_REQUEST:          res = "WSM_MOVE_REQUEST"; break;
    case WSM_COPY_REQUEST:          res = "WSM_COPY_REQUEST"; break;
    case WSM_MKCOL_REQUEST:         res = "WSM_MKCOL_REQUEST"; break;
    case WSM_DELETE_REQUEST:        res = "WSM_DELETE_REQUEST"; break;
    case WSM_OPTIONS_REQUEST:       res = "WSM_OPTIONS_REQUEST"; break;
    case WSM_LOCK_REQUEST:          res = "WSM_LOCK_REQUEST"; break;
    case WSM_UNLOCK_REQUEST:        res = "WSM_UNLOCK_REQUEST"; break;
    case WSM_PROPPATCH_REQUEST:     res = "WSM_PROPPATCH_REQUEST"; break;
    case WSM_SET_CERTIFICATE:       res = "WSM_SET_CERTIFICATE"; break;
    case WSM_REQUEST_USER_PASSWORD: res = "WSM_REQUEST_USER_PASSWORD"; break;
    case WSM_REQUEST_USER_PASSWORD_RESULT: res = "WSM_REQUEST_USER_PASSWORD_RESULT"; break;
    case WSM_SET_PLUGINS_WEB_ROOT:  res = "WSM_SET_PLUGINS_WEB_ROOT"; break;
    case WSM_SET_AUTH_CONFIG:       res = "WSM_SET_AUTH_CONFIG"; break;
    case WSM_SET_AUTH_CONFIG_RESULT: res = "WSM_SET_AUTH_CONFIG_RESULT"; break;
    case WSM_SET_PLUGINS_WEB_ROOT_RESULT: res = "WSM_SET_PLUGINS_WEB_ROOT_RESULT"; break;
    }

    return res;
}


inline const char * WsWebsocketMsgToStr(ws_msg_websocket_t msg)
{
    const char * res = "<ERROR: type unkown>";
    switch (msg)
    {
    case WS_WEBSOCKET_CONNECT_ACCEPT:   res = "WS_WEBSOCKET_CONNECT_ACCEPT"; break;
    case WS_WEBSOCKET_CONNECT_REJECT:   res = "WS_WEBSOCKET_CONNECT_REJECT"; break;
    case WS_WEBSOCKET_CONNECT_COMPLETE: res = "WS_WEBSOCKET_CONNECT_COMPLETE"; break;
    case WS_WEBSOCKET_SEND:             res = "WS_WEBSOCKET_SEND"; break;
    case WS_WEBSOCKET_SEND_RESULT:      res = "WS_WEBSOCKET_SEND_RESULT"; break;
    case WS_WEBSOCKET_RECV:             res = "WS_WEBSOCKET_RECV"; break;
    case WS_WEBSOCKET_RECV_RESULT:      res = "WS_WEBSOCKET_RECV_RESULT"; break;
    case WS_WEBSOCKET_CLOSE:            res = "WS_WEBSOCKET_CLOSE"; break;
    case WS_WEBSOCKET_CLOSE_COMPLETE:   res = "WS_WEBSOCKET_CLOSE_COMPLETE"; break;
    }

    return res;
}

inline const char * WSResourceTypeToStr(wsr_type_t r)
{
    switch (r)
    {
    case WSP_RESPONSE_NO_TYPE: return "WSP_RESPONSE_NO_TYPE";
    case WSP_RESPONSE_BINARY: return "WSP_RESPONSE_BINARY";
    case WSP_RESPONSE_JAVA: return "WSP_RESPONSE_JAVA";
    case WSP_RESPONSE_WAV: return "WSP_RESPONSE_WAV";
    case WSP_RESPONSE_OGG: return "WSP_RESPONSE_OGG";
    case WSP_RESPONSE_MP3: return "WSP_RESPONSE_MP3";
    case WSP_RESPONSE_GIF: return "WSP_RESPONSE_GIF";
    case WSP_RESPONSE_PNG: return "WSP_RESPONSE_PNG";
    case WSP_RESPONSE_BMP: return "WSP_RESPONSE_BMP";
    case WSP_RESPONSE_ICON: return "WSP_RESPONSE_ICON";
    case WSP_RESPONSE_TTF: return "WSP_RESPONSE_TTF";
    case WSP_RESPONSE_EOT: return "WSP_RESPONSE_EOT";
    case WSP_RESPONSE_PDF: return "WSP_RESPONSE_PDF";
    case WSP_RESPONSE_TEXT: return "WSP_RESPONSE_TEXT";
    case WSP_RESPONSE_XML: return "WSP_RESPONSE_XML";
    case WSP_RESPONSE_CSS: return "WSP_RESPONSE_CSS";
    case WSP_RESPONSE_JAVASCRIPT: return "WSP_RESPONSE_JAVASCRIPT";
    case WSP_RESPONSE_JSON: return "WSP_RESPONSE_JSON";
    case WSP_RESPONSE_HTML: return "WSP_RESPONSE_HTML";
    case WSP_RESPONSE_SVG: return "WSP_RESPONSE_SVG";
    case WSP_RESPONSE_WOFF: return "WSP_RESPONSE_WOFF";
    case WSP_RESPONSE_G711: return "WSP_RESPONSE_G711";
    case WSP_RESPONSE_G722: return "WSP_RESPONSE_G722";
    case WSP_RESPONSE_G729: return "WSP_RESPONSE_G729";
    case WSP_RESPONSE_JPEG: return "WSP_RESPONSE_JPEG";
    case WSP_RESPONSE_MP4: return "WSP_RESPONSE_MP4";
    case WSP_RESPONSE_WEBM: return "WSP_RESPONSE_WEBM";
    case WSP_RESPONSE_PEM: return "WSP_RESPONSE_PEM";
    case WSP_RESPONSE_MOBILECONFIG: return "WSP_RESPONSE_MOBILECONFIG";
    case WSP_RESPONSE_CACHE_MANIFEST: return "WSP_RESPONSE_CACHE_MANIFEST";
    case WSP_RESPONSE_OPUS: return "WSP_RESPONSE_OPUS";
    case WSP_RESPONSE_CSV: return "WSP_RESPONSE_CSV";
    case WSP_RESPONSE_APPLICATION_XML: return "WSP_RESPONSE_APPLICATION_XML";
    case WSP_RESPONSE_MJS: return "WSP_RESPONSE_MJS";
    case WSP_RESPONSE_WASM: return "WSP_RESPONSE_WASM";
    case WSP_RESPONSE_TFLITE: return "WSP_RESPONSE_TFLITE";
    }

    if (r <= int(WSR_INT_UNCHANGED)) {
        switch (wsr_int_type_t(r))
        {
        case WSR_INT_UNDEFINED: return "WSP_INT_UNDEFINED (internal)";
        case WSR_INT_REDIRECT: return "WSP_INT_REDIRECT (internal)";
        case WSR_INT_UNCHANGED: return "WSP_INT_UNCHANGED (internal)";
        case WSR_INT_HTTP_VERSION_UNSUPPORTED: return "WSR_INT_HTTP_VERSION_UNSUPPORTED (internal)";
        }
    }

    return "<unkown wsr_type_t>"; //  Make the compiler happy
}


inline const char * WSCloseReasonToStr(ws_close_reason_t c)
{
    switch (c)
    {
    case WSCR_NORMAL_CLOSE: return "WSCR_NORMAL_CLOSE";
    case WSCR_BUFFER_OVERFLOW: return "WSCR_BUFFER_OVERFLOW";
    case WSCR_SOCKET_LOST: return "WSCR_SOCKET_LOST";
    case WSCR_ERROR: return "WSCR_ERROR";
    case WSCR_DOUBLE_RECV_CALL: return "WSCR_DOUBLE_RECV_CALL";
    }
}


inline const char * WSRequestTypeToStr(ws_request_type_t rq)
{
    switch (rq)
    {
    case WS_REQUEST_GET:         return "WS_REQUEST_GET";
    case WS_REQUEST_POST:        return "WS_REQUEST_POST";
    case WS_REQUEST_PUT:         return "WS_REQUEST_PUT";
    case WS_REQUEST_PASSTHROUGH: return "WS_REQUEST_PASSTHROUGH";
    case WS_REQUEST_PROPFIND:    return "WS_REQUEST_PROPFIND";
    case WS_REQUEST_MOVE:        return "WS_REQUEST_MOVE";
    case WS_REQUEST_COPY:        return "WS_REQUEST_COPY";
    case WS_REQUEST_MKCOL:       return "WS_REQUEST_MKCOL";
    case WS_REQUEST_DELETE:      return "WS_REQUEST_DELETE";
    case WS_REQUEST_OPTIONS:     return "WS_REQUEST_OPTIONS";
    case WS_REQUEST_LOCK:        return "WS_REQUEST_LOCK";
    case WS_REQUEST_UNLOCK:      return "WS_REQUEST_UNLOCK";
    case WS_REQUEST_PROPPATCH:   return "WS_REQUEST_PROPPATCH";
    }
    return "<unkown ws_request_type_t>"; //  Make the compiler happy
}


class IWebserverMessage {
public:
    static IWebserverMessage * Create(class ISocket * const socket, IInstanceLog * const log);

    virtual ~IWebserverMessage() {}

    virtual void Shutdown() = 0;
    virtual void Send(const char * handlerName, dword connectionID, ws_message_t message, dword generalPurpos = 0, const void * dataBuf = NULL, size_t dataSize = 0) = 0;
    virtual void WebsocketSend(dword connectionID, ws_msg_websocket_t message, ws_websocket_flags_t flags, const byte * data, size_t len) = 0;
    virtual void SetBufferData(const byte * source) = 0;
    virtual void LogRecvMessage(const char * callingFncName, byte * buf, size_t size) = 0;

    virtual byte * GetHeaderBuffer() = 0;
    virtual size_t GetHeaderSize() = 0;

    virtual dword GetConnectionID() = 0;
    virtual ws_message_t GetMessageID() = 0;
    virtual ws_msg_websocket_t GetWebsocketMessageID() = 0;
    virtual ws_websocket_flags_t GetWebsocketFlags() = 0;
    virtual dword GetGeneralPurpose() = 0;
    virtual size_t GetDataSize() = 0;
};
