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

#define MESSAGE_SIZE_MAX        65535 // Maximum datasize per message...

enum WSC_STATES {
    WSC_STATE_ERROR = -1,
    WSCS_OFFLINE = 0,
    WSCS_RESOLVE_NAME,
    WSCS_SOCKET_CONNECTING,
    WSCS_CONNECT_TUNNEL,
    WSCS_HANDSHAKE_STARTED,
    WSCS_CONNECTED,
    WSCS_LISTENING,
    WSCS_READ_HEADER_EXT,
    WSCS_READ_DATA,
    WSCS_WAIT_FOR_USER_BUFFER,
    WSCS_REDIRECTING
};


class WebsocketClient : public IWebsocketClient, public USocket, public UDns, public UIoExec, public UTimer {
protected:
	IIoMux * const iomux;
	UWebsocketClient * const user;
	ISocketProvider * const tcpSocketProvider;
	ISocketProvider * const tlsSocketProvider;
    IDns * const dns;
    IInstanceLog * const log;
	ISocket * socket;
    WebsocketFrame * recvFrame;
    WebsocketFrame * sendFrame;
    class HTTPParser * httpParser;
    byte headerBuffer[1024];
	int state;
    bool recvCalled;
	char * hostName;
    char * hostPath;
	char hostIPAddress[46]; // The maximum length of address is 45 chars + closing zero (45 bytes is for IPv4 mapped IPv6 address)
    class HTTPProxies * proxies;
    class HTTPProxy * proxy;
    bool retryAuthenticated;
    char proxyIPAddress[46]; // The maximum length of address is 45 chars + closing zero (45 bytes is for IPv4 mapped IPv6 address)
    bool dnsRetried;
	bool useTLS;
	int  port;
	char * wsServerSecKey;
    UWebsocketClient::closereason_t userClosingReason;
	int closingReason;
	char * closingReasonMsg;
    byte * userRecvBuffer;
    size_t userRecvBufferLen;
    void * socketRecvBuffer;
    bool recvDataIsText;
    bool recvDataIsFragmented;
    bool isClosing;
    bool closeFrameSent;
    int srCompleteCnt;       // SendResult counter - complete
    int srUserCnt;           // SendResult counter - application sends
    bool isServerMode;
    bool hasPendingPing;
    unsigned pingIntervalSec;
    class ITimer * handshakeTimer;

	void ConnectSocket();
    void ConnectProxy();
    void ResolveDns();
    void ScanTunnelResponse(char * buf, size_t len);
	void SendHandshake();
	void ScanHandshakeResponse(char * buf, size_t len);
    void StartReadData();
    void ScanWebsocketFrame(void * buffer, size_t dataLen);
    //void HandleCloseFrame(size_t dataLen);
    void InitializeClose(bool waitForServerResponse);
    void FinalizeClose();
    void Ping();
    void SendFrame();
    void ReadPendingData(byte * newBuf, size_t newBufLen);
    bool IsRedirectResponse();
    bool ValidateHandshake();
    void HandshakeFailed();
    const char * GetHandshakeHeaderField(const char* headerFieldName, const char * neededValue);

    inline void UserWebsocketConnectComplete();
    inline void UserWebsocketCloseComplete(UWebsocketClient::closereason_t reason);
    inline void UserWebsocketSendResult();
    inline void UserWebsocketRecvResult(void * buf, size_t len, bool text, bool isFragment);
    inline void * UserWebsocketRecvBuffer(size_t len);
    inline void UserWebsocketRecvCanceled(void * buf);

public:
	WebsocketClient(IIoMux * const iomux,
					UWebsocketClient * const user,
					ISocketProvider * const tcpSocketProvider,
					ISocketProvider * const tlsSocketProvider,
                    IDns * const dns,
                    IInstanceLog * const log,
                    unsigned pingIntervalSec);
	virtual ~WebsocketClient();

    void Connect(const char * uri) override;
    void Close() override;
    void Cancel() override;
    void Send(const void * buf, size_t len, bool text = true) override;
    void Recv(void * buf = NULL, size_t len = 0) override;

    void SocketConnectComplete(ISocket * const socket) override;
    void SocketShutdown(ISocket * const socket, shutdownreason_t reason) override;
    void SocketSendResult(ISocket * const socket) override;
    void SocketRecvResult(ISocket * const socket, void * buf, size_t len) override;
    void SocketRecvCanceled(ISocket * const socket, void * buf) override;
    void SocketUpgradeToTLSComplete(ISocket* const socket) override;

    void IoExec(void * execContext) override;
    void TimerOnTimeout(ITimer * timer) override;
    void DnsGetHostByNameResult(const char * addr, bool isIPv6) override;
};
