BG
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
################################################################################
|
||||
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
|
||||
################################################################################
|
||||
|
||||
/.vs
|
||||
/out/build/x64-Debug
|
||||
40
ArqHelper.cpp
Normal file
40
ArqHelper.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ArqHelper.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
DWORD GenerateConversationID()
|
||||
{
|
||||
static volatile DWORD s_dwConvID = ::TimeGetTime();
|
||||
|
||||
DWORD dwConvID = ::InterlockedIncrement(&s_dwConvID);
|
||||
|
||||
if(dwConvID == 0)
|
||||
dwConvID = ::InterlockedIncrement(&s_dwConvID);
|
||||
|
||||
return dwConvID;
|
||||
}
|
||||
|
||||
#endif
|
||||
791
ArqHelper.h
Normal file
791
ArqHelper.h
Normal file
@@ -0,0 +1,791 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPTypeDef.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
#include "SocketHelper.h"
|
||||
|
||||
#include "common/FuncHelper.h"
|
||||
#include "common/BufferPool.h"
|
||||
#include "common/IODispatcher.h"
|
||||
#include "common/kcp/ikcp.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define DEFAULT_ARQ_NO_DELAY FALSE
|
||||
#define DEFAULT_ARQ_TURNOFF_NC FALSE
|
||||
#define DEFAULT_ARQ_FLUSH_INTERVAL 60
|
||||
#define DEFAULT_ARQ_RESEND_BY_ACKS 0
|
||||
#define DEFAULT_ARQ_SEND_WND_SIZE 128
|
||||
#define DEFAULT_ARQ_RECV_WND_SIZE 512
|
||||
#define DEFAULT_ARQ_MIN_RTO 30
|
||||
#define DEFAULT_ARQ_FAST_LIMIT 5
|
||||
#define DEFAULT_ARQ_MAX_TRANS_UNIT DEFAULT_UDP_MAX_DATAGRAM_SIZE
|
||||
#define DEFAULT_ARQ_MAX_MSG_SIZE DEFAULT_BUFFER_CACHE_CAPACITY
|
||||
#define DEFAULT_ARQ_HANND_SHAKE_TIMEOUT 5000
|
||||
|
||||
#define KCP_HEADER_SIZE 24
|
||||
#define KCP_MIN_RECV_WND 128
|
||||
|
||||
#define ARQ_MAX_HANDSHAKE_INTERVAL 2000
|
||||
|
||||
using Fn_ArqOutputProc = int (*)(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
|
||||
|
||||
DWORD GenerateConversationID();
|
||||
|
||||
/************************************************************************
|
||||
名称:ARQ 握手状态
|
||||
描述:标识当前连接的 ARQ 握手状态
|
||||
************************************************************************/
|
||||
enum EnArqHandShakeStatus
|
||||
{
|
||||
ARQ_HSS_INIT = 0, // 初始状态
|
||||
ARQ_HSS_PROC = 1, // 正在握手
|
||||
ARQ_HSS_SUCC = 2, // 握手成功
|
||||
};
|
||||
|
||||
struct TArqCmd
|
||||
{
|
||||
public:
|
||||
static const UINT16 MAGIC = 0xBB4F;
|
||||
static const UINT8 CMD_HANDSHAKE = 0x01;
|
||||
static const UINT8 FLAG_COMPLETE = 0x01;
|
||||
static const int PACKAGE_LENGTH = 12;
|
||||
|
||||
public:
|
||||
UINT16 magic;
|
||||
UINT8 cmd;
|
||||
UINT8 flag;
|
||||
DWORD selfID;
|
||||
DWORD peerID;
|
||||
|
||||
public:
|
||||
static BYTE* MakePackage(UINT8 cmd, UINT8 flag, DWORD selfID, DWORD peerID, UINT16 magic = MAGIC)
|
||||
{
|
||||
BYTE* buff = new BYTE[PACKAGE_LENGTH];
|
||||
|
||||
*((UINT16*)(buff + 0)) = magic;
|
||||
*((UINT8*)(buff + 2)) = cmd;
|
||||
*((UINT8*)(buff + 3)) = flag;
|
||||
*((DWORD*)(buff + 4)) = selfID;
|
||||
*((DWORD*)(buff + 8)) = peerID;
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
BYTE* MakePackage()
|
||||
{
|
||||
return MakePackage(cmd, flag, selfID, peerID, magic);
|
||||
}
|
||||
|
||||
BOOL Parse(const BYTE buff[PACKAGE_LENGTH])
|
||||
{
|
||||
magic = *((UINT16*)(buff + 0));
|
||||
cmd = *((UINT8*) (buff + 2));
|
||||
flag = *((UINT8*) (buff + 3));
|
||||
selfID = *((DWORD*) (buff + 4));
|
||||
peerID = *((DWORD*) (buff + 8));
|
||||
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
BOOL IsValid() {return (magic == MAGIC && cmd == CMD_HANDSHAKE && (flag & 0xFE) == 0);}
|
||||
|
||||
public:
|
||||
TArqCmd()
|
||||
{
|
||||
::ZeroMemory(this, sizeof(TArqCmd));
|
||||
}
|
||||
|
||||
TArqCmd(UINT8 c, UINT8 f, DWORD sid, DWORD pid)
|
||||
: magic (MAGIC)
|
||||
, cmd (c)
|
||||
, flag (f)
|
||||
, selfID(sid)
|
||||
, peerID(pid)
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
struct TArqAttr
|
||||
{
|
||||
BOOL bNoDelay;
|
||||
BOOL bTurnoffNc;
|
||||
DWORD dwResendByAcks;
|
||||
DWORD dwFlushInterval;
|
||||
DWORD dwSendWndSize;
|
||||
DWORD dwRecvWndSize;
|
||||
DWORD dwMinRto;
|
||||
DWORD dwMtu;
|
||||
DWORD dwFastLimit;
|
||||
DWORD dwMaxMessageSize;
|
||||
DWORD dwHandShakeTimeout;
|
||||
|
||||
public:
|
||||
TArqAttr( BOOL no_delay = DEFAULT_ARQ_NO_DELAY
|
||||
, BOOL turnoff_nc = DEFAULT_ARQ_TURNOFF_NC
|
||||
, DWORD resend_by_acks = DEFAULT_ARQ_RESEND_BY_ACKS
|
||||
, DWORD flush_interval = DEFAULT_ARQ_FLUSH_INTERVAL
|
||||
, DWORD send_wnd_size = DEFAULT_ARQ_SEND_WND_SIZE
|
||||
, DWORD recv_wnd_size = DEFAULT_ARQ_RECV_WND_SIZE
|
||||
, DWORD min_rto = DEFAULT_ARQ_MIN_RTO
|
||||
, DWORD mtu = DEFAULT_ARQ_MAX_TRANS_UNIT
|
||||
, DWORD fast_limit = DEFAULT_ARQ_FAST_LIMIT
|
||||
, DWORD max_msg_size = DEFAULT_ARQ_MAX_MSG_SIZE
|
||||
, DWORD hand_shake_timeout = DEFAULT_ARQ_HANND_SHAKE_TIMEOUT
|
||||
)
|
||||
: bNoDelay (no_delay)
|
||||
, bTurnoffNc (turnoff_nc)
|
||||
, dwResendByAcks (resend_by_acks)
|
||||
, dwFlushInterval (flush_interval)
|
||||
, dwSendWndSize (send_wnd_size)
|
||||
, dwRecvWndSize (recv_wnd_size)
|
||||
, dwMinRto (min_rto)
|
||||
, dwMtu (mtu)
|
||||
, dwFastLimit (fast_limit)
|
||||
, dwMaxMessageSize (max_msg_size)
|
||||
, dwHandShakeTimeout(hand_shake_timeout)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
}
|
||||
|
||||
BOOL IsValid() const
|
||||
{
|
||||
return ((int)dwResendByAcks >= 0) &&
|
||||
((int)dwFlushInterval > 0) &&
|
||||
((int)dwSendWndSize > 0) &&
|
||||
((int)dwRecvWndSize > 0) &&
|
||||
((int)dwMinRto > 0) &&
|
||||
((int)dwFastLimit >= 0) &&
|
||||
((int)dwHandShakeTimeout > 2 * (int)dwMinRto) &&
|
||||
((int)dwMtu >= 3 * KCP_HEADER_SIZE && dwMtu <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE) &&
|
||||
((int)dwMaxMessageSize > 0 && dwMaxMessageSize < ((KCP_MIN_RECV_WND - 1) * (dwMtu - KCP_HEADER_SIZE))) ;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<class T, class S> class CArqSessionT
|
||||
{
|
||||
public:
|
||||
CArqSessionT* Renew(T* pContext, S* pSocket, const TArqAttr& attr, DWORD dwPeerConvID = 0)
|
||||
{
|
||||
m_pContext = pContext;
|
||||
m_pSocket = pSocket;
|
||||
m_dwSelfConvID = ::GenerateConversationID();
|
||||
|
||||
DoRenew(attr, dwPeerConvID);
|
||||
RenewExtra(attr);
|
||||
|
||||
m_dwCreateTime = ::TimeGetTime();
|
||||
m_dwHSNextTime = m_dwCreateTime;
|
||||
m_dwHSSndCount = 0;
|
||||
m_bHSComplete = FALSE;
|
||||
m_enStatus = ARQ_HSS_PROC;
|
||||
|
||||
Check();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
if(!IsValid())
|
||||
return FALSE;
|
||||
|
||||
{
|
||||
CReentrantCriSecLock recvlock(m_csRecv);
|
||||
CReentrantCriSecLock sendlock(m_csSend);
|
||||
|
||||
if(!IsValid())
|
||||
return FALSE;
|
||||
|
||||
m_enStatus = ARQ_HSS_INIT;
|
||||
|
||||
DoReset();
|
||||
}
|
||||
|
||||
ResetExtra();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Check()
|
||||
{
|
||||
if(IsReady())
|
||||
{
|
||||
if(m_bHSComplete || DoHandShake())
|
||||
return Flush();
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
else if(IsHandShaking())
|
||||
return DoHandShake();
|
||||
else
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL DoHandShake()
|
||||
{
|
||||
if(!IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
unique_ptr<BYTE[]> bufCmdPtr;
|
||||
|
||||
{
|
||||
CReentrantCriSecLock recvlock(m_csRecv);
|
||||
|
||||
if(!IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DWORD dwCurrent = ::TimeGetTime();
|
||||
|
||||
if(::GetTimeGap32(m_dwCreateTime, dwCurrent) > m_pContext->GetHandShakeTimeout())
|
||||
{
|
||||
::SetLastError(ERROR_TIMEOUT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if((int)(::GetTimeGap32(m_dwHSNextTime, dwCurrent)) < 0)
|
||||
return TRUE;
|
||||
|
||||
m_dwHSNextTime = dwCurrent + MIN(m_kcp->interval * (++m_dwHSSndCount), ARQ_MAX_HANDSHAKE_INTERVAL);
|
||||
UINT8 iFlag = IsReady() ? TArqCmd::FLAG_COMPLETE : 0;
|
||||
|
||||
bufCmdPtr.reset(TArqCmd::MakePackage(TArqCmd::CMD_HANDSHAKE, iFlag, m_dwSelfConvID, m_dwPeerConvID));
|
||||
}
|
||||
|
||||
return m_pContext->DoSend(m_pSocket, bufCmdPtr.get(), TArqCmd::PACKAGE_LENGTH);
|
||||
}
|
||||
|
||||
BOOL Flush(BOOL bForce = FALSE)
|
||||
{
|
||||
if(!IsReady())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{
|
||||
CReentrantCriSecTryLock recvlock(m_csRecv);
|
||||
|
||||
if(recvlock.IsValid())
|
||||
{
|
||||
CReentrantCriSecTryLock sendlock(m_csSend);
|
||||
|
||||
if(sendlock.IsValid())
|
||||
{
|
||||
if(!IsReady())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(bForce)
|
||||
::ikcp_flush(m_kcp);
|
||||
else
|
||||
::ikcp_update(m_kcp, ::TimeGetTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int Send(const BYTE* pBuffer, int iLength)
|
||||
{
|
||||
if(!IsReady())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
int rs = NO_ERROR;
|
||||
|
||||
{
|
||||
CReentrantCriSecLock sendlock(m_csSend);
|
||||
|
||||
if(!IsReady())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
rs = ::ikcp_send(m_kcp, (const char*)pBuffer, iLength);
|
||||
if(rs < 0) rs = ERROR_INCORRECT_SIZE;
|
||||
}
|
||||
|
||||
if(rs == NO_ERROR)
|
||||
Flush(TRUE);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
int GetWaitingSend()
|
||||
{
|
||||
if(!IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock sendlock(m_csSend);
|
||||
|
||||
if(!IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ::ikcp_waitsnd(m_kcp);
|
||||
}
|
||||
|
||||
EnHandleResult Receive(const BYTE* pData, int iLength, BYTE* pBuffer, int iCapacity)
|
||||
{
|
||||
if(iLength >= KCP_HEADER_SIZE)
|
||||
return ReceiveArq(pData, iLength, pBuffer, iCapacity);
|
||||
else if(iLength == TArqCmd::PACKAGE_LENGTH)
|
||||
return ReceiveHandShake(pData);
|
||||
else
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_DATA);
|
||||
return HR_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
EnHandleResult ReceiveHandShake(const BYTE* pBuffer)
|
||||
{
|
||||
TArqCmd cmd;
|
||||
cmd.Parse(pBuffer);
|
||||
|
||||
if(!cmd.IsValid())
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_DATA);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
{
|
||||
CReentrantCriSecLock recvlock(m_csRecv);
|
||||
|
||||
if(!IsValid())
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_STATE);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
if(IsReady())
|
||||
{
|
||||
BOOL bReset = FALSE;
|
||||
|
||||
if(cmd.selfID != m_dwPeerConvID)
|
||||
bReset = TRUE;
|
||||
else if(cmd.peerID != m_dwSelfConvID)
|
||||
{
|
||||
if(cmd.peerID != 0)
|
||||
bReset = TRUE;
|
||||
else
|
||||
{
|
||||
if(::GetTimeGap32(m_dwCreateTime) > 2 * m_pContext->GetHandShakeTimeout())
|
||||
bReset = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(bReset)
|
||||
{
|
||||
::WSASetLastError(ERROR_CONNRESET);
|
||||
return HR_ERROR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_dwPeerConvID == 0)
|
||||
{
|
||||
m_dwPeerConvID = cmd.selfID;
|
||||
m_dwHSNextTime = ::TimeGetTime();
|
||||
m_dwHSSndCount = 0;
|
||||
}
|
||||
else if(cmd.selfID != m_dwPeerConvID)
|
||||
{
|
||||
::WSASetLastError(ERROR_CONNRESET);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
if(cmd.peerID == m_dwSelfConvID)
|
||||
{
|
||||
m_enStatus = ARQ_HSS_SUCC;
|
||||
return m_pContext->DoFireHandShake(m_pSocket);
|
||||
}
|
||||
else if(cmd.peerID != 0)
|
||||
{
|
||||
::WSASetLastError(ERROR_CONNRESET);
|
||||
return HR_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!m_bHSComplete && cmd.flag == TArqCmd::FLAG_COMPLETE)
|
||||
m_bHSComplete = TRUE;
|
||||
|
||||
DoHandShake();
|
||||
|
||||
return HR_OK;
|
||||
}
|
||||
|
||||
EnHandleResult ReceiveArq(const BYTE* pData, int iLength, BYTE* pBuffer, int iCapacity)
|
||||
{
|
||||
if(!IsReady()) return HR_IGNORE;
|
||||
|
||||
{
|
||||
CReentrantCriSecLock recvlock(m_csRecv);
|
||||
|
||||
if(!IsReady())
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_STATE);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
if(iLength < KCP_HEADER_SIZE)
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_DATA);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
int rs = ::ikcp_input(m_kcp, (const char*)pData, iLength);
|
||||
|
||||
if(rs != NO_ERROR)
|
||||
{
|
||||
::WSASetLastError(ERROR_INVALID_DATA);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
int iRead = ::ikcp_recv(m_kcp, (char*)pBuffer, iCapacity);
|
||||
|
||||
if(iRead >= 0)
|
||||
{
|
||||
EnHandleResult result = m_pContext->DoFireReceive(m_pSocket, pBuffer, iRead);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
return result;
|
||||
}
|
||||
else if(iRead == -3)
|
||||
{
|
||||
::WSASetLastError(ERROR_INCORRECT_SIZE);
|
||||
return HR_ERROR;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Flush(TRUE);
|
||||
|
||||
return HR_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void DoRenew(const TArqAttr& attr, DWORD dwPeerConvID = 0)
|
||||
{
|
||||
ASSERT(attr.IsValid());
|
||||
|
||||
DoReset();
|
||||
|
||||
m_dwPeerConvID = dwPeerConvID;
|
||||
m_kcp = ::ikcp_create(m_dwSelfConvID, m_pSocket);
|
||||
|
||||
::ikcp_nodelay(m_kcp, attr.bNoDelay ? 1 : 0, (int)attr.dwFlushInterval, (int)attr.dwResendByAcks, attr.bTurnoffNc ? 1 : 0);
|
||||
::ikcp_wndsize(m_kcp, (int)attr.dwSendWndSize, (int)attr.dwRecvWndSize);
|
||||
::ikcp_setmtu(m_kcp, attr.dwMtu);
|
||||
|
||||
m_kcp->rx_minrto = (int)attr.dwMinRto;
|
||||
m_kcp->fastlimit = (int)attr.dwFastLimit;
|
||||
m_kcp->output = m_pContext->GetArqOutputProc();
|
||||
}
|
||||
|
||||
void DoReset()
|
||||
{
|
||||
if(m_kcp != nullptr)
|
||||
{
|
||||
::ikcp_release(m_kcp);
|
||||
m_kcp = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
BOOL IsValid() const {return GetStatus() != ARQ_HSS_INIT;}
|
||||
BOOL IsHandShaking() const {return GetStatus() == ARQ_HSS_PROC;}
|
||||
BOOL IsReady() const {return GetStatus() == ARQ_HSS_SUCC;}
|
||||
IKCPCB* GetKcp() {return m_kcp;}
|
||||
DWORD GetConvID() const {if(!IsValid()) return 0; return m_kcp->conv;}
|
||||
DWORD GetSelfConvID() const {return m_dwSelfConvID;}
|
||||
DWORD GetPeerConvID() const {return m_dwPeerConvID;}
|
||||
|
||||
EnArqHandShakeStatus GetStatus() const {return m_enStatus;}
|
||||
|
||||
protected:
|
||||
virtual void RenewExtra(const TArqAttr& attr) {}
|
||||
virtual void ResetExtra() {}
|
||||
|
||||
public:
|
||||
CArqSessionT()
|
||||
: m_pContext (nullptr)
|
||||
, m_pSocket (nullptr)
|
||||
, m_kcp (nullptr)
|
||||
, m_enStatus (ARQ_HSS_INIT)
|
||||
, m_dwSelfConvID(0)
|
||||
, m_dwPeerConvID(0)
|
||||
, m_dwCreateTime(0)
|
||||
, m_dwHSNextTime(0)
|
||||
, m_dwHSSndCount(0)
|
||||
, m_bHSComplete (FALSE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CArqSessionT()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
static CArqSessionT* Construct()
|
||||
{return new CArqSessionT();}
|
||||
|
||||
static void Destruct(CArqSessionT* pSession)
|
||||
{if(pSession) delete pSession;}
|
||||
|
||||
protected:
|
||||
T* m_pContext;
|
||||
S* m_pSocket;
|
||||
|
||||
private:
|
||||
BOOL m_bHSComplete;
|
||||
DWORD m_dwHSNextTime;
|
||||
DWORD m_dwHSSndCount;
|
||||
DWORD m_dwCreateTime;
|
||||
DWORD m_dwSelfConvID;
|
||||
DWORD m_dwPeerConvID;
|
||||
EnArqHandShakeStatus m_enStatus;
|
||||
|
||||
CReentrantCriSec m_csRecv;
|
||||
CReentrantCriSec m_csSend;
|
||||
IKCPCB* m_kcp;
|
||||
};
|
||||
|
||||
template<class T, class S> class CArqSessionPoolT;
|
||||
|
||||
template<class T, class S> class CArqSessionExT : public CArqSessionT<T, S>, public CSafeCounter
|
||||
{
|
||||
using __super = CArqSessionT<T, S>;
|
||||
|
||||
using __super::Reset;
|
||||
|
||||
friend class CArqSessionPoolT<T, S>;
|
||||
|
||||
public:
|
||||
DWORD GetFreeTime () const {return m_dwFreeTime;}
|
||||
FD GetTimer () const {return m_fdTimer;}
|
||||
|
||||
protected:
|
||||
virtual void RenewExtra(const TArqAttr& attr)
|
||||
{
|
||||
ResetCount();
|
||||
|
||||
m_fdTimer = m_ioDispatcher.AddTimer(attr.dwFlushInterval, this);
|
||||
ASSERT(IS_VALID_FD(m_fdTimer));
|
||||
}
|
||||
|
||||
virtual void ResetExtra()
|
||||
{
|
||||
m_ioDispatcher.DelTimer(m_fdTimer);
|
||||
|
||||
m_dwFreeTime = ::TimeGetTime();
|
||||
m_fdTimer = INVALID_FD;
|
||||
}
|
||||
|
||||
public:
|
||||
CArqSessionExT(CIODispatcher& ioDispatcher)
|
||||
: m_ioDispatcher(ioDispatcher)
|
||||
, m_fdTimer (INVALID_FD)
|
||||
, m_dwFreeTime (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CArqSessionExT()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
static CArqSessionExT* Construct(CIODispatcher& ioDispatcher)
|
||||
{return new CArqSessionExT(ioDispatcher);}
|
||||
|
||||
static void Destruct(CArqSessionExT* pSession)
|
||||
{if(pSession) delete pSession;}
|
||||
|
||||
private:
|
||||
CIODispatcher& m_ioDispatcher;
|
||||
|
||||
FD m_fdTimer;
|
||||
DWORD m_dwFreeTime;
|
||||
};
|
||||
|
||||
template<class T, class S> class CArqSessionPoolT : private CIOHandler
|
||||
{
|
||||
using CArqSessionEx = CArqSessionExT<T, S>;
|
||||
using TArqSessionList = CRingPool<CArqSessionEx>;
|
||||
using TArqSessionQueue = CCASQueue<CArqSessionEx>;
|
||||
|
||||
public:
|
||||
CArqSessionEx* PickFreeSession(T* pContext, S* pSocket, const TArqAttr& attr)
|
||||
{
|
||||
DWORD dwIndex;
|
||||
CArqSessionEx* pSession = nullptr;
|
||||
|
||||
if(m_lsFreeSession.TryLock(&pSession, dwIndex))
|
||||
{
|
||||
if(::GetTimeGap32(pSession->GetFreeTime()) >= m_dwSessionLockTime)
|
||||
ENSURE(m_lsFreeSession.ReleaseLock(nullptr, dwIndex));
|
||||
else
|
||||
{
|
||||
ENSURE(m_lsFreeSession.ReleaseLock(pSession, dwIndex));
|
||||
pSession = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(!pSession) pSession = CArqSessionEx::Construct(m_ioDispatcher);
|
||||
|
||||
ASSERT(pSession);
|
||||
return (CArqSessionEx*)pSession->Renew(pContext, pSocket, attr);
|
||||
}
|
||||
|
||||
void PutFreeSession(CArqSessionEx* pSession)
|
||||
{
|
||||
if(pSession->Reset())
|
||||
{
|
||||
#ifndef USE_EXTERNAL_GC
|
||||
ReleaseGCSession();
|
||||
#endif
|
||||
if(!m_lsFreeSession.TryPut(pSession))
|
||||
m_lsGCSession.PushBack(pSession);
|
||||
}
|
||||
}
|
||||
|
||||
void Prepare()
|
||||
{
|
||||
m_lsFreeSession.Reset(m_dwSessionPoolSize);
|
||||
|
||||
m_ioDispatcher.Start(this, m_pContext->GetPostReceiveCount(), m_pContext->GetWorkerThreadCount());
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_ioDispatcher.Stop();
|
||||
|
||||
m_lsFreeSession.Clear();
|
||||
|
||||
ReleaseGCSession(TRUE);
|
||||
ENSURE(m_lsGCSession.IsEmpty());
|
||||
}
|
||||
|
||||
void ReleaseGCSession(BOOL bForce = FALSE)
|
||||
{
|
||||
::ReleaseGCObj(m_lsGCSession, m_dwSessionLockTime, bForce);
|
||||
}
|
||||
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override
|
||||
{
|
||||
|
||||
if(events & _EPOLL_ALL_ERROR_EVENTS)
|
||||
return FALSE;
|
||||
|
||||
CArqSessionEx* pSession = (CArqSessionEx*)pv;
|
||||
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
if(!pSession->Check() && pSession->IsValid() && TUdpSocketObj::IsValid(pSession->m_pSocket))
|
||||
pSession->m_pContext->Disconnect(pSession->m_pSocket->connID);
|
||||
|
||||
::ReadTimer(pSession->GetTimer());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
public:
|
||||
void SetSessionLockTime (DWORD dwSessionLockTime) {m_dwSessionLockTime = dwSessionLockTime;}
|
||||
void SetSessionPoolSize (DWORD dwSessionPoolSize) {m_dwSessionPoolSize = dwSessionPoolSize;}
|
||||
void SetSessionPoolHold (DWORD dwSessionPoolHold) {m_dwSessionPoolHold = dwSessionPoolHold;}
|
||||
|
||||
DWORD GetSessionLockTime() {return m_dwSessionLockTime;}
|
||||
DWORD GetSessionPoolSize() {return m_dwSessionPoolSize;}
|
||||
DWORD GetSessionPoolHold() {return m_dwSessionPoolHold;}
|
||||
|
||||
public:
|
||||
CArqSessionPoolT(T* pContext,
|
||||
DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
|
||||
DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
|
||||
DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
|
||||
: m_pContext(pContext)
|
||||
, m_dwSessionPoolSize(dwPoolSize)
|
||||
, m_dwSessionPoolHold(dwPoolHold)
|
||||
, m_dwSessionLockTime(dwLockTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CArqSessionPoolT() {Clear();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CArqSessionPoolT)
|
||||
|
||||
public:
|
||||
static const DWORD DEFAULT_SESSION_LOCK_TIME;
|
||||
static const DWORD DEFAULT_SESSION_POOL_SIZE;
|
||||
static const DWORD DEFAULT_SESSION_POOL_HOLD;
|
||||
|
||||
private:
|
||||
T* m_pContext;
|
||||
|
||||
DWORD m_dwSessionLockTime;
|
||||
DWORD m_dwSessionPoolSize;
|
||||
DWORD m_dwSessionPoolHold;
|
||||
|
||||
TArqSessionList m_lsFreeSession;
|
||||
TArqSessionQueue m_lsGCSession;
|
||||
|
||||
CIODispatcher m_ioDispatcher;
|
||||
};
|
||||
|
||||
template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME;
|
||||
template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE;
|
||||
template<class T, class S> const DWORD CArqSessionPoolT<T, S>::DEFAULT_SESSION_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD;
|
||||
|
||||
#endif
|
||||
23
CMakeLists.txt
Normal file
23
CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(hpsocket)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
file(GLOB_RECURSE ALL_SRC_FILES CONFIGURE_DEPENDS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/*.c"
|
||||
)
|
||||
|
||||
add_library(hpsocket STATIC ${ALL_SRC_FILES})
|
||||
target_include_directories(hpsocket PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# 解包 brotli 的 .a 为 .o 文件
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/brotli_objects)
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_AR} -x ${CMAKE_CURRENT_SOURCE_DIR}/libbrotli.a
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/brotli_objects
|
||||
)
|
||||
|
||||
file(GLOB BROTLI_OBJECTS "${CMAKE_BINARY_DIR}/brotli_objects/*.o")
|
||||
target_sources(hpsocket PRIVATE ${BROTLI_OBJECTS})
|
||||
200
HPSocket-SSL.cpp
Normal file
200
HPSocket-SSL.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hpsocket/HPSocket-SSL.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "TcpServer.h"
|
||||
#include "TcpAgent.h"
|
||||
#include "TcpClient.h"
|
||||
#include "TcpPullServer.h"
|
||||
#include "TcpPullClient.h"
|
||||
#include "TcpPullAgent.h"
|
||||
#include "TcpPackServer.h"
|
||||
#include "TcpPackClient.h"
|
||||
#include "TcpPackAgent.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
#include "HttpServer.h"
|
||||
#include "HttpAgent.h"
|
||||
#include "HttpClient.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/******************************************************************** SSL Exports ********************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API ITcpServer* HP_Create_SSLServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return new CSSLServer(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpAgent* HP_Create_SSLAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return new CSSLAgent(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpClient* HP_Create_SSLClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return new CSSLClient(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullServer* HP_Create_SSLPullServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return (ITcpPullServer*)(new CSSLPullServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullAgent* HP_Create_SSLPullAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return (ITcpPullAgent*)(new CSSLPullAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullClient* HP_Create_SSLPullClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return (ITcpPullClient*)(new CSSLPullClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackServer* HP_Create_SSLPackServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return (ITcpPackServer*)(new CSSLPackServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackAgent* HP_Create_SSLPackAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return (ITcpPackAgent*)(new CSSLPackAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackClient* HP_Create_SSLPackClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return (ITcpPackClient*)(new CSSLPackClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLServer(ITcpServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLAgent(ITcpAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLClient(ITcpClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPullServer(ITcpPullServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPullAgent(ITcpPullAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPullClient(ITcpPullClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPackServer(ITcpPackServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPackAgent(ITcpPackAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SSLPackClient(ITcpPackClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/*************************************************************** Global Function Exports *************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext)
|
||||
{
|
||||
return CSSLContext::DefaultServerNameCallback(lpszServerName, pContext);
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID)
|
||||
{
|
||||
CSSLContext::RemoveThreadLocalState(dwThreadID);
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/******************************************************************** HTTPS Exports ******************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
HPSOCKET_API IHttpServer* HP_Create_HttpsServer(IHttpServerListener* pListener)
|
||||
{
|
||||
return (IHttpServer*)(new CHttpsServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpAgent* HP_Create_HttpsAgent(IHttpAgentListener* pListener)
|
||||
{
|
||||
return (IHttpAgent*)(new CHttpsAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpClient* HP_Create_HttpsClient(IHttpClientListener* pListener)
|
||||
{
|
||||
return (IHttpClient*)(new CHttpsClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpSyncClient* HP_Create_HttpsSyncClient(IHttpClientListener* pListener)
|
||||
{
|
||||
return (IHttpSyncClient*)(new CHttpsSyncClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpsServer(IHttpServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpsAgent(IHttpAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpsClient(IHttpClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpsSyncClient(IHttpSyncClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
749
HPSocket.cpp
Normal file
749
HPSocket.cpp
Normal file
@@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hpsocket/HPSocket.h"
|
||||
#include "TcpServer.h"
|
||||
#include "TcpAgent.h"
|
||||
#include "TcpClient.h"
|
||||
#include "TcpPullServer.h"
|
||||
#include "TcpPullClient.h"
|
||||
#include "TcpPullAgent.h"
|
||||
#include "TcpPackServer.h"
|
||||
#include "TcpPackClient.h"
|
||||
#include "TcpPackAgent.h"
|
||||
#include "HPThreadPool.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
#include "UdpServer.h"
|
||||
#include "UdpClient.h"
|
||||
#include "UdpCast.h"
|
||||
#include "UdpNode.h"
|
||||
#include "UdpArqServer.h"
|
||||
#include "UdpArqClient.h"
|
||||
#endif
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
#include "HttpServer.h"
|
||||
#include "HttpAgent.h"
|
||||
#include "HttpClient.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/****************************************************************** TCP/UDP Exports ******************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API ITcpServer* HP_Create_TcpServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return new CTcpServer(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpAgent* HP_Create_TcpAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return new CTcpAgent(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpClient* HP_Create_TcpClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return new CTcpClient(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullServer* HP_Create_TcpPullServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return (ITcpPullServer*)(new CTcpPullServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullAgent* HP_Create_TcpPullAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return (ITcpPullAgent*)(new CTcpPullAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPullClient* HP_Create_TcpPullClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return (ITcpPullClient*)(new CTcpPullClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackServer* HP_Create_TcpPackServer(ITcpServerListener* pListener)
|
||||
{
|
||||
return (ITcpPackServer*)(new CTcpPackServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackAgent* HP_Create_TcpPackAgent(ITcpAgentListener* pListener)
|
||||
{
|
||||
return (ITcpPackAgent*)(new CTcpPackAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API ITcpPackClient* HP_Create_TcpPackClient(ITcpClientListener* pListener)
|
||||
{
|
||||
return (ITcpPackClient*)(new CTcpPackClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpServer(ITcpServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpAgent(ITcpAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpClient(ITcpClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPullServer(ITcpPullServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPullAgent(ITcpPullAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPullClient(ITcpPullClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPackServer(ITcpPackServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPackAgent(ITcpPackAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_TcpPackClient(ITcpPackClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
HPSOCKET_API IUdpServer* HP_Create_UdpServer(IUdpServerListener* pListener)
|
||||
{
|
||||
return new CUdpServer(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API IUdpClient* HP_Create_UdpClient(IUdpClientListener* pListener)
|
||||
{
|
||||
return new CUdpClient(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API IUdpCast* HP_Create_UdpCast(IUdpCastListener* pListener)
|
||||
{
|
||||
return new CUdpCast(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API IUdpNode* HP_Create_UdpNode(IUdpNodeListener* pListener)
|
||||
{
|
||||
return new CUdpNode(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API IUdpArqServer* HP_Create_UdpArqServer(IUdpServerListener* pListener)
|
||||
{
|
||||
return (IUdpArqServer*)(new CUdpArqServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IUdpArqClient* HP_Create_UdpArqClient(IUdpClientListener* pListener)
|
||||
{
|
||||
return (IUdpArqClient*)(new CUdpArqClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpServer(IUdpServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpClient(IUdpClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpCast(IUdpCast* pCast)
|
||||
{
|
||||
delete pCast;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpNode(IUdpNode* pNode)
|
||||
{
|
||||
delete pNode;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpArqServer(IUdpArqServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_UdpArqClient(IUdpArqClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/*************************************************************** Global Function Exports *************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API DWORD HP_GetHPSocketVersion()
|
||||
{
|
||||
return ::GetHPSocketVersion();
|
||||
}
|
||||
|
||||
HPSOCKET_API LPCTSTR HP_GetSocketErrorDesc(EnSocketError enCode)
|
||||
{
|
||||
return ::GetSocketErrorDesc(enCode);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GetLastError()
|
||||
{
|
||||
return ::GetLastError();
|
||||
}
|
||||
|
||||
HPSOCKET_API LPCSTR SYS_GetLastErrorStr()
|
||||
{
|
||||
return ::GetLastErrorStr();
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SetSocketOption(SOCKET sock, int level, int name, LPVOID val, int len)
|
||||
{
|
||||
return ::SSO_SetSocketOption(sock, level, name, val, len);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_GetSocketOption(SOCKET sock, int level, int name, LPVOID val, int* len)
|
||||
{
|
||||
return ::SSO_GetSocketOption(sock, level, name, val, len);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_IoctlSocket(SOCKET sock, long cmd, ULONG* arg)
|
||||
{
|
||||
return ::SSO_IoctlSocket(sock, cmd, arg);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_fcntl_SETFL(FD fd, INT fl, BOOL bSet)
|
||||
{
|
||||
return ::fcntl_SETFL(fd, fl, bSet);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_NoBlock(SOCKET sock, BOOL bNoBlock)
|
||||
{
|
||||
return ::SSO_NoBlock(sock, bNoBlock);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_NoDelay(SOCKET sock, BOOL bNoDelay)
|
||||
{
|
||||
return ::SSO_NoDelay(sock, bNoDelay);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_DontLinger(SOCKET sock, BOOL bDont)
|
||||
{
|
||||
return ::SSO_DontLinger(sock, bDont);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_Linger(SOCKET sock, USHORT l_onoff, USHORT l_linger)
|
||||
{
|
||||
return ::SSO_Linger(sock, l_onoff, l_linger);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_RecvBuffSize(SOCKET sock, int size)
|
||||
{
|
||||
return ::SSO_RecvBuffSize(sock, size);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_SendBuffSize(SOCKET sock, int size)
|
||||
{
|
||||
return ::SSO_SendBuffSize(sock, size);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_RecvTimeOut(SOCKET sock, int ms)
|
||||
{
|
||||
return ::SSO_RecvTimeOut(sock, ms);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_SendTimeOut(SOCKET sock, int ms)
|
||||
{
|
||||
return ::SSO_SendTimeOut(sock, ms);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_SSO_ReuseAddress(SOCKET sock, EnReuseAddressPolicy opt)
|
||||
{
|
||||
return ::SSO_ReuseAddress(sock, opt);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GetSocketLocalAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
return ::GetSocketLocalAddress(socket, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GetSocketRemoteAddress(SOCKET socket, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
return ::GetSocketRemoteAddress(socket, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount)
|
||||
{
|
||||
return ::EnumHostIPAddresses(lpszHost, enType, lpppIPAddr, iIPAddrCount);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_FreeHostIPAddresses(LPTIPAddr* lppIPAddr)
|
||||
{
|
||||
return ::FreeHostIPAddresses(lppIPAddr);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType)
|
||||
{
|
||||
return ::IsIPAddress(lpszAddress, penType);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GetIPAddress(LPCTSTR lpszHost, TCHAR lpszIP[], int& iIPLenth, EnIPAddrType& enType)
|
||||
{
|
||||
return ::GetIPAddress(lpszHost, lpszIP, iIPLenth, enType);
|
||||
}
|
||||
|
||||
HPSOCKET_API ULONGLONG SYS_NToH64(ULONGLONG value)
|
||||
{
|
||||
return ::NToH64(value);
|
||||
}
|
||||
|
||||
HPSOCKET_API ULONGLONG SYS_HToN64(ULONGLONG value)
|
||||
{
|
||||
return ::HToN64(value);
|
||||
}
|
||||
|
||||
HPSOCKET_API USHORT SYS_SwapEndian16(USHORT value)
|
||||
{
|
||||
return ENDIAN_SWAP_16(value);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_SwapEndian32(DWORD value)
|
||||
{
|
||||
return ENDIAN_SWAP_32(value);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_IsLittleEndian()
|
||||
{
|
||||
return ::IsLittleEndian();
|
||||
}
|
||||
|
||||
HPSOCKET_API LPBYTE SYS_Malloc(int size)
|
||||
{
|
||||
return MALLOC(BYTE, size);
|
||||
}
|
||||
|
||||
HPSOCKET_API LPBYTE SYS_Realloc(LPBYTE p, int size)
|
||||
{
|
||||
return REALLOC(BYTE, p, size);
|
||||
}
|
||||
|
||||
HPSOCKET_API VOID SYS_Free(LPBYTE p)
|
||||
{
|
||||
FREE(p);
|
||||
}
|
||||
|
||||
HPSOCKET_API LPVOID SYS_Calloc(int number, int size)
|
||||
{
|
||||
return CALLOC(number, size);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GuessBase64EncodeBound(DWORD dwSrcLen)
|
||||
{
|
||||
return ::GuessBase64EncodeBound(dwSrcLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
|
||||
{
|
||||
return ::GuessBase64DecodeBound(lpszSrc, dwSrcLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::Base64Encode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::Base64Decode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
|
||||
{
|
||||
return ::GuessUrlEncodeBound(lpszSrc, dwSrcLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen)
|
||||
{
|
||||
return ::GuessUrlDecodeBound(lpszSrc, dwSrcLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::UrlEncode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::UrlDecode(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
#ifdef _ZLIB_SUPPORT
|
||||
|
||||
HPSOCKET_API int SYS_Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::Compress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel, int iMethod, int iWindowBits, int iMemLevel, int iStrategy)
|
||||
{
|
||||
return ::CompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iLevel, iMethod, iWindowBits, iMemLevel, iStrategy);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::Uncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits)
|
||||
{
|
||||
return ::UncompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iWindowBits);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GuessCompressBound(DWORD dwSrcLen, BOOL bGZip)
|
||||
{
|
||||
return ::GuessCompressBound(dwSrcLen, bGZip);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::GZipCompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::GZipUncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen)
|
||||
{
|
||||
return ::GZipGuessUncompressBound(lpszSrc, dwSrcLen);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _BROTLI_SUPPORT
|
||||
|
||||
HPSOCKET_API int SYS_BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::BrotliCompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality, int iWindow, int iMode)
|
||||
{
|
||||
return ::BrotliCompressEx(lpszSrc, dwSrcLen, lpszDest, dwDestLen, iQuality, iWindow, (BrotliEncoderMode)iMode);
|
||||
}
|
||||
|
||||
HPSOCKET_API int SYS_BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen)
|
||||
{
|
||||
return ::BrotliUncompress(lpszSrc, dwSrcLen, lpszDest, dwDestLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API DWORD SYS_BrotliGuessCompressBound(DWORD dwSrcLen)
|
||||
{
|
||||
return ::BrotliGuessCompressBound(dwSrcLen);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _ICONV_SUPPORT
|
||||
|
||||
HPSOCKET_API BOOL SYS_CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen)
|
||||
{
|
||||
return ::CharsetConvert(lpszFromCharset, lpszToCharset, lpszInBuf, iInBufLen, lpszOutBuf, iOutBufLen);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
|
||||
{
|
||||
return ::GbkToUnicodeEx(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::UnicodeToGbkEx(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength)
|
||||
{
|
||||
return ::Utf8ToUnicodeEx(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::UnicodeToUtf8Ex(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::GbkToUtf8Ex(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::Utf8ToGbkEx(szSrc, iSrcLength, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
|
||||
{
|
||||
return ::GbkToUnicode(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::UnicodeToGbk(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength)
|
||||
{
|
||||
return ::Utf8ToUnicode(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::UnicodeToUtf8(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::GbkToUtf8(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL SYS_Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength)
|
||||
{
|
||||
return ::Utf8ToGbk(szSrc, szDest, iDestLength);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/******************************************************************** HTTP Exports *******************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
HPSOCKET_API IHttpServer* HP_Create_HttpServer(IHttpServerListener* pListener)
|
||||
{
|
||||
return (IHttpServer*)(new CHttpServer(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpAgent* HP_Create_HttpAgent(IHttpAgentListener* pListener)
|
||||
{
|
||||
return (IHttpAgent*)(new CHttpAgent(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpClient* HP_Create_HttpClient(IHttpClientListener* pListener)
|
||||
{
|
||||
return (IHttpClient*)(new CHttpClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API IHttpSyncClient* HP_Create_HttpSyncClient(IHttpClientListener* pListener)
|
||||
{
|
||||
return (IHttpSyncClient*)(new CHttpSyncClient(pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpServer(IHttpServer* pServer)
|
||||
{
|
||||
delete pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpAgent(IHttpAgent* pAgent)
|
||||
{
|
||||
delete pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpClient(IHttpClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_HttpSyncClient(IHttpSyncClient* pClient)
|
||||
{
|
||||
delete pClient;
|
||||
}
|
||||
|
||||
/**************************************************************************/
|
||||
/*************************** HTTP Cookie 管理方法 **************************/
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
|
||||
{
|
||||
return g_CookieMgr.LoadFromFile(lpszFile, bKeepExists);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
|
||||
{
|
||||
return g_CookieMgr.SaveToFile(lpszFile, bKeepExists);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
return g_CookieMgr.ClearCookies(lpszDomain, lpszPath);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
return g_CookieMgr.RemoveExpiredCookies(lpszDomain, lpszPath);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite, BOOL bOnlyUpdateValueIfExists)
|
||||
{
|
||||
return g_CookieMgr.SetCookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite, bOnlyUpdateValueIfExists);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
|
||||
{
|
||||
return g_CookieMgr.DeleteCookie(lpszDomain, lpszPath, lpszName);
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_HttpCookie_MGR_SetEnableThirdPartyCookie(BOOL bEnableThirdPartyCookie)
|
||||
{
|
||||
g_CookieMgr.SetEnableThirdPartyCookie(bEnableThirdPartyCookie);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_MGR_IsEnableThirdPartyCookie()
|
||||
{
|
||||
return g_CookieMgr.IsEnableThirdPartyCookie();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_HLP_ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires)
|
||||
{
|
||||
return CCookie::ParseExpires(lpszExpires, tmExpires);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_HLP_MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires)
|
||||
{
|
||||
return CCookie::MakeExpiresStr(lpszBuff, iBuffLen, tmExpires);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL HP_HttpCookie_HLP_ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, int enSameSite)
|
||||
{
|
||||
return CCookie::ToString(lpszBuff, iBuffLen, lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, (CCookie::EnSameSite)enSameSite);
|
||||
}
|
||||
|
||||
HPSOCKET_API __time64_t HP_HttpCookie_HLP_CurrentUTCTime()
|
||||
{
|
||||
return CCookie::CurrentUTCTime();
|
||||
}
|
||||
|
||||
HPSOCKET_API __time64_t HP_HttpCookie_HLP_MaxAgeToExpires(int iMaxAge)
|
||||
{
|
||||
return CCookie::MaxAgeToExpires(iMaxAge);
|
||||
}
|
||||
|
||||
HPSOCKET_API int HP_HttpCookie_HLP_ExpiresToMaxAge(__time64_t tmExpires)
|
||||
{
|
||||
return CCookie::ExpiresToMaxAge(tmExpires);
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/************************************************************ HTTP Global Function Exports ***********************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/**************************************************************** Thread Pool Exports ****************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API IHPThreadPool* HP_Create_ThreadPool(IHPThreadPoolListener* pListener)
|
||||
{
|
||||
return new CHPThreadPool(pListener);
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_ThreadPool(IHPThreadPool* pThreadPool)
|
||||
{
|
||||
delete pThreadPool;
|
||||
}
|
||||
|
||||
HPSOCKET_API LPTSocketTask HP_Create_SocketTaskObj(Fn_SocketTaskProc fnTaskProc, PVOID pSender, CONNID dwConnID, LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
return ::CreateSocketTaskObj(fnTaskProc, pSender, dwConnID, pBuffer, iBuffLen, enBuffType, wParam, lParam);
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_SocketTaskObj(LPTSocketTask pTask)
|
||||
{
|
||||
::DestroySocketTaskObj(pTask);
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/********************************************************* Compressor / Decompressor Exports *********************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
HPSOCKET_API void HP_Destroy_Compressor(IHPCompressor* pCompressor)
|
||||
{
|
||||
::DestroyCompressor(pCompressor);
|
||||
}
|
||||
|
||||
HPSOCKET_API void HP_Destroy_Decompressor(IHPDecompressor* pDecompressor)
|
||||
{
|
||||
::DestroyDecompressor(pDecompressor);
|
||||
}
|
||||
|
||||
#ifdef _ZLIB_SUPPORT
|
||||
|
||||
HPSOCKET_API IHPCompressor* HP_Create_ZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateZLibCompressor(fnCallback, iWindowBits, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
|
||||
}
|
||||
|
||||
HPSOCKET_API IHPCompressor* HP_Create_GZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel, int iMethod, int iMemLevel, int iStrategy, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateGZipCompressor(fnCallback, iLevel, iMethod, iMemLevel, iStrategy, dwBuffSize);
|
||||
}
|
||||
|
||||
HPSOCKET_API IHPDecompressor* HP_Create_ZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateZLibDecompressor(fnCallback, iWindowBits, dwBuffSize);
|
||||
}
|
||||
|
||||
HPSOCKET_API IHPDecompressor* HP_Create_GZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateGZipDecompressor(fnCallback, dwBuffSize);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _BROTLI_SUPPORT
|
||||
|
||||
HPSOCKET_API IHPCompressor* HP_Create_BrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality, int iWindow, int iMode, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateBrotliCompressor(fnCallback, iQuality, iWindow, iMode, dwBuffSize);
|
||||
}
|
||||
|
||||
HPSOCKET_API IHPDecompressor* HP_Create_BrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize)
|
||||
{
|
||||
return ::CreateBrotliDecompressor(fnCallback, dwBuffSize);
|
||||
}
|
||||
|
||||
#endif
|
||||
380
HPSocket4C-SSL.cpp
Normal file
380
HPSocket4C-SSL.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "hpsocket/HPSocket4C-SSL.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SocketObject4C.h"
|
||||
#include "TcpServer.h"
|
||||
#include "TcpClient.h"
|
||||
#include "TcpAgent.h"
|
||||
#include "TcpPullServer.h"
|
||||
#include "TcpPullClient.h"
|
||||
#include "TcpPullAgent.h"
|
||||
#include "TcpPackServer.h"
|
||||
#include "TcpPackClient.h"
|
||||
#include "TcpPackAgent.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
#include "HttpServer.h"
|
||||
#include "HttpAgent.h"
|
||||
#include "HttpClient.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/******************************************************************** SSL Exports ********************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
typedef C_HP_ObjectT<CSSLServer, ITcpServerListener> C_HP_SSLServer;
|
||||
typedef C_HP_ObjectT<CSSLPullServer, ITcpServerListener, sizeof(IPullSocket)> C_HP_SSLPullServer;
|
||||
typedef C_HP_ObjectT<CSSLPackServer, ITcpServerListener, sizeof(IPackSocket)> C_HP_SSLPackServer;
|
||||
|
||||
typedef C_HP_ObjectT<CSSLAgent, ITcpAgentListener> C_HP_SSLAgent;
|
||||
typedef C_HP_ObjectT<CSSLPullAgent, ITcpAgentListener, sizeof(IPullSocket)> C_HP_SSLPullAgent;
|
||||
typedef C_HP_ObjectT<CSSLPackAgent, ITcpAgentListener, sizeof(IPackSocket)> C_HP_SSLPackAgent;
|
||||
|
||||
typedef C_HP_ObjectT<CSSLClient, ITcpClientListener> C_HP_SSLClient;
|
||||
typedef C_HP_ObjectT<CSSLPullClient, ITcpClientListener, sizeof(IPullClient)> C_HP_SSLPullClient;
|
||||
typedef C_HP_ObjectT<CSSLPackClient, ITcpClientListener, sizeof(IPackClient)> C_HP_SSLPackClient;
|
||||
|
||||
/********************************************************/
|
||||
/************** HPSocket4C-SSL 对象创建函数 **************/
|
||||
|
||||
HPSOCKET_API HP_SSLServer __HP_CALL Create_HP_SSLServer(HP_TcpServerListener pListener)
|
||||
{
|
||||
return (HP_SSLServer)(new C_HP_SSLServer((ITcpServerListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLAgent __HP_CALL Create_HP_SSLAgent(HP_TcpAgentListener pListener)
|
||||
{
|
||||
return (HP_SSLAgent)(new C_HP_SSLAgent((ITcpAgentListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLClient __HP_CALL Create_HP_SSLClient(HP_TcpClientListener pListener)
|
||||
{
|
||||
return (HP_SSLClient)(new C_HP_SSLClient((ITcpClientListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPullServer __HP_CALL Create_HP_SSLPullServer(HP_TcpPullServerListener pListener)
|
||||
{
|
||||
return (HP_SSLPullServer)(new C_HP_SSLPullServer((ITcpServerListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPullAgent __HP_CALL Create_HP_SSLPullAgent(HP_TcpPullAgentListener pListener)
|
||||
{
|
||||
return (HP_SSLPullAgent)(new C_HP_SSLPullAgent((ITcpAgentListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPullClient __HP_CALL Create_HP_SSLPullClient(HP_TcpPullClientListener pListener)
|
||||
{
|
||||
return (HP_SSLPullClient)(new C_HP_SSLPullClient((ITcpClientListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPackServer __HP_CALL Create_HP_SSLPackServer(HP_TcpServerListener pListener)
|
||||
{
|
||||
return (HP_SSLPackServer)(new C_HP_SSLPackServer((ITcpServerListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPackAgent __HP_CALL Create_HP_SSLPackAgent(HP_TcpAgentListener pListener)
|
||||
{
|
||||
return (HP_SSLPackAgent)(new C_HP_SSLPackAgent((ITcpAgentListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_SSLPackClient __HP_CALL Create_HP_SSLPackClient(HP_TcpClientListener pListener)
|
||||
{
|
||||
return (HP_SSLPackClient)(new C_HP_SSLPackClient((ITcpClientListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLServer(HP_SSLServer pServer)
|
||||
{
|
||||
delete (C_HP_SSLServer*)pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLAgent(HP_SSLAgent pAgent)
|
||||
{
|
||||
delete (C_HP_SSLAgent*)pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLClient(HP_SSLClient pClient)
|
||||
{
|
||||
delete (C_HP_SSLClient*)pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullServer(HP_SSLPullServer pServer)
|
||||
{
|
||||
delete (C_HP_SSLPullServer*)pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullAgent(HP_SSLPullAgent pAgent)
|
||||
{
|
||||
delete (C_HP_SSLPullAgent*)pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPullClient(HP_SSLPullClient pClient)
|
||||
{
|
||||
delete (C_HP_SSLPullClient*)pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackServer(HP_SSLPackServer pServer)
|
||||
{
|
||||
delete (C_HP_SSLPackServer*)pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackAgent(HP_SSLPackAgent pAgent)
|
||||
{
|
||||
delete (C_HP_SSLPackAgent*)pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_SSLPackClient(HP_SSLPackClient pClient)
|
||||
{
|
||||
delete (C_HP_SSLPackClient*)pClient;
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/*************************************************************** Global Function Exports *************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
/***************************************************************************************/
|
||||
/************************************ SSL 初始化方法 ************************************/
|
||||
|
||||
HPSOCKET_API int __HP_CALL HP_SSL_DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext)
|
||||
{
|
||||
return CSSLContext::DefaultServerNameCallback(lpszServerName, pContext);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSL_RemoveThreadLocalState(THR_ID dwThreadID)
|
||||
{
|
||||
CSSLContext::RemoveThreadLocalState(dwThreadID);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContext(HP_SSLServer pServer, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath, HP_Fn_SNI_ServerNameCallback fnServerNameCallback)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath, fnServerNameCallback);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_SetupSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert, HP_Fn_SNI_ServerNameCallback fnServerNameCallback)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert, fnServerNameCallback);
|
||||
}
|
||||
|
||||
HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContext(HP_SSLServer pServer, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->AddSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
|
||||
}
|
||||
|
||||
HPSOCKET_API int __HP_CALL HP_SSLServer_AddSSLContextByMemory(HP_SSLServer pServer, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->AddSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_BindSSLServerName(HP_SSLServer pServer, LPCTSTR lpszServerName, int iContextIndex)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->BindSSLServerName(lpszServerName, iContextIndex);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLServer_CleanupSSLContext(HP_SSLServer pServer)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpServer>(pServer)->CleanupSSLContext();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContext(HP_SSLAgent pAgent, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_SetupSSLContextByMemory(HP_SSLAgent pAgent, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLAgent_CleanupSSLContext(HP_SSLAgent pAgent)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpAgent>(pAgent)->CleanupSSLContext();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContext(HP_SSLClient pClient, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->SetupSSLContext(iVerifyMode, lpszPemCertFile, lpszPemKeyFile, lpszKeyPassword, lpszCAPemCertFileOrPath);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLClient_SetupSSLContextByMemory(HP_SSLClient pClient, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->SetupSSLContextByMemory(iVerifyMode, lpszPemCert, lpszPemKey, lpszKeyPassword, lpszCAPemCert);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLClient_CleanupSSLContext(HP_SSLClient pClient)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpClient>(pClient)->CleanupSSLContext();
|
||||
}
|
||||
|
||||
/***************************************************************************************/
|
||||
/************************************* SSL 操作方法 ************************************/
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_StartSSLHandShake(HP_SSLServer pServer, HP_CONNID dwConnID)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->StartSSLHandShake(dwConnID);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLAutoHandShake(HP_SSLServer pServer, BOOL bAutoHandShake)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSSLAutoHandShake(bAutoHandShake);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_IsSSLAutoHandShake(HP_SSLServer pServer)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->IsSSLAutoHandShake();
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLServer_SetSSLCipherList(HP_SSLServer pServer, LPCTSTR lpszCipherList)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpServer>(pServer)->SetSSLCipherList(lpszCipherList);
|
||||
}
|
||||
|
||||
HPSOCKET_API LPCTSTR __HP_CALL HP_SSLServer_GetSSLCipherList(HP_SSLServer pServer)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSSLCipherList();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLServer_GetSSLSessionInfo(HP_SSLServer pServer, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpServer>(pServer)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_StartSSLHandShake(HP_SSLAgent pAgent, HP_CONNID dwConnID)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->StartSSLHandShake(dwConnID);
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLAutoHandShake(HP_SSLAgent pAgent, BOOL bAutoHandShake)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSSLAutoHandShake(bAutoHandShake);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_IsSSLAutoHandShake(HP_SSLAgent pAgent)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->IsSSLAutoHandShake();
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLAgent_SetSSLCipherList(HP_SSLAgent pAgent, LPCTSTR lpszCipherList)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpAgent>(pAgent)->SetSSLCipherList(lpszCipherList);
|
||||
}
|
||||
|
||||
HPSOCKET_API LPCTSTR __HP_CALL HP_SSLAgent_GetSSLCipherList(HP_SSLAgent pAgent)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSSLCipherList();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLAgent_GetSSLSessionInfo(HP_SSLAgent pAgent, HP_CONNID dwConnID, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpAgent>(pAgent)->GetSSLSessionInfo(dwConnID, enInfo, lppInfo);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLClient_StartSSLHandShake(HP_SSLClient pClient)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->StartSSLHandShake();
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLAutoHandShake(HP_SSLClient pClient, BOOL bAutoHandShake)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSSLAutoHandShake(bAutoHandShake);
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLClient_IsSSLAutoHandShake(HP_SSLClient pClient)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->IsSSLAutoHandShake();
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL HP_SSLClient_SetSSLCipherList(HP_SSLClient pClient, LPCTSTR lpszCipherList)
|
||||
{
|
||||
C_HP_Object::ToSecond<ITcpClient>(pClient)->SetSSLCipherList(lpszCipherList);
|
||||
}
|
||||
|
||||
HPSOCKET_API LPCTSTR __HP_CALL HP_SSLClient_GetSSLCipherList(HP_SSLClient pClient)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSSLCipherList();
|
||||
}
|
||||
|
||||
HPSOCKET_API BOOL __HP_CALL HP_SSLClient_GetSSLSessionInfo(HP_SSLClient pClient, En_HP_SSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
return C_HP_Object::ToSecond<ITcpClient>(pClient)->GetSSLSessionInfo(enInfo, lppInfo);
|
||||
}
|
||||
|
||||
/*****************************************************************************************************************************************************/
|
||||
/******************************************************************** HTTPS Exports ******************************************************************/
|
||||
/*****************************************************************************************************************************************************/
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
typedef C_HP_ObjectT<CHttpsServer, IHttpServerListener, sizeof(IComplexHttpResponder)> C_HP_HttpsServer;
|
||||
typedef C_HP_ObjectT<CHttpsAgent, IHttpAgentListener, sizeof(IComplexHttpRequester)> C_HP_HttpsAgent;
|
||||
typedef C_HP_ObjectT<CHttpsClient, IHttpClientListener, sizeof(IHttpRequester)> C_HP_HttpsClient;
|
||||
typedef C_HP_ObjectT<CHttpsSyncClient, IHttpClientListener, sizeof(IHttpSyncRequester)> C_HP_HttpsSyncClient;
|
||||
|
||||
/****************************************************/
|
||||
/**************** HTTPS 对象创建函数 *****************/
|
||||
|
||||
HPSOCKET_API HP_HttpsServer __HP_CALL Create_HP_HttpsServer(HP_HttpServerListener pListener)
|
||||
{
|
||||
return (HP_HttpsServer)(new C_HP_HttpsServer((IHttpServerListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_HttpsAgent __HP_CALL Create_HP_HttpsAgent(HP_HttpAgentListener pListener)
|
||||
{
|
||||
return (HP_HttpsAgent)(new C_HP_HttpsAgent((IHttpAgentListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_HttpsClient __HP_CALL Create_HP_HttpsClient(HP_HttpClientListener pListener)
|
||||
{
|
||||
return (HP_HttpsClient)(new C_HP_HttpsClient((IHttpClientListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API HP_HttpsSyncClient __HP_CALL Create_HP_HttpsSyncClient(HP_HttpClientListener pListener)
|
||||
{
|
||||
return (HP_HttpsSyncClient)(new C_HP_HttpsSyncClient((IHttpClientListener*)pListener));
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_HttpsServer(HP_HttpsServer pServer)
|
||||
{
|
||||
delete (C_HP_HttpsServer*)pServer;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_HttpsAgent(HP_HttpsAgent pAgent)
|
||||
{
|
||||
delete (C_HP_HttpsAgent*)pAgent;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_HttpsClient(HP_HttpsClient pClient)
|
||||
{
|
||||
delete (C_HP_HttpsClient*)pClient;
|
||||
}
|
||||
|
||||
HPSOCKET_API void __HP_CALL Destroy_HP_HttpsSyncClient(HP_HttpsSyncClient pClient)
|
||||
{
|
||||
delete (C_HP_HttpsSyncClient*)pClient;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
3747
HPSocket4C.cpp
Normal file
3747
HPSocket4C.cpp
Normal file
File diff suppressed because it is too large
Load Diff
496
HPThreadPool.cpp
Normal file
496
HPThreadPool.cpp
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HPThreadPool.h"
|
||||
#include "common/FuncHelper.h"
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
LPTSocketTask CreateSocketTaskObj( Fn_SocketTaskProc fnTaskProc,
|
||||
PVOID pSender, CONNID dwConnID,
|
||||
LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
ASSERT(fnTaskProc != nullptr);
|
||||
ASSERT(iBuffLen >= 0);
|
||||
|
||||
LPTSocketTask pTask = new TSocketTask;
|
||||
|
||||
pTask->fn = fnTaskProc;
|
||||
pTask->sender = pSender;
|
||||
pTask->connID = dwConnID;
|
||||
pTask->bufLen = iBuffLen;
|
||||
pTask->bufType = enBuffType;
|
||||
pTask->wparam = wParam;
|
||||
pTask->lparam = lParam;
|
||||
|
||||
if(enBuffType != TBT_COPY || !pBuffer)
|
||||
pTask->buf = pBuffer;
|
||||
else
|
||||
{
|
||||
pTask->buf = MALLOC(BYTE, iBuffLen);
|
||||
::CopyMemory((LPBYTE)pTask->buf, pBuffer, iBuffLen);
|
||||
}
|
||||
|
||||
return pTask;
|
||||
}
|
||||
|
||||
void DestroySocketTaskObj(LPTSocketTask pTask)
|
||||
{
|
||||
if(pTask)
|
||||
{
|
||||
if(pTask->bufType != TBT_REFER && pTask->buf)
|
||||
FREE(pTask->buf);
|
||||
|
||||
delete pTask;
|
||||
}
|
||||
}
|
||||
|
||||
volatile UINT CHPThreadPool::sm_uiNum = MAXUINT;
|
||||
LPCTSTR CHPThreadPool::POOLED_THREAD_PREFIX = _T("hp-pool-");
|
||||
|
||||
BOOL CHPThreadPool::Start(DWORD dwThreadCount, DWORD dwMaxQueueSize, EnRejectedPolicy enRejectedPolicy, DWORD dwStackSize)
|
||||
{
|
||||
if(!CheckStarting())
|
||||
return FALSE;
|
||||
|
||||
m_dwStackSize = dwStackSize;
|
||||
m_dwMaxQueueSize = dwMaxQueueSize;
|
||||
m_enRejectedPolicy = enRejectedPolicy;
|
||||
|
||||
FireStartup();
|
||||
|
||||
if(!InternalAdjustThreadCount(dwThreadCount))
|
||||
{
|
||||
EXECUTE_RESTORE_ERROR(Stop());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_enState = SS_STARTED;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::Stop(DWORD dwMaxWait)
|
||||
{
|
||||
if(!CheckStoping())
|
||||
return FALSE;
|
||||
|
||||
::WaitFor(15);
|
||||
|
||||
Shutdown(dwMaxWait);
|
||||
|
||||
FireShutdown();
|
||||
|
||||
Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::Shutdown(DWORD dwMaxWait)
|
||||
{
|
||||
BOOL isOK = TRUE;
|
||||
BOOL bLimited = (m_dwMaxQueueSize != 0);
|
||||
BOOL bInfinite = (dwMaxWait == (DWORD)INFINITE || dwMaxWait == 0);
|
||||
auto prdShutdown = [this]() {return m_stThreads.empty();};
|
||||
|
||||
if(m_enRejectedPolicy == TRP_WAIT_FOR && bLimited)
|
||||
m_evQueue.SyncNotifyAll();
|
||||
|
||||
VERIFY(DoAdjustThreadCount(0));
|
||||
|
||||
if(bInfinite)
|
||||
m_evShutdown.Wait(prdShutdown);
|
||||
else
|
||||
m_evShutdown.WaitFor(dwMaxWait, prdShutdown);
|
||||
|
||||
ASSERT(m_lsTasks.Size() == 0);
|
||||
ASSERT(m_stThreads.size() == 0);
|
||||
|
||||
if(!m_lsTasks.IsEmpty())
|
||||
{
|
||||
TTask* pTask = nullptr;
|
||||
|
||||
while(m_lsTasks.PopFront(&pTask))
|
||||
{
|
||||
if(pTask->freeArg)
|
||||
::DestroySocketTaskObj((LPTSocketTask)pTask->arg);
|
||||
|
||||
TTask::Destruct(pTask);
|
||||
}
|
||||
|
||||
::SetLastError(ERROR_CANCELLED);
|
||||
isOK = FALSE;
|
||||
}
|
||||
|
||||
if(!m_stThreads.empty())
|
||||
{
|
||||
CCriSecLock lock(m_csThread);
|
||||
|
||||
if(!m_stThreads.empty())
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
for(auto it = m_stThreads.begin(), end = m_stThreads.end(); it != end; ++it)
|
||||
pthread_cancel(*it);
|
||||
#endif
|
||||
m_stThreads.clear();
|
||||
|
||||
::SetLastError(ERROR_CANCELLED);
|
||||
isOK = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::Submit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait)
|
||||
{
|
||||
return DoSubmit(fnTaskProc, pvArg, FALSE, dwMaxWait);
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::Submit(LPTSocketTask pTask, DWORD dwMaxWait)
|
||||
{
|
||||
return DoSubmit((Fn_TaskProc)pTask->fn, (PVOID)pTask, TRUE, dwMaxWait);
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::DoSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg, DWORD dwMaxWait)
|
||||
{
|
||||
EnSubmitResult sr = DirectSubmit(fnTaskProc, pvArg, bFreeArg);
|
||||
|
||||
if(sr != SUBMIT_FULL)
|
||||
return (sr == SUBMIT_OK);
|
||||
|
||||
if(m_enRejectedPolicy == TRP_CALL_FAIL)
|
||||
{
|
||||
::SetLastError(ERROR_DESTINATION_ELEMENT_FULL);
|
||||
return FALSE;
|
||||
}
|
||||
else if(m_enRejectedPolicy == TRP_WAIT_FOR)
|
||||
{
|
||||
return CycleWaitSubmit(fnTaskProc, pvArg, dwMaxWait, bFreeArg);
|
||||
}
|
||||
else if(m_enRejectedPolicy == TRP_CALLER_RUN)
|
||||
{
|
||||
DoRunTaskProc(fnTaskProc, pvArg, bFreeArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(FALSE);
|
||||
|
||||
::SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CHPThreadPool::DoRunTaskProc(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
|
||||
{
|
||||
::InterlockedIncrement(&m_dwTaskCount);
|
||||
fnTaskProc(pvArg);
|
||||
::InterlockedDecrement(&m_dwTaskCount);
|
||||
|
||||
if(bFreeArg)
|
||||
::DestroySocketTaskObj((LPTSocketTask)pvArg);
|
||||
}
|
||||
|
||||
CHPThreadPool::EnSubmitResult CHPThreadPool::DirectSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
|
||||
{
|
||||
if(!CheckStarted())
|
||||
return SUBMIT_ERROR;
|
||||
|
||||
BOOL bLimited = (m_dwMaxQueueSize != 0);
|
||||
|
||||
if(bLimited && m_lsTasks.Size() >= m_dwMaxQueueSize)
|
||||
return SUBMIT_FULL;
|
||||
else
|
||||
{
|
||||
TTask* pTask = TTask::Construct(fnTaskProc, pvArg, bFreeArg);
|
||||
|
||||
m_lsTasks.PushBack(pTask);
|
||||
m_evTask.SyncNotifyOne();
|
||||
}
|
||||
|
||||
return SUBMIT_OK;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CycleWaitSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait, BOOL bFreeArg)
|
||||
{
|
||||
ASSERT(m_dwMaxQueueSize != 0);
|
||||
|
||||
DWORD dwTime = ::TimeGetTime();
|
||||
BOOL bInfinite = (dwMaxWait == (DWORD)INFINITE || dwMaxWait == 0);
|
||||
auto prdQueue = [this]() {return (m_lsTasks.Size() < m_dwMaxQueueSize);};
|
||||
|
||||
while(CheckStarted())
|
||||
{
|
||||
EnSubmitResult sr = DirectSubmit(fnTaskProc, pvArg, bFreeArg);
|
||||
|
||||
if(sr == SUBMIT_OK)
|
||||
return TRUE;
|
||||
if(sr == SUBMIT_ERROR)
|
||||
return FALSE;
|
||||
|
||||
if(bInfinite)
|
||||
m_evQueue.Wait(prdQueue);
|
||||
else
|
||||
{
|
||||
DWORD dwNow = ::GetTimeGap32(dwTime);
|
||||
|
||||
if(dwNow > dwMaxWait || !m_evQueue.WaitFor(chrono::milliseconds(dwMaxWait - dwNow), prdQueue))
|
||||
{
|
||||
::SetLastError(ERROR_TIMEOUT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::AdjustThreadCount(DWORD dwNewThreadCount)
|
||||
{
|
||||
if(!CheckStarted())
|
||||
return FALSE;
|
||||
|
||||
return InternalAdjustThreadCount(dwNewThreadCount);
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::InternalAdjustThreadCount(DWORD dwNewThreadCount)
|
||||
{
|
||||
int iNewThreadCount = (int)dwNewThreadCount;
|
||||
|
||||
if(iNewThreadCount == 0)
|
||||
iNewThreadCount = ::GetDefaultWorkerThreadCount();
|
||||
else if(iNewThreadCount < 0)
|
||||
iNewThreadCount = PROCESSOR_COUNT * (-iNewThreadCount);
|
||||
|
||||
return DoAdjustThreadCount((DWORD)iNewThreadCount);
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::DoAdjustThreadCount(DWORD dwNewThreadCount)
|
||||
{
|
||||
ASSERT((int)dwNewThreadCount >= 0);
|
||||
|
||||
BOOL bRemove = FALSE;
|
||||
DWORD dwThreadCount = 0;
|
||||
|
||||
if(dwNewThreadCount > m_dwThreadCount)
|
||||
{
|
||||
dwThreadCount = dwNewThreadCount - m_dwThreadCount;
|
||||
return CreateWorkerThreads(dwThreadCount);
|
||||
}
|
||||
else if(dwNewThreadCount < m_dwThreadCount)
|
||||
{
|
||||
bRemove = TRUE;
|
||||
dwThreadCount = m_dwThreadCount - dwNewThreadCount;
|
||||
|
||||
::InterlockedSub(&m_dwThreadCount, dwThreadCount);
|
||||
}
|
||||
|
||||
if(bRemove)
|
||||
{
|
||||
for(DWORD i = 0; i < dwThreadCount; i++)
|
||||
m_evTask.SyncNotifyOne();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CreateWorkerThreads(DWORD dwThreadCount)
|
||||
{
|
||||
unique_ptr<pthread_attr_t> pThreadAttr;
|
||||
|
||||
if(m_dwStackSize != 0)
|
||||
{
|
||||
pThreadAttr = make_unique<pthread_attr_t>();
|
||||
VERIFY_IS_NO_ERROR(pthread_attr_init(pThreadAttr.get()));
|
||||
|
||||
int rs = pthread_attr_setstacksize(pThreadAttr.get(), m_dwStackSize);
|
||||
|
||||
if(!IS_NO_ERROR(rs))
|
||||
{
|
||||
pthread_attr_destroy(pThreadAttr.get());
|
||||
::SetLastError(rs);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL isOK = TRUE;
|
||||
|
||||
for(DWORD i = 0; i < dwThreadCount; i++)
|
||||
{
|
||||
THR_ID dwThreadID;
|
||||
int rs = pthread_create(&dwThreadID, pThreadAttr.get(), ThreadProc, (PVOID)this);
|
||||
|
||||
if(!IS_NO_ERROR(rs))
|
||||
{
|
||||
::SetLastError(rs);
|
||||
isOK = FALSE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
::InterlockedIncrement(&m_dwThreadCount);
|
||||
|
||||
CCriSecLock lock(m_csThread);
|
||||
m_stThreads.emplace(dwThreadID);
|
||||
}
|
||||
|
||||
if(pThreadAttr != nullptr)
|
||||
pthread_attr_destroy(pThreadAttr.get());
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
PVOID CHPThreadPool::ThreadProc(LPVOID pv)
|
||||
{
|
||||
CHPThreadPool* pThis = (CHPThreadPool*)pv;
|
||||
|
||||
::SetSequenceThreadName(SELF_THREAD_ID, pThis->m_strPrefix, pThis->m_uiSeq);
|
||||
|
||||
pThis->FireWorkerThreadStart();
|
||||
|
||||
PVOID rs = (PVOID)(UINT_PTR)(pThis->WorkerProc());
|
||||
|
||||
pThis->FireWorkerThreadEnd();
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
int CHPThreadPool::WorkerProc()
|
||||
{
|
||||
BOOL bLimited = (m_dwMaxQueueSize != 0);
|
||||
TTask* pTask = nullptr;
|
||||
auto prdTask = [this]() {return (!m_lsTasks.IsEmpty()) || (m_dwThreadCount < m_stThreads.size());};
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
pTask = nullptr;
|
||||
|
||||
while(m_lsTasks.PopFront(&pTask))
|
||||
{
|
||||
if(m_enRejectedPolicy == TRP_WAIT_FOR && bLimited)
|
||||
m_evQueue.SyncNotifyOne();
|
||||
|
||||
DoRunTaskProc(pTask->fn, pTask->arg, pTask->freeArg);
|
||||
|
||||
TTask::Destruct(pTask);
|
||||
}
|
||||
|
||||
if(CheckWorkerThreadExit())
|
||||
break;
|
||||
|
||||
m_evTask.Wait(prdTask);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CheckWorkerThreadExit()
|
||||
{
|
||||
BOOL bExit = FALSE;
|
||||
BOOL bShutdown = FALSE;
|
||||
|
||||
if(m_dwThreadCount < m_stThreads.size())
|
||||
{
|
||||
CCriSecLock lock(m_csThread);
|
||||
|
||||
if(m_dwThreadCount < m_stThreads.size())
|
||||
{
|
||||
VERIFY(m_stThreads.erase(SELF_THREAD_ID) == 1);
|
||||
|
||||
bExit = TRUE;
|
||||
bShutdown = m_stThreads.empty();
|
||||
}
|
||||
}
|
||||
|
||||
if(bExit)
|
||||
{
|
||||
pthread_detach(SELF_THREAD_ID);
|
||||
|
||||
if(bShutdown)
|
||||
m_evShutdown.SyncNotifyOne();
|
||||
}
|
||||
|
||||
return bExit;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CheckStarting()
|
||||
{
|
||||
if(::InterlockedCompareExchange(&m_enState, SS_STARTING, SS_STOPPED) != SS_STOPPED)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CheckStarted()
|
||||
{
|
||||
if(m_enState != SS_STARTED)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CHPThreadPool::CheckStoping()
|
||||
{
|
||||
if( ::InterlockedCompareExchange(&m_enState, SS_STOPPING, SS_STARTED) != SS_STARTED &&
|
||||
::InterlockedCompareExchange(&m_enState, SS_STOPPING, SS_STARTING) != SS_STARTING)
|
||||
{
|
||||
while(m_enState != SS_STOPPED)
|
||||
::WaitFor(5);
|
||||
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CHPThreadPool::Reset(BOOL bSetWaitEvent)
|
||||
{
|
||||
m_uiSeq = MAXUINT;
|
||||
m_dwStackSize = 0;
|
||||
m_dwTaskCount = 0;
|
||||
m_dwThreadCount = 0;
|
||||
m_dwMaxQueueSize = 0;
|
||||
m_enRejectedPolicy = TRP_CALL_FAIL;
|
||||
m_enState = SS_STOPPED;
|
||||
|
||||
if(bSetWaitEvent)
|
||||
m_evWait.SyncNotifyAll();
|
||||
}
|
||||
|
||||
void CHPThreadPool::MakePrefix()
|
||||
{
|
||||
UINT uiNumber = ::InterlockedIncrement(&sm_uiNum);
|
||||
|
||||
m_strPrefix.Format(_T("%s%u-"), POOLED_THREAD_PREFIX, uiNumber);
|
||||
}
|
||||
168
HPThreadPool.h
Normal file
168
HPThreadPool.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/SocketInterface.h"
|
||||
#include "common/STLHelper.h"
|
||||
#include "common/Semaphore.h"
|
||||
#include "common/RingBuffer.h"
|
||||
#include "InternalDef.h"
|
||||
|
||||
LPTSocketTask CreateSocketTaskObj( Fn_SocketTaskProc fnTaskProc,
|
||||
PVOID pSender, CONNID dwConnID,
|
||||
LPCBYTE pBuffer, INT iBuffLen, EnTaskBufferType enBuffType = TBT_COPY,
|
||||
WPARAM wParam = 0, LPARAM lParam = 0);
|
||||
|
||||
void DestroySocketTaskObj(LPTSocketTask pTask);
|
||||
|
||||
class CHPThreadPool : public IHPThreadPool
|
||||
{
|
||||
private:
|
||||
enum EnSubmitResult{SUBMIT_OK, SUBMIT_FULL, SUBMIT_ERROR};
|
||||
|
||||
struct TTask
|
||||
{
|
||||
Fn_TaskProc fn;
|
||||
PVOID arg;
|
||||
BOOL freeArg;
|
||||
|
||||
public:
|
||||
static TTask* Construct(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
|
||||
{
|
||||
return new TTask(fnTaskProc, pvArg, bFreeArg);
|
||||
}
|
||||
|
||||
static void Destruct(TTask* pTask)
|
||||
{
|
||||
if(pTask)
|
||||
{
|
||||
delete pTask;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TTask(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg)
|
||||
: fn(fnTaskProc), arg(pvArg), freeArg(bFreeArg)
|
||||
{
|
||||
ASSERT(fn != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
using CTaskQueue = CCASQueue<TTask>;
|
||||
|
||||
public:
|
||||
virtual BOOL Start(DWORD dwThreadCount = 0, DWORD dwMaxQueueSize = 0, EnRejectedPolicy enRejectedPolicy = TRP_CALL_FAIL, DWORD dwStackSize = 0);
|
||||
virtual BOOL Stop(DWORD dwMaxWait = INFINITE);
|
||||
virtual BOOL Wait(DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
|
||||
virtual BOOL Submit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait = INFINITE);
|
||||
virtual BOOL Submit(LPTSocketTask pTask, DWORD dwMaxWait = INFINITE);
|
||||
virtual BOOL AdjustThreadCount(DWORD dwNewThreadCount);
|
||||
|
||||
public:
|
||||
virtual BOOL HasStarted() {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState() {return m_enState;}
|
||||
|
||||
virtual DWORD GetQueueSize() {return m_lsTasks.Size();}
|
||||
virtual DWORD GetTaskCount() {return m_dwTaskCount;}
|
||||
virtual DWORD GetThreadCount() {return m_dwThreadCount;}
|
||||
virtual DWORD GetMaxQueueSize() {return m_dwMaxQueueSize;}
|
||||
virtual EnRejectedPolicy GetRejectedPolicy() {return m_enRejectedPolicy;}
|
||||
|
||||
private:
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStarted();
|
||||
BOOL CheckStoping();
|
||||
|
||||
BOOL InternalAdjustThreadCount(DWORD dwNewThreadCount);
|
||||
BOOL DoAdjustThreadCount(DWORD dwNewThreadCount);
|
||||
|
||||
BOOL CreateWorkerThreads(DWORD dwThreadCount);
|
||||
|
||||
BOOL Shutdown(DWORD dwMaxWait);
|
||||
BOOL CheckWorkerThreadExit();
|
||||
|
||||
static PVOID ThreadProc(LPVOID pv);
|
||||
int WorkerProc();
|
||||
|
||||
EnSubmitResult DirectSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg);
|
||||
BOOL CycleWaitSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, DWORD dwMaxWait, BOOL bFreeArg);
|
||||
BOOL DoSubmit(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg, DWORD dwMaxWait);
|
||||
void DoRunTaskProc(Fn_TaskProc fnTaskProc, PVOID pvArg, BOOL bFreeArg);
|
||||
|
||||
void FireStartup()
|
||||
{if(m_pListener != nullptr) m_pListener->OnStartup(this);}
|
||||
void FireShutdown()
|
||||
{if(m_pListener != nullptr) m_pListener->OnShutdown(this);}
|
||||
void FireWorkerThreadStart()
|
||||
{if(m_pListener != nullptr) m_pListener->OnWorkerThreadStart(this, SELF_THREAD_ID);}
|
||||
void FireWorkerThreadEnd()
|
||||
{if(m_pListener != nullptr) m_pListener->OnWorkerThreadEnd(this, SELF_THREAD_ID);}
|
||||
|
||||
public:
|
||||
CHPThreadPool(IHPThreadPoolListener* pListener = nullptr)
|
||||
: m_pListener(pListener)
|
||||
{
|
||||
MakePrefix();
|
||||
Reset(FALSE);
|
||||
}
|
||||
|
||||
virtual ~CHPThreadPool()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
void Reset(BOOL bSetWaitEvent = TRUE);
|
||||
void MakePrefix();
|
||||
|
||||
private:
|
||||
static LPCTSTR POOLED_THREAD_PREFIX;
|
||||
static volatile UINT sm_uiNum;
|
||||
|
||||
volatile UINT m_uiSeq;
|
||||
CString m_strPrefix;
|
||||
|
||||
private:
|
||||
IHPThreadPoolListener* m_pListener;
|
||||
|
||||
DWORD m_dwStackSize;
|
||||
DWORD m_dwMaxQueueSize;
|
||||
EnRejectedPolicy m_enRejectedPolicy;
|
||||
|
||||
volatile DWORD m_dwTaskCount;
|
||||
volatile DWORD m_dwThreadCount;
|
||||
volatile EnServiceState m_enState;
|
||||
|
||||
CSEM m_evWait;
|
||||
CSEM m_evShutdown;
|
||||
CSEM m_evTask;
|
||||
CSEM m_evQueue;
|
||||
CCriSec m_csThread;
|
||||
|
||||
CTaskQueue m_lsTasks;
|
||||
unordered_set<THR_ID> m_stThreads;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CHPThreadPool)
|
||||
};
|
||||
479
HttpAgent.cpp
Normal file
479
HttpAgent.cpp
Normal file
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HttpAgent.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::CheckParams()
|
||||
{
|
||||
if(m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0)
|
||||
{
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpAgentT<T, default_port>::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime());
|
||||
m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool());
|
||||
m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold());
|
||||
|
||||
m_objPool.Prepare();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendRequest(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WSABUF szBuffer[2];
|
||||
CStringA strHeader;
|
||||
|
||||
LPCSTR lpszHost = nullptr;
|
||||
USHORT usPort = 0;
|
||||
BOOL bConnect = (stricmp(lpszMethod, HTTP_METHOD_CONNECT) == 0);
|
||||
|
||||
if(!bConnect)
|
||||
{
|
||||
GetRemoteHost(dwConnID, &lpszHost, &usPort);
|
||||
if(usPort == default_port) usPort = 0;
|
||||
}
|
||||
|
||||
CStringA strPath;
|
||||
::AdjustRequestPath(bConnect, lpszPath, strPath);
|
||||
|
||||
pHttpObj->SetRequestPath(lpszMethod, strPath);
|
||||
pHttpObj->ReloadCookies();
|
||||
|
||||
::MakeRequestLine(lpszMethod, strPath, m_enLocalVersion, strHeader);
|
||||
::MakeHeaderLines(lpHeaders, iHeaderCount, &pHttpObj->GetCookieMap(), iLength, TRUE, -1, lpszHost, usPort, strHeader);
|
||||
::MakeHttpPacket(strHeader, pBody, iLength, szBuffer);
|
||||
|
||||
return SendPackets(dwConnID, szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount)
|
||||
{
|
||||
CFile file;
|
||||
CFileMapping fmap;
|
||||
|
||||
HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
::SetLastError(hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return SendRequest(dwConnID, lpszMethod, lpszPath, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendChunkData(CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
|
||||
{
|
||||
char szLen[12];
|
||||
WSABUF bufs[5];
|
||||
|
||||
int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
|
||||
|
||||
return SendPackets(dwConnID, bufs, iCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
|
||||
{
|
||||
ASSERT(lpszMask);
|
||||
|
||||
WSABUF szBuffer[2];
|
||||
BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
|
||||
|
||||
unique_ptr<BYTE[]> szData = make_unique<BYTE[]>(iLength);
|
||||
memcpy(szData.get(), pData, iLength);
|
||||
|
||||
if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, lpszMask, szData.get(), iLength, ullBodyLen, szHeader, szBuffer))
|
||||
return FALSE;
|
||||
|
||||
return SendPackets(dwConnID, szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::FireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
return m_bHttpAutoStart ? __super::FireConnect(pSocketObj) : __super::DoFireConnect(pSocketObj);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = DoStartHttp(pSocketObj);
|
||||
EnHandleResult result = __super::DoFireConnect(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
{
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireHandShake(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
VERIFY(pHttpObj);
|
||||
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
return pHttpObj->Execute(pData, iLength);
|
||||
else
|
||||
return DoFireSuperReceive(pSocketObj, pData, iLength);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
pHttpObj->CheckBodyIdentityEof();
|
||||
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
{
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpAgentT<T, default_port>::DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_objPool.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpAgentT<T, default_port>::ReleaseGCSocketObj(BOOL bForce)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_objPool.ReleaseGCHttpObj(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::IsUpgrade(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->IsUpgrade();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::IsKeepAlive(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->IsKeepAlive();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetVersion(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetVersion();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> ULONGLONG CHttpAgentT<T, default_port>::GetContentLength(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetContentLength();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetContentType(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetContentType();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetContentEncoding(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetContentEncoding();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpAgentT<T, default_port>::GetTransferEncoding(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetTransferEncoding();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpUpgradeType CHttpAgentT<T, default_port>::GetUpgradeType(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return HUT_NONE;
|
||||
|
||||
return pHttpObj->GetUpgradeType();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetParseErrorCode(lpszErrorDesc);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetHeader(lpszName, lpszValue);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllHeaders(lpHeaders, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllHeaderNames(lpszName, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetCookie(lpszName, lpszValue);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllCookies(lpCookies, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpAgentT<T, default_port>::GetStatusCode(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetStatusCode();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> inline typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::FindHttpObj(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = nullptr;
|
||||
GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj);
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> inline typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::FindHttpObj(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj);
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::StartHttp(CONNID dwConnID)
|
||||
{
|
||||
if(IsHttpAutoStart())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartHttp(pSocketObj);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpAgentT<T, default_port>::StartHttp(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock locallock(pSocketObj->csSend);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoStartHttp(pSocketObj);
|
||||
|
||||
if(!IsSecure())
|
||||
FireHandShake(pSocketObj);
|
||||
else
|
||||
{
|
||||
#ifdef _SSL_SUPPORT
|
||||
if(IsSSLAutoHandShake())
|
||||
StartSSLHandShake(pSocketObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> typename CHttpAgentT<T, default_port>::THttpObj* CHttpAgentT<T, default_port>::DoStartHttp(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj);
|
||||
VERIFY(SetConnectionReserved(pSocketObj, pHttpObj));
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template class CHttpAgentT<CTcpAgent, HTTP_DEFAULT_PORT>;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLAgent.h"
|
||||
|
||||
template class CHttpAgentT<CSSLAgent, HTTPS_DEFAULT_PORT>;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
216
HttpAgent.h
Normal file
216
HttpAgent.h
Normal file
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpAgent.h"
|
||||
#include "HttpHelper.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class T, USHORT default_port> class CHttpAgentT : public IComplexHttpRequester, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::SetLastError;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
using __super::SendPackets;
|
||||
using __super::HasStarted;
|
||||
using __super::GetRemoteHost;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
|
||||
using __super::IsSecure;
|
||||
using __super::FireHandShake;
|
||||
using __super::FindSocketObj;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
using __super::StartSSLHandShake;
|
||||
using __super::IsSSLAutoHandShake;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
using CHttpObjPool = CHttpObjPoolT<FALSE, CHttpAgentT, TAgentSocketObj>;
|
||||
using THttpObj = THttpObjT<CHttpAgentT, TAgentSocketObj>;
|
||||
|
||||
friend typename CHttpAgentT::THttpObj;
|
||||
|
||||
public:
|
||||
virtual BOOL SendRequest(CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
|
||||
virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
|
||||
virtual BOOL SendChunkData(CONNID dwConnID, const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
|
||||
|
||||
virtual BOOL SendPost(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_POST, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendPut(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_PUT, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendPatch(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_PATCH, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendGet(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_GET, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendDelete(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_DELETE, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendHead(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_HEAD, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendTrace(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_TRACE, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendOptions(CONNID dwConnID, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_OPTIONS, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendConnect(CONNID dwConnID, LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(dwConnID, HTTP_METHOD_CONNECT, lpszHost, lpHeaders, iHeaderCount);}
|
||||
|
||||
virtual BOOL SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
|
||||
|
||||
virtual BOOL StartHttp(CONNID dwConnID);
|
||||
|
||||
public:
|
||||
virtual void SetUseCookie(BOOL bUseCookie) {ENSURE_HAS_STOPPED(); m_pCookieMgr = bUseCookie ? &g_CookieMgr : nullptr;}
|
||||
virtual void SetHttpAutoStart(BOOL bAutoStart) {ENSURE_HAS_STOPPED(); m_bHttpAutoStart = bAutoStart;}
|
||||
virtual void SetLocalVersion(EnHttpVersion enLocalVersion) {ENSURE_HAS_STOPPED(); m_enLocalVersion = enLocalVersion;}
|
||||
|
||||
virtual BOOL IsUseCookie() {return m_pCookieMgr != nullptr;}
|
||||
virtual BOOL IsHttpAutoStart() {return m_bHttpAutoStart;}
|
||||
virtual EnHttpVersion GetLocalVersion() {return m_enLocalVersion;}
|
||||
|
||||
virtual BOOL IsUpgrade(CONNID dwConnID);
|
||||
virtual BOOL IsKeepAlive(CONNID dwConnID);
|
||||
virtual USHORT GetVersion(CONNID dwConnID);
|
||||
virtual ULONGLONG GetContentLength(CONNID dwConnID);
|
||||
virtual LPCSTR GetContentType(CONNID dwConnID);
|
||||
virtual LPCSTR GetContentEncoding(CONNID dwConnID);
|
||||
virtual LPCSTR GetTransferEncoding(CONNID dwConnID);
|
||||
virtual EnHttpUpgradeType GetUpgradeType(CONNID dwConnID);
|
||||
virtual USHORT GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc = nullptr);
|
||||
|
||||
virtual BOOL GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
|
||||
virtual BOOL GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount);
|
||||
virtual BOOL GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount);
|
||||
virtual BOOL GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount);
|
||||
|
||||
virtual BOOL GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
|
||||
virtual BOOL GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount);
|
||||
|
||||
virtual USHORT GetStatusCode(CONNID dwConnID);
|
||||
|
||||
virtual BOOL GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain);
|
||||
|
||||
private:
|
||||
BOOL StartHttp(TAgentSocketObj* pSocketObj);
|
||||
THttpObj* DoStartHttp(TAgentSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
virtual EnHandleResult DoFireShutdown();
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
EnHandleResult DoFireSuperReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return __super::DoFireReceive(pSocketObj, pData, iLength);}
|
||||
|
||||
EnHttpParseResult FireMessageBegin(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnMessageBegin((IHttpAgent*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireRequestLine(TAgentSocketObj* pSocketObj, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_pListener->OnRequestLine((IHttpAgent*)this, pSocketObj->connID, lpszMethod, lpszUrl);}
|
||||
EnHttpParseResult FireStatusLine(TAgentSocketObj* pSocketObj, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_pListener->OnStatusLine((IHttpAgent*)this, pSocketObj->connID, usStatusCode, lpszDesc);}
|
||||
EnHttpParseResult FireHeader(TAgentSocketObj* pSocketObj, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_pListener->OnHeader((IHttpAgent*)this, pSocketObj->connID, lpszName, lpszValue);}
|
||||
EnHttpParseResult FireHeadersComplete(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnHeadersComplete((IHttpAgent*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireBody(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnBody((IHttpAgent*)this, pSocketObj->connID, pData, iLength);}
|
||||
EnHttpParseResult FireChunkHeader(TAgentSocketObj* pSocketObj, int iLength)
|
||||
{return m_pListener->OnChunkHeader((IHttpAgent*)this, pSocketObj->connID, iLength);}
|
||||
EnHttpParseResult FireChunkComplete(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnChunkComplete((IHttpAgent*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireMessageComplete(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnMessageComplete((IHttpAgent*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireUpgrade(TAgentSocketObj* pSocketObj, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_pListener->OnUpgrade((IHttpAgent*)this, pSocketObj->connID, enUpgradeType);}
|
||||
EnHttpParseResult FireParseError(TAgentSocketObj* pSocketObj, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_pListener->OnParseError((IHttpAgent*)this, pSocketObj->connID, iErrorCode, lpszErrorDesc);}
|
||||
|
||||
EnHandleResult FireWSMessageHeader(TAgentSocketObj* pSocketObj, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_pListener->OnWSMessageHeader((IHttpAgent*)this, pSocketObj->connID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
EnHandleResult FireWSMessageBody(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnWSMessageBody((IHttpAgent*)this, pSocketObj->connID, pData, iLength);}
|
||||
EnHandleResult FireWSMessageComplete(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnWSMessageComplete((IHttpAgent*)this, pSocketObj->connID);}
|
||||
|
||||
inline THttpObj* FindHttpObj(CONNID dwConnID);
|
||||
inline THttpObj* FindHttpObj(TAgentSocketObj* pSocketObj);
|
||||
|
||||
CCookieMgr* GetCookieMgr() {return m_pCookieMgr;}
|
||||
LPCSTR GetRemoteDomain(TAgentSocketObj* pSocketObj) {LPCSTR lpszDomain; pSocketObj->GetRemoteHost(&lpszDomain); return lpszDomain;}
|
||||
|
||||
public:
|
||||
CHttpAgentT(IHttpAgentListener* pListener)
|
||||
: T (pListener)
|
||||
, m_pListener (pListener)
|
||||
, m_pCookieMgr (&g_CookieMgr)
|
||||
, m_bHttpAutoStart (TRUE)
|
||||
, m_enLocalVersion (DEFAULT_HTTP_VERSION)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CHttpAgentT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
IHttpAgentListener* m_pListener;
|
||||
CCookieMgr* m_pCookieMgr;
|
||||
EnHttpVersion m_enLocalVersion;
|
||||
|
||||
BOOL m_bHttpAutoStart;
|
||||
|
||||
CHttpObjPool m_objPool;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
typedef CHttpAgentT<CTcpAgent, HTTP_DEFAULT_PORT> CHttpAgent;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLAgent.h"
|
||||
|
||||
typedef CHttpAgentT<CSSLAgent, HTTPS_DEFAULT_PORT> CHttpsAgent;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
598
HttpClient.cpp
Normal file
598
HttpClient.cpp
Normal file
@@ -0,0 +1,598 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HttpClient.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::CheckParams()
|
||||
{
|
||||
if(m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0)
|
||||
{
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{
|
||||
USES_CONVERSION;
|
||||
|
||||
WSABUF szBuffer[2];
|
||||
CStringA strHeader;
|
||||
|
||||
LPCSTR lpszHost = nullptr;
|
||||
USHORT usPort = 0;
|
||||
BOOL bConnect = (stricmp(lpszMethod, HTTP_METHOD_CONNECT) == 0);
|
||||
|
||||
if(!bConnect)
|
||||
{
|
||||
GetRemoteHost(&lpszHost, &usPort);
|
||||
if(usPort == default_port) usPort = 0;
|
||||
}
|
||||
|
||||
CStringA strPath;
|
||||
::AdjustRequestPath(bConnect, lpszPath, strPath);
|
||||
|
||||
m_objHttp.SetRequestPath(lpszMethod, strPath);
|
||||
m_objHttp.ReloadCookies();
|
||||
|
||||
::MakeRequestLine(lpszMethod, strPath, m_enLocalVersion, strHeader);
|
||||
::MakeHeaderLines(lpHeaders, iHeaderCount, &m_objHttp.GetCookieMap(), iLength, TRUE, -1, lpszHost, usPort, strHeader);
|
||||
::MakeHttpPacket(strHeader, pBody, iLength, szBuffer);
|
||||
|
||||
return SendPackets(szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendLocalFile(LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount)
|
||||
{
|
||||
CFile file;
|
||||
CFileMapping fmap;
|
||||
|
||||
HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
::SetLastError(hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
|
||||
}
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendChunkData(const BYTE* pData, int iLength, LPCSTR lpszExtensions)
|
||||
{
|
||||
char szLen[12];
|
||||
WSABUF bufs[5];
|
||||
|
||||
int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
|
||||
|
||||
return SendPackets(bufs, iCount);
|
||||
}
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
|
||||
{
|
||||
ASSERT(lpszMask);
|
||||
|
||||
WSABUF szBuffer[2];
|
||||
BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
|
||||
|
||||
unique_ptr<BYTE[]> szData = make_unique<BYTE[]>(iLength);
|
||||
memcpy(szData.get(), pData, iLength);
|
||||
|
||||
if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, lpszMask, szData.get(), iLength, ullBodyLen, szHeader, szBuffer))
|
||||
return FALSE;
|
||||
|
||||
return SendPackets(szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class R, class T, USHORT default_port> BOOL CHttpClientT<R, T, default_port>::StartHttp()
|
||||
{
|
||||
if(IsHttpAutoStart())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock locallock(m_csHttp);
|
||||
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_objHttp.IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoStartHttp();
|
||||
|
||||
if(!IsSecure())
|
||||
FireHandShake();
|
||||
else
|
||||
{
|
||||
#ifdef _SSL_SUPPORT
|
||||
if(IsSSLAutoHandShake())
|
||||
StartSSLHandShakeNoCheck();
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
|
||||
{
|
||||
CleanupRequestResult();
|
||||
|
||||
if(!__super::Start(lpszRemoteAddress, usPort, TRUE, lpszBindAddress, usLocalPort))
|
||||
return FALSE;
|
||||
|
||||
BOOL isOK = WaitForEvent(m_dwConnectTimeout);
|
||||
|
||||
if(!isOK || m_enProgress != HSRP_DONE)
|
||||
{
|
||||
int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT : ERROR_CONNREFUSED;
|
||||
|
||||
if(!isOK) Stop();
|
||||
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ec);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{
|
||||
CleanupRequestResult();
|
||||
|
||||
if(!__super::SendRequest(lpszMethod, lpszPath, lpHeaders, iHeaderCount, pBody, iLength))
|
||||
return FALSE;
|
||||
|
||||
BOOL isOK = WaitForEvent(m_dwRequestTimeout);
|
||||
|
||||
if(!isOK || m_enProgress != HSRP_DONE)
|
||||
{
|
||||
int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT :
|
||||
(m_enProgress == HSRP_CLOSE ? ERROR_CONNABORTED : ERROR_INVALID_DATA);
|
||||
|
||||
if(!isOK) Stop();
|
||||
|
||||
SetLastError(SE_DATA_SEND, __FUNCTION__, ec);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
|
||||
{
|
||||
CleanupRequestResult();
|
||||
|
||||
if(!__super::SendWSMessage(bFinal, iReserved, iOperationCode, lpszMask, pData, iLength, ullBodyLen))
|
||||
return FALSE;
|
||||
|
||||
BOOL isOK = WaitForEvent(m_dwRequestTimeout);
|
||||
|
||||
if(!isOK || m_enProgress != HSRP_DONE)
|
||||
{
|
||||
int ec = m_enProgress == HSRP_WAITING ? ERROR_TIMEDOUT :
|
||||
(m_enProgress == HSRP_CLOSE ? ERROR_CONNABORTED : ERROR_INVALID_DATA);
|
||||
|
||||
if(!isOK) Stop();
|
||||
|
||||
SetLastError(SE_DATA_SEND, __FUNCTION__, ec);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::OpenUrl(LPCSTR lpszMethod, LPCSTR lpszUrl, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength, BOOL bForceReconnect)
|
||||
{
|
||||
BOOL bHttps;
|
||||
USHORT usPort;
|
||||
CStringA strHost;
|
||||
CStringA strPath;
|
||||
|
||||
if(!IsHttpAutoStart())
|
||||
{
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_NOT_SUPPORTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!::ParseUrl(lpszUrl, bHttps, strHost, usPort, strPath))
|
||||
{
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ERROR_ADDRNOTAVAIL);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if((bHttps && default_port == HTTP_DEFAULT_PORT) || (!bHttps && default_port == HTTPS_DEFAULT_PORT))
|
||||
{
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ERROR_PROTO);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(HasStarted())
|
||||
{
|
||||
BOOL bNeedStop = bForceReconnect;
|
||||
|
||||
if(!bNeedStop)
|
||||
{
|
||||
LPCSTR lpszHost = nullptr;
|
||||
USHORT usPort2 = 0;
|
||||
|
||||
GetRemoteHost(&lpszHost, &usPort2);
|
||||
|
||||
if(usPort != usPort2)
|
||||
bNeedStop = TRUE;
|
||||
else
|
||||
{
|
||||
HP_SCOPE_HOST host(CA2T((LPCSTR)strHost));
|
||||
|
||||
if(lstricmp(host.name, CA2T(lpszHost)) != 0)
|
||||
bNeedStop = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(bNeedStop) Stop();
|
||||
}
|
||||
|
||||
EnServiceState state = GetState();
|
||||
|
||||
if(state != SS_STARTED)
|
||||
{
|
||||
if(state == SS_STARTING)
|
||||
{
|
||||
do
|
||||
{
|
||||
::WaitFor(10);
|
||||
state = GetState();
|
||||
} while(state != SS_STARTED && state != SS_STOPPED);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(state != SS_STOPPED)
|
||||
{
|
||||
::WaitFor(10);
|
||||
state = GetState();
|
||||
}
|
||||
|
||||
Start(CA2T(strHost), usPort, FALSE, nullptr);
|
||||
state = GetState();
|
||||
}
|
||||
|
||||
if(state == SS_STOPPED)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(iLength < 0 && !::IsStrEmptyA((LPCSTR)pBody))
|
||||
return SendLocalFile((LPCSTR)pBody, lpszMethod, strPath, lpHeaders, iHeaderCount);
|
||||
|
||||
return SendRequest(lpszMethod, strPath, lpHeaders, iHeaderCount, pBody, iLength);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::CleanupRequestResult()
|
||||
{
|
||||
m_pHttpObj = &m_objHttp;
|
||||
m_enProgress = HSRP_WAITING;
|
||||
|
||||
m_szBuffer.Free();
|
||||
m_objHttp2.Reset();
|
||||
m_evWait.Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpSyncClientT<T, default_port>::SetRequestEvent(EnHttpSyncRequestProgress enProgress, BOOL bCopyHttpObj)
|
||||
{
|
||||
if(m_enProgress != HSRP_WAITING)
|
||||
return;
|
||||
|
||||
m_enProgress = enProgress;
|
||||
|
||||
if(bCopyHttpObj)
|
||||
{
|
||||
m_objHttp2.CopyData(m_objHttp);
|
||||
m_objHttp2.CopyWSContext(m_objHttp);
|
||||
|
||||
m_pHttpObj = &m_objHttp2;
|
||||
}
|
||||
|
||||
m_evWait.Set();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::WaitForEvent(DWORD dwWait)
|
||||
{
|
||||
LONG lTimeout = INFINITE;
|
||||
|
||||
if(dwWait != 0)
|
||||
lTimeout = (LONG)dwWait;
|
||||
|
||||
return (m_evWait.Wait(lTimeout) > TIMEOUT);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpSyncClientT<T, default_port>::GetResponseBody(LPCBYTE* lpszBody, int* iLength)
|
||||
{
|
||||
ASSERT(lpszBody && iLength);
|
||||
|
||||
*lpszBody = m_szBuffer.Ptr();
|
||||
*iLength = (int)m_szBuffer.Size();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnHandShake(ITcpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnHandShake(pSender, dwConnID);
|
||||
|
||||
if(rs != HR_ERROR)
|
||||
SetRequestEvent(HSRP_DONE, FALSE);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnClose(pSender, dwConnID, enOperation, iErrorCode);
|
||||
|
||||
SetRequestEvent(HSRP_CLOSE);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnBody(pSender, dwConnID, pData, iLength);
|
||||
|
||||
if(rs != HPR_ERROR)
|
||||
m_szBuffer.Cat(pData, iLength);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnMessageComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnMessageComplete(pSender, dwConnID);
|
||||
|
||||
if(rs != HPR_ERROR)
|
||||
{
|
||||
if(GetUpgradeType() == HUT_NONE)
|
||||
SetRequestEvent(HSRP_DONE);
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnUpgrade(pSender, dwConnID, enUpgradeType);
|
||||
|
||||
if(rs != HPR_ERROR)
|
||||
{
|
||||
if(enUpgradeType == HUT_WEB_SOCKET)
|
||||
{
|
||||
SetRequestEvent(HSRP_DONE);
|
||||
rs = HPR_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRequestEvent(HSRP_ERROR);
|
||||
rs = HPR_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);
|
||||
|
||||
SetRequestEvent(HSRP_ERROR);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnWSMessageBody(pSender, dwConnID, pData, iLength);
|
||||
|
||||
if(rs != HR_ERROR)
|
||||
m_szBuffer.Cat(pData, iLength);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnWSMessageComplete(pSender, dwConnID);
|
||||
|
||||
if(rs != HR_ERROR)
|
||||
SetRequestEvent(HSRP_DONE);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
return m_pListener2->OnPrepareConnect(pSender, dwConnID, socket);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnConnect(ITcpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
return m_pListener2->OnConnect(pSender, dwConnID);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
return m_pListener2->OnSend(pSender, dwConnID, pData, iLength);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
return m_pListener2->OnReceive(pSender, dwConnID, pData, iLength);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnMessageBegin(IHttpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnMessageBegin(pSender, dwConnID);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnHeader(pSender, dwConnID, lpszName, lpszValue);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnHeadersComplete(pSender, dwConnID);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnChunkHeader(pSender, dwConnID, iLength);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpParseResult CHttpSyncClientT<T, default_port>::OnChunkComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{
|
||||
EnHttpParseResult rs = HPR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnChunkComplete(pSender, dwConnID);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpSyncClientT<T, default_port>::OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
if(m_pListener2 != nullptr)
|
||||
rs = m_pListener2->OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template class CHttpClientT<IHttpRequester, CTcpClient, HTTP_DEFAULT_PORT>;
|
||||
template class CHttpSyncClientT<CTcpClient, HTTP_DEFAULT_PORT>;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLClient.h"
|
||||
|
||||
template class CHttpClientT<IHttpRequester, CSSLClient, HTTPS_DEFAULT_PORT>;
|
||||
template class CHttpSyncClientT<CSSLClient, HTTPS_DEFAULT_PORT>;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
397
HttpClient.h
Normal file
397
HttpClient.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpClient.h"
|
||||
#include "HttpHelper.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class R, class T, USHORT default_port> class CHttpClientT : public R, public T
|
||||
{
|
||||
using __super = T;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
using __super::SendPackets;
|
||||
using __super::HasStarted;
|
||||
using __super::GetRemoteHost;
|
||||
|
||||
using __super::IsSecure;
|
||||
using __super::IsConnected;
|
||||
using __super::FireHandShake;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
using __super::IsSSLAutoHandShake;
|
||||
using __super::StartSSLHandShakeNoCheck;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
using __super::SetLastError;
|
||||
|
||||
using THttpObj = THttpObjT<CHttpClientT, IHttpClient>;
|
||||
friend typename CHttpClientT::THttpObj;
|
||||
|
||||
public:
|
||||
virtual BOOL SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
|
||||
virtual BOOL SendLocalFile(LPCSTR lpszFileName, LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
|
||||
virtual BOOL SendChunkData(const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
|
||||
|
||||
virtual BOOL SendPost(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(HTTP_METHOD_POST, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendPut(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(HTTP_METHOD_PUT, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendPatch(LPCSTR lpszPath, const THeader lpHeaders[], int iHeaderCount, const BYTE* pBody, int iLength)
|
||||
{return SendRequest(HTTP_METHOD_PATCH, lpszPath, lpHeaders, iHeaderCount, pBody, iLength);}
|
||||
virtual BOOL SendGet(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_GET, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendDelete(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_DELETE, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendHead(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_HEAD, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendTrace(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_TRACE, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendOptions(LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_OPTIONS, lpszPath, lpHeaders, iHeaderCount);}
|
||||
virtual BOOL SendConnect(LPCSTR lpszHost, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0)
|
||||
{return SendRequest(HTTP_METHOD_CONNECT, lpszHost, lpHeaders, iHeaderCount);}
|
||||
|
||||
virtual BOOL SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
|
||||
|
||||
virtual BOOL StartHttp();
|
||||
|
||||
public:
|
||||
virtual void SetUseCookie(BOOL bUseCookie) {ENSURE_HAS_STOPPED(); m_pCookieMgr = bUseCookie ? &g_CookieMgr : nullptr;}
|
||||
virtual void SetHttpAutoStart(BOOL bAutoStart) {ENSURE_HAS_STOPPED(); m_bHttpAutoStart = bAutoStart;}
|
||||
virtual void SetLocalVersion(EnHttpVersion enLocalVersion) {ENSURE_HAS_STOPPED(); m_enLocalVersion = enLocalVersion;}
|
||||
|
||||
virtual BOOL IsUseCookie() {return m_pCookieMgr != nullptr;}
|
||||
virtual BOOL IsHttpAutoStart() {return m_bHttpAutoStart;}
|
||||
virtual EnHttpVersion GetLocalVersion() {return m_enLocalVersion;}
|
||||
|
||||
virtual BOOL IsUpgrade()
|
||||
{return m_objHttp.IsUpgrade();}
|
||||
virtual BOOL IsKeepAlive()
|
||||
{return m_objHttp.IsKeepAlive();}
|
||||
virtual USHORT GetVersion()
|
||||
{return m_objHttp.GetVersion();}
|
||||
virtual ULONGLONG GetContentLength()
|
||||
{return m_objHttp.GetContentLength();}
|
||||
virtual LPCSTR GetContentType()
|
||||
{return m_objHttp.GetContentType();}
|
||||
virtual LPCSTR GetContentEncoding()
|
||||
{return m_objHttp.GetContentEncoding();}
|
||||
virtual LPCSTR GetTransferEncoding()
|
||||
{return m_objHttp.GetTransferEncoding();}
|
||||
virtual EnHttpUpgradeType GetUpgradeType()
|
||||
{return m_objHttp.GetUpgradeType();}
|
||||
virtual USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
|
||||
{return m_objHttp.GetParseErrorCode(lpszErrorDesc);}
|
||||
|
||||
virtual BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{return m_objHttp.GetHeader(lpszName, lpszValue);}
|
||||
virtual BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
|
||||
{return m_objHttp.GetHeaders(lpszName, lpszValue, dwCount);}
|
||||
virtual BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
|
||||
{return m_objHttp.GetAllHeaders(lpHeaders, dwCount);}
|
||||
virtual BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
|
||||
{return m_objHttp.GetAllHeaderNames(lpszName, dwCount);}
|
||||
|
||||
virtual BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{return m_objHttp.GetCookie(lpszName, lpszValue);}
|
||||
virtual BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
|
||||
{return m_objHttp.GetAllCookies(lpCookies, dwCount);}
|
||||
|
||||
virtual USHORT GetStatusCode()
|
||||
{return m_objHttp.GetStatusCode();}
|
||||
|
||||
virtual BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
|
||||
{return m_objHttp.GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);}
|
||||
|
||||
private:
|
||||
virtual BOOL CheckParams();
|
||||
|
||||
void DoStartHttp()
|
||||
{m_objHttp.SetValid(TRUE);}
|
||||
|
||||
virtual EnHandleResult FireConnect()
|
||||
{return m_bHttpAutoStart ? __super::FireConnect() : __super::DoFireConnect(this);}
|
||||
|
||||
virtual EnHandleResult DoFireConnect(ITcpClient* pSender)
|
||||
{
|
||||
ASSERT(pSender == this);
|
||||
|
||||
DoStartHttp();
|
||||
|
||||
EnHandleResult result = __super::DoFireConnect(this);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
m_objHttp.SetValid(FALSE);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{ASSERT(pSender == this); return m_objHttp.IsValid() ? m_objHttp.Execute(pData, iLength) : __super::DoFireReceive(pSender, pData, iLength);}
|
||||
|
||||
EnHandleResult DoFireSuperReceive(IHttpClient* pSender, const BYTE* pData, int iLength)
|
||||
{ASSERT(pSender == (IHttpClient*)this); return __super::DoFireReceive(pSender, pData, iLength);}
|
||||
|
||||
virtual EnHandleResult DoFireClose(ITcpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
ASSERT(pSender == (IHttpClient*)this);
|
||||
|
||||
m_objHttp.CheckBodyIdentityEof();
|
||||
|
||||
return __super::DoFireClose(pSender, enOperation, iErrorCode);
|
||||
}
|
||||
|
||||
virtual void Reset()
|
||||
{
|
||||
m_objHttp.Reset();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
EnHttpParseResult FireMessageBegin(IHttpClient* pSender)
|
||||
{return m_pListener->OnMessageBegin(pSender, pSender->GetConnectionID());}
|
||||
EnHttpParseResult FireRequestLine(IHttpClient* pSender, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_pListener->OnRequestLine(pSender, pSender->GetConnectionID(), lpszMethod, lpszUrl);}
|
||||
EnHttpParseResult FireStatusLine(IHttpClient* pSender, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_pListener->OnStatusLine(pSender, pSender->GetConnectionID(), usStatusCode, lpszDesc);}
|
||||
EnHttpParseResult FireHeader(IHttpClient* pSender, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_pListener->OnHeader(pSender, pSender->GetConnectionID(), lpszName, lpszValue);}
|
||||
EnHttpParseResult FireHeadersComplete(IHttpClient* pSender)
|
||||
{return m_pListener->OnHeadersComplete(pSender, pSender->GetConnectionID());}
|
||||
EnHttpParseResult FireBody(IHttpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnBody(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
EnHttpParseResult FireChunkHeader(IHttpClient* pSender, int iLength)
|
||||
{return m_pListener->OnChunkHeader(pSender, pSender->GetConnectionID(), iLength);}
|
||||
EnHttpParseResult FireChunkComplete(IHttpClient* pSender)
|
||||
{return m_pListener->OnChunkComplete(pSender, pSender->GetConnectionID());}
|
||||
EnHttpParseResult FireMessageComplete(IHttpClient* pSender)
|
||||
{return m_pListener->OnMessageComplete(pSender, pSender->GetConnectionID());}
|
||||
EnHttpParseResult FireUpgrade(IHttpClient* pSender, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_pListener->OnUpgrade(pSender, pSender->GetConnectionID(), enUpgradeType);}
|
||||
EnHttpParseResult FireParseError(IHttpClient* pSender, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_pListener->OnParseError(pSender, pSender->GetConnectionID(), iErrorCode, lpszErrorDesc);}
|
||||
|
||||
EnHandleResult FireWSMessageHeader(IHttpClient* pSender, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_pListener->OnWSMessageHeader(pSender, pSender->GetConnectionID(), bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
EnHandleResult FireWSMessageBody(IHttpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnWSMessageBody(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
EnHandleResult FireWSMessageComplete(IHttpClient* pSender)
|
||||
{return m_pListener->OnWSMessageComplete(pSender, pSender->GetConnectionID());}
|
||||
|
||||
CCookieMgr* GetCookieMgr() {return m_pCookieMgr;}
|
||||
LPCSTR GetRemoteDomain(IHttpClient* pSender) {LPCSTR lpszDomain; GetRemoteHost(&lpszDomain); return lpszDomain;}
|
||||
|
||||
public:
|
||||
CHttpClientT(IHttpClientListener* pListener)
|
||||
: T (pListener)
|
||||
, m_pListener (pListener)
|
||||
, m_pCookieMgr (&g_CookieMgr)
|
||||
, m_bHttpAutoStart (TRUE)
|
||||
, m_enLocalVersion (DEFAULT_HTTP_VERSION)
|
||||
, m_objHttp (FALSE, this, (IHttpClient*)this)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CHttpClientT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL m_bHttpAutoStart;
|
||||
|
||||
IHttpClientListener* m_pListener;
|
||||
CCookieMgr* m_pCookieMgr;
|
||||
EnHttpVersion m_enLocalVersion;
|
||||
|
||||
CReentrantCriSec m_csHttp;
|
||||
|
||||
protected:
|
||||
THttpObj m_objHttp;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template<class T, USHORT default_port> class CHttpSyncClientT : public CHttpClientT<IHttpSyncRequester, T, default_port>, private CHttpClientListener
|
||||
{
|
||||
using __super = CHttpClientT<IHttpSyncRequester, T, default_port>;
|
||||
using __super::m_objHttp;
|
||||
using __super::SetLastError;
|
||||
|
||||
using typename __super::THttpObj;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
using __super::HasStarted;
|
||||
using __super::GetRemoteHost;
|
||||
using __super::SendLocalFile;
|
||||
using __super::IsHttpAutoStart;
|
||||
|
||||
public:
|
||||
virtual BOOL Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
|
||||
public:
|
||||
virtual BOOL SendRequest(LPCSTR lpszMethod, LPCSTR lpszPath, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0);
|
||||
virtual BOOL SendWSMessage(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4] = nullptr, const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
|
||||
|
||||
public:
|
||||
virtual BOOL IsUpgrade()
|
||||
{return m_pHttpObj->IsUpgrade();}
|
||||
virtual BOOL IsKeepAlive()
|
||||
{return m_pHttpObj->IsKeepAlive();}
|
||||
virtual USHORT GetVersion()
|
||||
{return m_pHttpObj->GetVersion();}
|
||||
virtual ULONGLONG GetContentLength()
|
||||
{return m_pHttpObj->GetContentLength();}
|
||||
virtual LPCSTR GetContentType()
|
||||
{return m_pHttpObj->GetContentType();}
|
||||
virtual LPCSTR GetContentEncoding()
|
||||
{return m_pHttpObj->GetContentEncoding();}
|
||||
virtual LPCSTR GetTransferEncoding()
|
||||
{return m_pHttpObj->GetTransferEncoding();}
|
||||
virtual EnHttpUpgradeType GetUpgradeType()
|
||||
{return m_pHttpObj->GetUpgradeType();}
|
||||
virtual USHORT GetParseErrorCode(LPCSTR* lpszErrorDesc = nullptr)
|
||||
{return m_pHttpObj->GetParseErrorCode(lpszErrorDesc);}
|
||||
|
||||
virtual BOOL GetHeader(LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{return m_pHttpObj->GetHeader(lpszName, lpszValue);}
|
||||
virtual BOOL GetHeaders(LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
|
||||
{return m_pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);}
|
||||
virtual BOOL GetAllHeaders(THeader lpHeaders[], DWORD& dwCount)
|
||||
{return m_pHttpObj->GetAllHeaders(lpHeaders, dwCount);}
|
||||
virtual BOOL GetAllHeaderNames(LPCSTR lpszName[], DWORD& dwCount)
|
||||
{return m_pHttpObj->GetAllHeaderNames(lpszName, dwCount);}
|
||||
|
||||
virtual BOOL GetCookie(LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{return m_pHttpObj->GetCookie(lpszName, lpszValue);}
|
||||
virtual BOOL GetAllCookies(TCookie lpCookies[], DWORD& dwCount)
|
||||
{return m_pHttpObj->GetAllCookies(lpCookies, dwCount);}
|
||||
|
||||
virtual USHORT GetStatusCode()
|
||||
{return m_pHttpObj->GetStatusCode();}
|
||||
|
||||
virtual BOOL GetWSMessageState(BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
|
||||
{return m_pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);}
|
||||
|
||||
public:
|
||||
virtual BOOL OpenUrl(LPCSTR lpszMethod, LPCSTR lpszUrl, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pBody = nullptr, int iLength = 0, BOOL bForceReconnect = FALSE);
|
||||
virtual BOOL CleanupRequestResult();
|
||||
|
||||
public:
|
||||
virtual BOOL GetResponseBody (LPCBYTE* lpszBody, int* iLength);
|
||||
|
||||
virtual void SetConnectTimeout (DWORD dwConnectTimeout) {ENSURE_HAS_STOPPED(); m_dwConnectTimeout = dwConnectTimeout;}
|
||||
virtual void SetRequestTimeout (DWORD dwRequestTimeout) {ENSURE_HAS_STOPPED(); m_dwRequestTimeout = dwRequestTimeout;}
|
||||
|
||||
virtual DWORD GetConnectTimeout () {return m_dwConnectTimeout;}
|
||||
virtual DWORD GetRequestTimeout () {return m_dwRequestTimeout;}
|
||||
|
||||
private:
|
||||
virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID);
|
||||
virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
virtual EnHttpParseResult OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
|
||||
virtual EnHttpParseResult OnMessageComplete(IHttpClient* pSender, CONNID dwConnID);
|
||||
virtual EnHttpParseResult OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType);
|
||||
virtual EnHttpParseResult OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc);
|
||||
|
||||
virtual EnHandleResult OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID);
|
||||
|
||||
virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket);
|
||||
virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID);
|
||||
virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength);
|
||||
|
||||
virtual EnHttpParseResult OnMessageBegin(IHttpClient* pSender, CONNID dwConnID);
|
||||
virtual EnHttpParseResult OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc);
|
||||
virtual EnHttpParseResult OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue);
|
||||
virtual EnHttpParseResult OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID);
|
||||
virtual EnHttpParseResult OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength);
|
||||
virtual EnHttpParseResult OnChunkComplete(IHttpClient* pSender, CONNID dwConnID);
|
||||
|
||||
virtual EnHandleResult OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen);
|
||||
|
||||
|
||||
private:
|
||||
void SetRequestEvent(EnHttpSyncRequestProgress enProgress, BOOL bCopyHttpObj = TRUE);
|
||||
BOOL WaitForEvent(DWORD dwWait);
|
||||
|
||||
public:
|
||||
CHttpSyncClientT(IHttpClientListener* pListener = nullptr)
|
||||
: __super (this)
|
||||
, m_enProgress (HSRP_DONE)
|
||||
, m_objHttp2 (FALSE, this, (IHttpClient*)this)
|
||||
, m_pHttpObj (nullptr)
|
||||
, m_pListener2 (pListener)
|
||||
, m_dwConnectTimeout (DEFAULT_HTTP_SYNC_CONNECT_TIMEOUT)
|
||||
, m_dwRequestTimeout (DEFAULT_HTTP_SYNC_REQUEST_TIMEOUT)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CHttpSyncClientT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwConnectTimeout;
|
||||
DWORD m_dwRequestTimeout;
|
||||
|
||||
CEvt m_evWait;
|
||||
THttpObj m_objHttp2;
|
||||
|
||||
THttpObj* m_pHttpObj;
|
||||
IHttpClientListener* m_pListener2;
|
||||
|
||||
EnHttpSyncRequestProgress m_enProgress;
|
||||
CBufferPtrT<BYTE, 16 * 1024> m_szBuffer;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
typedef CHttpClientT<IHttpRequester, CTcpClient, HTTP_DEFAULT_PORT> CHttpClient;
|
||||
typedef CHttpSyncClientT<CTcpClient, HTTP_DEFAULT_PORT> CHttpSyncClient;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLClient.h"
|
||||
|
||||
typedef CHttpClientT<IHttpRequester, CSSLClient, HTTPS_DEFAULT_PORT> CHttpsClient;
|
||||
typedef CHttpSyncClientT<CSSLClient, HTTPS_DEFAULT_PORT> CHttpsSyncClient;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
927
HttpCookie.cpp
Normal file
927
HttpCookie.cpp
Normal file
@@ -0,0 +1,927 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HttpCookie.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
static const char* s_short_week[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
static const char* s_short_month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
|
||||
CCookieMgr g_CookieMgr;
|
||||
|
||||
CCookie* CCookie::FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain, LPCSTR lpszDefaultPath)
|
||||
{
|
||||
CStringA strName;
|
||||
CStringA strValue;
|
||||
CStringA strDomain;
|
||||
CStringA strPath;
|
||||
|
||||
int iMaxAge = -1;
|
||||
BOOL bHttpOnly = FALSE;
|
||||
BOOL bSecure = FALSE;
|
||||
EnSameSite enSameSite = SS_UNKNOWN;
|
||||
|
||||
int i = 0;
|
||||
int iStart = 0;
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
CStringA strField = strCookie.Tokenize(COOKIE_FIELD_SEP, iStart);
|
||||
strField.Trim();
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
ParseFieldKV(strField, strName, strValue, COOKIE_KV_SEP_CHAR);
|
||||
|
||||
if(strName.IsEmpty())
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strField.IsEmpty())
|
||||
break;
|
||||
|
||||
CStringA strKey;
|
||||
CStringA strVal;
|
||||
|
||||
ParseFieldKV(strField, strKey, strVal, COOKIE_KV_SEP_CHAR);
|
||||
|
||||
if(strKey.CompareNoCase(COOKIE_DOMAIN) == 0)
|
||||
strDomain = strVal;
|
||||
else if(strKey.CompareNoCase(COOKIE_PATH) == 0)
|
||||
strPath = strVal;
|
||||
else if(strKey.CompareNoCase(COOKIE_MAX_AGE) == 0 && !strVal.IsEmpty())
|
||||
iMaxAge = atoi(strVal);
|
||||
else if(strKey.CompareNoCase(COOKIE_EXPIRES) == 0 && !strVal.IsEmpty() && iMaxAge == -1)
|
||||
{
|
||||
__time64_t tmExpires = -1;
|
||||
|
||||
if(!ParseExpires(strVal, tmExpires))
|
||||
return nullptr;
|
||||
|
||||
iMaxAge = ExpiresToMaxAge(tmExpires);
|
||||
}
|
||||
else if(strKey.CompareNoCase(COOKIE_HTTPONLY) == 0)
|
||||
bHttpOnly = TRUE;
|
||||
else if(strKey.CompareNoCase(COOKIE_SECURE) == 0)
|
||||
bSecure = TRUE;
|
||||
else if(strKey.CompareNoCase(COOKIE_SAMESITE) == 0)
|
||||
{
|
||||
if(strVal.IsEmpty() || strVal.CompareNoCase(COOKIE_SAMESITE_LAX) == 0)
|
||||
enSameSite = SS_LAX;
|
||||
else if(strVal.CompareNoCase(COOKIE_SAMESITE_STRICT) == 0)
|
||||
enSameSite = SS_STRICT;
|
||||
else if(strVal.CompareNoCase(COOKIE_SAMESITE_NONE) == 0)
|
||||
enSameSite = SS_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if(!AdjustDomain(strDomain, lpszDefaultDomain) || !AdjustPath(strPath, lpszDefaultPath))
|
||||
return nullptr;
|
||||
|
||||
CCookie* pCookie = new CCookie(strName, strValue, strDomain, strPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
|
||||
ASSERT(pCookie->IsValid());
|
||||
|
||||
return pCookie;
|
||||
}
|
||||
|
||||
CStringA CCookie::ToString(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
|
||||
{
|
||||
CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
|
||||
return cookie.ToString();
|
||||
}
|
||||
|
||||
BOOL CCookie::ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, EnSameSite enSameSite)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
CStringA str = ToString(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
|
||||
int iLength = str.GetLength() + 1;
|
||||
|
||||
if(lpszBuff && iBuffLen >= iLength)
|
||||
{
|
||||
memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
iBuffLen = iLength;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
CStringA CCookie::ToString()
|
||||
{
|
||||
ASSERT(!name.IsEmpty());
|
||||
|
||||
CStringA strCookie;
|
||||
|
||||
strCookie.AppendFormat("%s=%s", (LPCSTR)name, (LPCSTR)value);
|
||||
|
||||
if(!domain.IsEmpty())
|
||||
strCookie.AppendFormat("; %s=%s", COOKIE_DOMAIN, (LPCSTR)domain);
|
||||
if(!path.IsEmpty())
|
||||
strCookie.AppendFormat("; %s=%s", COOKIE_PATH, (LPCSTR)path);
|
||||
if(expires >= 0)
|
||||
strCookie.AppendFormat("; %s=%s", COOKIE_EXPIRES, (LPCSTR)MakeExpiresStr(expires));
|
||||
if(httpOnly)
|
||||
strCookie.AppendFormat("; %s", COOKIE_HTTPONLY);
|
||||
if(secure)
|
||||
strCookie.AppendFormat("; %s", COOKIE_SECURE);
|
||||
if(sameSite != SS_UNKNOWN)
|
||||
strCookie.AppendFormat("; %s=%s", COOKIE_SAMESITE, sameSite == SS_LAX ? COOKIE_SAMESITE_LAX : (sameSite == SS_STRICT ? COOKIE_SAMESITE_STRICT : COOKIE_SAMESITE_NONE));
|
||||
|
||||
return strCookie;
|
||||
}
|
||||
|
||||
BOOL CCookie::Match(LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
|
||||
{
|
||||
int iLen = (int)strlen(lpszDomain);
|
||||
int iDiff = iLen - domain.GetLength();
|
||||
|
||||
if(iDiff < 0 || stricmp(lpszDomain + iDiff, domain) != 0)
|
||||
return FALSE;
|
||||
if(iDiff > 0 && *(lpszDomain + iDiff - 1) != COOKIE_DOMAIN_SEP_CHAR)
|
||||
return FALSE;
|
||||
if(strncmp(lpszPath, path, path.GetLength()) != 0)
|
||||
return FALSE;
|
||||
|
||||
return (bHttp || !httpOnly) && (bSecure || !secure);
|
||||
}
|
||||
|
||||
BOOL CCookie::IsSameDomain(LPCSTR lpszDomain)
|
||||
{
|
||||
int iLen = (int)strlen(lpszDomain);
|
||||
int iDiff = iLen - domain.GetLength();
|
||||
|
||||
LPCSTR lpszLong, lpszShort;
|
||||
|
||||
if(iDiff < 0)
|
||||
{
|
||||
lpszLong = (LPCSTR)domain + iDiff;
|
||||
lpszShort = lpszDomain;
|
||||
}
|
||||
else
|
||||
{
|
||||
lpszLong = lpszDomain + iDiff;
|
||||
lpszShort = (LPCSTR)domain;
|
||||
}
|
||||
|
||||
if(stricmp(lpszLong, lpszShort) != 0)
|
||||
return FALSE;
|
||||
if(iDiff != 0 && *(lpszLong - 1) != COOKIE_DOMAIN_SEP_CHAR)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CCookie::ParseFieldKV(const CStringA& strField, CStringA& strKey, CStringA& strVal, char chSep)
|
||||
{
|
||||
int i = strField.Find(chSep);
|
||||
|
||||
if(i < 0)
|
||||
strKey = strField;
|
||||
else
|
||||
{
|
||||
strKey = strField.Left(i);
|
||||
strVal = strField.Mid(i + 1);
|
||||
|
||||
strVal.Trim();
|
||||
}
|
||||
|
||||
strKey.Trim();
|
||||
}
|
||||
|
||||
BOOL CCookie::ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires)
|
||||
{
|
||||
int iLength = (int)strlen(lpszExpires);
|
||||
|
||||
if(iLength == 0 || iLength > 50)
|
||||
return FALSE;
|
||||
|
||||
char szMonth[10];
|
||||
char szZone[10];
|
||||
|
||||
tm t = {0};
|
||||
|
||||
if(sscanf( lpszExpires, "%*[^, ]%*[, ]%2d%*[-/ ]%8[^-/ ]%*[-/ ]%4d %2d:%2d:%2d %8s",
|
||||
&t.tm_mday, szMonth, &t.tm_year, &t.tm_hour, &t.tm_min, &t.tm_sec, szZone) != 7)
|
||||
return FALSE;
|
||||
|
||||
if(t.tm_year < 70)
|
||||
t.tm_year += 100;
|
||||
else if (t.tm_year > 100)
|
||||
t.tm_year -= 1900;
|
||||
|
||||
int i = 0;
|
||||
int size = _countof(s_short_month);
|
||||
|
||||
for(; i < size; i++)
|
||||
{
|
||||
if(strnicmp(szMonth, s_short_month[i], 3) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if(i == size)
|
||||
return FALSE;
|
||||
|
||||
t.tm_mon = i;
|
||||
|
||||
CStringA strZone = szZone;
|
||||
|
||||
int iZone = 0;
|
||||
int iMix = 0;
|
||||
int iPos = strZone.Find('+');
|
||||
|
||||
if(iPos >= 0)
|
||||
iMix = 1;
|
||||
else
|
||||
{
|
||||
iPos = strZone.Find('-');
|
||||
|
||||
if(iPos >= 0)
|
||||
iMix = -1;
|
||||
}
|
||||
|
||||
if(iPos >= 0)
|
||||
{
|
||||
strZone = strZone.Mid(iPos + 1);
|
||||
strZone.Remove(':');
|
||||
|
||||
int val = atoi(strZone);
|
||||
|
||||
if(val > 0)
|
||||
{
|
||||
int minutes = val % 100;
|
||||
int hours = val / 100;
|
||||
|
||||
iZone = iMix * (minutes * 60 + hours * 3600);
|
||||
}
|
||||
}
|
||||
|
||||
tmExpires = GetUTCTime(t, iZone);
|
||||
|
||||
return tmExpires >= 0;
|
||||
}
|
||||
|
||||
BOOL CCookie::AdjustDomain(CStringA& strDomain, LPCSTR lpszDefaultDomain)
|
||||
{
|
||||
if(strDomain.IsEmpty() && lpszDefaultDomain)
|
||||
strDomain = lpszDefaultDomain;
|
||||
|
||||
strDomain.TrimLeft(COOKIE_DOMAIN_SEP_CHAR).MakeLower();
|
||||
|
||||
return !strDomain.IsEmpty();
|
||||
}
|
||||
|
||||
BOOL CCookie::AdjustPath(CStringA& strPath, LPCSTR lpszDefaultPath)
|
||||
{
|
||||
if(strPath.IsEmpty() && lpszDefaultPath)
|
||||
strPath = lpszDefaultPath;
|
||||
|
||||
int iLength = strPath.GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return FALSE;
|
||||
|
||||
if(strPath.GetAt(iLength - 1) != COOKIE_PATH_SEP_CHAR)
|
||||
{
|
||||
int iPos = strPath.ReverseFind(COOKIE_PATH_SEP_CHAR);
|
||||
|
||||
if(iPos >= 0)
|
||||
strPath = strPath.Left(iPos + 1);
|
||||
else
|
||||
strPath.Empty();
|
||||
}
|
||||
|
||||
if(!strPath.IsEmpty() && strPath.GetAt(0) != COOKIE_PATH_SEP_CHAR)
|
||||
strPath.Insert(0, COOKIE_PATH_SEP_CHAR);
|
||||
|
||||
return !strPath.IsEmpty();
|
||||
}
|
||||
|
||||
__time64_t CCookie::GetUTCTime(tm& t, int iSecondOffsetTZ)
|
||||
{
|
||||
__time64_t v = _mkgmtime64(&t);
|
||||
if(v >= 0) v -= iSecondOffsetTZ;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
CStringA CCookie::MakeExpiresStr(__time64_t tmExpires)
|
||||
{
|
||||
ASSERT( tmExpires >= 0);
|
||||
|
||||
if(tmExpires < 1) tmExpires = 1;
|
||||
|
||||
tm t;
|
||||
VERIFY(_gmtime64(&t, &tmExpires) != nullptr);
|
||||
|
||||
CStringA str;
|
||||
str.Format("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
|
||||
s_short_week[t.tm_wday], t.tm_mday, s_short_month[t.tm_mon], t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
BOOL CCookie::MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
CStringA str = MakeExpiresStr(tmExpires);
|
||||
int iLength = str.GetLength() + 1;
|
||||
|
||||
if(lpszBuff && iBuffLen >= iLength)
|
||||
{
|
||||
memcpy(lpszBuff, (LPCSTR)str, iLength * sizeof(char));
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
iBuffLen = iLength;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
BOOL CCookieMgr::LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
FILE* pFile = nullptr;
|
||||
|
||||
if((pFile = fopen(lpszFile, "r")) == nullptr)
|
||||
goto _ERROR_END;
|
||||
|
||||
{
|
||||
CStringA strDomain;
|
||||
CStringA strPath;
|
||||
CCookie cookie;
|
||||
|
||||
char szBuffer[8192];
|
||||
int iBufferSize = _countof(szBuffer);
|
||||
__time64_t tmCurrent = _time64(nullptr);
|
||||
CCookieSet* pCookieSet = nullptr;
|
||||
|
||||
CWriteLock locallock(m_cs);
|
||||
|
||||
if(!bKeepExists)
|
||||
ClearDomainCookiesNoLock();
|
||||
|
||||
while(fgets(szBuffer, iBufferSize, pFile) != nullptr)
|
||||
{
|
||||
char c = szBuffer[0];
|
||||
|
||||
if(c == '\n' || c == '\r')
|
||||
continue;
|
||||
else if(c != '\t')
|
||||
{
|
||||
if(!LoadDomainAndPath(szBuffer, strDomain, strPath))
|
||||
goto _ERROR_END;
|
||||
|
||||
pCookieSet = GetCookieSetNoLock(strDomain, strPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!LoadCookie(szBuffer, strDomain, strPath, cookie))
|
||||
goto _ERROR_END;
|
||||
|
||||
if(cookie.expires <= tmCurrent)
|
||||
continue;
|
||||
|
||||
if(pCookieSet)
|
||||
{
|
||||
if(bKeepExists)
|
||||
{
|
||||
CCookieSetCI it = pCookieSet->find(cookie);
|
||||
if(it != pCookieSet->end())
|
||||
continue;
|
||||
}
|
||||
|
||||
pCookieSet->emplace(move(cookie));
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCookieNoLock(cookie, FALSE);
|
||||
pCookieSet = GetCookieSetNoLock(strDomain, strPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!feof(pFile))
|
||||
goto _ERROR_END;
|
||||
}
|
||||
|
||||
isOK = TRUE;
|
||||
|
||||
_ERROR_END:
|
||||
|
||||
if(pFile) fclose(pFile);
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::SaveToFile(LPCSTR lpszFile, BOOL bKeepExists)
|
||||
{
|
||||
if(bKeepExists)
|
||||
{
|
||||
if(!LoadFromFile(lpszFile, TRUE) && !IS_ERROR(ERROR_FILE_NOT_FOUND))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL isOK = FALSE;
|
||||
FILE* pFile = nullptr;
|
||||
|
||||
if((pFile = fopen(lpszFile, "w")) == nullptr)
|
||||
goto _ERROR_END;
|
||||
|
||||
{
|
||||
__time64_t tmCurrent = _time64(nullptr);
|
||||
|
||||
CReadLock locallock(m_cs);
|
||||
|
||||
for(CCookieDomainMapCI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
|
||||
{
|
||||
const CStringA& strDomain = it->first;
|
||||
const CCookiePathMap& paths = it->second;
|
||||
|
||||
for(CCookiePathMapCI it2 = paths.begin(), end2 = paths.end(); it2 != end2; ++it2)
|
||||
{
|
||||
const CStringA& strPath = it2->first;
|
||||
const CCookieSet& cookies = it2->second;
|
||||
|
||||
if(fprintf(pFile, "%s %s\n", (LPCSTR)strDomain, (LPCSTR)strPath) < 0)
|
||||
goto _ERROR_END;
|
||||
|
||||
for(CCookieSetCI it3 = cookies.begin(), end3 = cookies.end(); it3 != end3; ++it3)
|
||||
{
|
||||
const CCookie& cookie = *it3;
|
||||
|
||||
if(cookie.expires <= tmCurrent)
|
||||
continue;
|
||||
|
||||
LPCSTR lpszValue = (LPCSTR)cookie.value;
|
||||
|
||||
if(lpszValue[0] == 0)
|
||||
lpszValue = " ";
|
||||
|
||||
if(fprintf(pFile, "\t%s;%s;%lld;%d;%d;%d\n", (LPCSTR)cookie.name, lpszValue, cookie.expires, cookie.httpOnly, cookie.secure, cookie.sameSite) < 0)
|
||||
goto _ERROR_END;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOK = TRUE;
|
||||
|
||||
_ERROR_END:
|
||||
|
||||
if(pFile) fclose(pFile);
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::ClearCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
CStringA strDomain;
|
||||
CStringA strPath;
|
||||
|
||||
if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
|
||||
return FALSE;
|
||||
|
||||
CWriteLock locallock(m_cs);
|
||||
|
||||
ClearDomainCookiesNoLock(lpszDomain, lpszPath);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::RemoveExpiredCookies(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
CStringA strDomain;
|
||||
CStringA strPath;
|
||||
|
||||
if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, TRUE))
|
||||
return FALSE;
|
||||
|
||||
CWriteLock locallock(m_cs);
|
||||
|
||||
RemoveExpiredCookiesNoLock(lpszDomain, lpszPath);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
|
||||
{
|
||||
ASSERT(lpszDomain && lpszPath);
|
||||
|
||||
CStringA strDomain;
|
||||
CStringA strPath;
|
||||
|
||||
if(!AdjustDomainAndPath(lpszDomain, lpszPath, strDomain, strPath, FALSE))
|
||||
return FALSE;
|
||||
|
||||
list<LPCSTR> lsDomains(1, lpszDomain);
|
||||
list<CStringA> lsPaths(1, lpszPath);
|
||||
|
||||
char c;
|
||||
LPCSTR lpszTemp = lpszDomain;
|
||||
|
||||
while((c = *(++lpszTemp)) != 0)
|
||||
{
|
||||
if(c == COOKIE_DOMAIN_SEP_CHAR)
|
||||
{
|
||||
if((c = *(++lpszTemp)) != 0)
|
||||
lsDomains.push_back(lpszTemp);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lpszTemp = lpszPath + strlen(lpszPath) - 1;
|
||||
|
||||
while(--lpszTemp >= lpszPath)
|
||||
{
|
||||
if((c = *lpszTemp) == COOKIE_PATH_SEP_CHAR)
|
||||
{
|
||||
*(LPSTR)(lpszTemp + 1) = 0;
|
||||
lsPaths.push_back(lpszPath);
|
||||
}
|
||||
}
|
||||
|
||||
CReadLock locallock(m_cs);
|
||||
|
||||
for(list<LPCSTR>::const_iterator it = lsDomains.begin(), end = lsDomains.end(); it != end; ++it)
|
||||
{
|
||||
for(list<CStringA>::const_iterator it2 = lsPaths.begin(), end2 = lsPaths.end(); it2 != end2; ++it2)
|
||||
MatchCookiesNoLock(cookies, *it, *it2, bHttp, bSecure);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge, BOOL bHttpOnly, BOOL bSecure, CCookie::EnSameSite enSameSite, BOOL bOnlyUpdateValueIfExists)
|
||||
{
|
||||
CCookie cookie(lpszName, lpszValue, lpszDomain, lpszPath, iMaxAge, bHttpOnly, bSecure, enSameSite);
|
||||
|
||||
return SetCookie(cookie, bOnlyUpdateValueIfExists);
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists)
|
||||
{
|
||||
unique_ptr<CCookie> pCookie(CCookie::FromString(strCookie));
|
||||
if(!pCookie) return FALSE;
|
||||
|
||||
return SetCookie(*pCookie, bOnlyUpdateValueIfExists);
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
|
||||
{
|
||||
if(!cookie.IsValid()) return FALSE;
|
||||
|
||||
CWriteLock locallock(m_cs);
|
||||
|
||||
return SetCookieNoLock(cookie, bOnlyUpdateValueIfExists);
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
|
||||
{
|
||||
CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
|
||||
|
||||
return DeleteCookie(cookie);
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::DeleteCookie(const CCookie& cookie)
|
||||
{
|
||||
if(!cookie.IsValid()) return FALSE;
|
||||
|
||||
CWriteLock locallock(m_cs);
|
||||
|
||||
return DeleteCookieNoLock(cookie);
|
||||
}
|
||||
|
||||
void CCookieMgr::ClearDomainCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
if(!lpszDomain && !lpszPath)
|
||||
m_cookies.clear();
|
||||
else if(!lpszPath)
|
||||
m_cookies.erase(lpszDomain);
|
||||
else
|
||||
{
|
||||
if(!lpszDomain)
|
||||
{
|
||||
for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
|
||||
ClearPathCookiesNoLock(it->second, lpszPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCookieDomainMapI it = m_cookies.find(lpszDomain);
|
||||
if(it != m_cookies.end())
|
||||
ClearPathCookiesNoLock(it->second, lpszPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CCookieMgr::ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
|
||||
{
|
||||
if(!lpszPath)
|
||||
paths.clear();
|
||||
else
|
||||
{
|
||||
CCookiePathMapI it = paths.find(lpszPath);
|
||||
if(it != paths.end())
|
||||
paths.erase(it->first);
|
||||
}
|
||||
}
|
||||
|
||||
void CCookieMgr::RemoveExpiredCookiesNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
if(!lpszDomain)
|
||||
{
|
||||
for(CCookieDomainMapI it = m_cookies.begin(), end = m_cookies.end(); it != end; ++it)
|
||||
RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCookieDomainMapI it = m_cookies.find(lpszDomain);
|
||||
if(it != m_cookies.end())
|
||||
RemoveDomainExpiredCookiesNoLock(it->second, lpszPath);
|
||||
}
|
||||
}
|
||||
|
||||
void CCookieMgr::RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath)
|
||||
{
|
||||
if(!lpszPath)
|
||||
{
|
||||
for(CCookiePathMapI it = paths.begin(), end = paths.end(); it != end; ++it)
|
||||
RemovePathExpiredCookiesNoLock(it->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
CCookiePathMapI it = paths.find(lpszPath);
|
||||
if(it != paths.end())
|
||||
RemovePathExpiredCookiesNoLock(it->second);
|
||||
}
|
||||
}
|
||||
|
||||
void CCookieMgr::RemovePathExpiredCookiesNoLock(CCookieSet& cookies)
|
||||
{
|
||||
CCookiePtrSet ptrs;
|
||||
|
||||
for(CCookieSetI it = cookies.begin(), end = cookies.end(); it != end; ++it)
|
||||
{
|
||||
if(it->IsExpired())
|
||||
ptrs.emplace(&*it);
|
||||
}
|
||||
|
||||
if(!ptrs.empty())
|
||||
{
|
||||
for(CCookiePtrSetI it = ptrs.begin(), end = ptrs.end(); it != end; ++it)
|
||||
cookies.erase(**it);
|
||||
}
|
||||
}
|
||||
|
||||
const CCookie* CCookieMgr::GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName)
|
||||
{
|
||||
const CCookie cookie(lpszName, nullptr, lpszDomain, lpszPath);
|
||||
return GetCookieNoLock(cookie);
|
||||
}
|
||||
|
||||
const CCookie* CCookieMgr::GetCookieNoLock(const CCookie& cookie)
|
||||
{
|
||||
const CCookie* pCookie = nullptr;
|
||||
|
||||
CCookieDomainMapCI it = m_cookies.find(cookie.domain);
|
||||
if(it != m_cookies.end())
|
||||
{
|
||||
CCookiePathMapCI it2 = it->second.find(cookie.path);
|
||||
if(it2 != it->second.end())
|
||||
{
|
||||
CCookieSetCI it3 = it2->second.find(cookie);
|
||||
if(it3 != it2->second.end())
|
||||
pCookie = &*it3;
|
||||
}
|
||||
}
|
||||
|
||||
return pCookie;
|
||||
}
|
||||
|
||||
void CCookieMgr::MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure)
|
||||
{
|
||||
CCookieDomainMapCI it = m_cookies.find(lpszDomain);
|
||||
if(it != m_cookies.end())
|
||||
{
|
||||
CCookiePathMapCI it2 = it->second.find(lpszPath);
|
||||
if(it2 != it->second.end())
|
||||
{
|
||||
for(CCookieSetCI it3 = it2->second.begin(), end3 = it2->second.end(); it3 != end3; ++it3)
|
||||
{
|
||||
const CCookie& cookie = *it3;
|
||||
|
||||
if(!cookie.IsExpired() && (bHttp || !cookie.httpOnly) && (bSecure || !cookie.secure))
|
||||
cookies.emplace(cookie);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists)
|
||||
{
|
||||
if(cookie.IsExpired())
|
||||
return DeleteCookieNoLock(cookie);
|
||||
|
||||
CCookieDomainMapI it = m_cookies.find(cookie.domain);
|
||||
|
||||
if(it == m_cookies.end())
|
||||
it = m_cookies.emplace(CCookieDomainMap::value_type(cookie.domain, CCookiePathMap())).first;
|
||||
|
||||
CCookiePathMapI it2 = it->second.find(cookie.path);
|
||||
|
||||
if(it2 == it->second.end())
|
||||
it2 = it->second.emplace(CCookiePathMap::value_type(cookie.path, CCookieSet())).first;
|
||||
|
||||
CCookieSet& cookies = it2->second;
|
||||
CCookieSetI it3 = cookies.find(cookie);
|
||||
|
||||
if(it3 != cookies.end())
|
||||
{
|
||||
if(bOnlyUpdateValueIfExists && !it3->IsExpired() && cookie.IsTransient())
|
||||
{
|
||||
((CCookie*)&*it3)->value = cookie.value;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cookies.erase(*it3);
|
||||
}
|
||||
|
||||
return cookies.emplace(cookie).second;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::DeleteCookieNoLock(const CCookie& cookie)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
CCookieDomainMapI it = m_cookies.find(cookie.domain);
|
||||
if(it != m_cookies.end())
|
||||
{
|
||||
CCookiePathMapI it2 = it->second.find(cookie.path);
|
||||
if(it2 != it->second.end())
|
||||
{
|
||||
CCookieSetI it3 = it2->second.find(cookie);
|
||||
if(it3 != it2->second.end())
|
||||
{
|
||||
it2->second.erase(*it3);
|
||||
isOK = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
CCookieSet* CCookieMgr::GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath)
|
||||
{
|
||||
CCookieSet* pCookieSet = nullptr;
|
||||
CCookieDomainMapI it = m_cookies.find(lpszDomain);
|
||||
|
||||
if(it != m_cookies.end())
|
||||
{
|
||||
CCookiePathMapI it2 = it->second.find(lpszPath);
|
||||
if(it2 != it->second.end())
|
||||
pCookieSet = &(it2->second);
|
||||
}
|
||||
|
||||
return pCookieSet;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath)
|
||||
{
|
||||
int i = 0;
|
||||
char* lpszCtx = nullptr;
|
||||
|
||||
for(; i < 2; i++)
|
||||
{
|
||||
char* lpszToken = strtok_r(lpszBuff, " \n\r", &lpszCtx);
|
||||
|
||||
if(!lpszToken)
|
||||
{
|
||||
::SetLastError(ERROR_BAD_FORMAT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
strDomain = lpszToken;
|
||||
lpszBuff = nullptr;
|
||||
}
|
||||
else
|
||||
strPath = lpszToken;
|
||||
}
|
||||
|
||||
if(!CCookie::AdjustDomain(strDomain))
|
||||
return FALSE;
|
||||
if(!CCookie::AdjustPath(strPath))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie)
|
||||
{
|
||||
cookie.domain = lpszDomain;
|
||||
cookie.path = lpszPath;
|
||||
|
||||
int i = 0;
|
||||
char* lpszCtx = nullptr;
|
||||
|
||||
for(; i < 6; i++)
|
||||
{
|
||||
char* lpszToken = strtok_r(lpszBuff, "\t;\n\r", &lpszCtx);
|
||||
|
||||
if(!lpszToken)
|
||||
{
|
||||
::SetLastError(ERROR_BAD_FORMAT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
cookie.name = lpszToken;
|
||||
lpszBuff = nullptr;
|
||||
}
|
||||
else if(i == 1)
|
||||
cookie.value = lpszToken;
|
||||
else if(i == 2)
|
||||
cookie.expires = atoll(lpszToken);
|
||||
else if(i == 3)
|
||||
cookie.httpOnly = (BOOL)atoi(lpszToken);
|
||||
else if(i == 4)
|
||||
cookie.secure = (BOOL)atoi(lpszToken);
|
||||
else if(i == 5)
|
||||
cookie.sameSite = (CCookie::EnSameSite)atoi(lpszToken);
|
||||
}
|
||||
|
||||
cookie.name.Trim();
|
||||
cookie.value.Trim();
|
||||
|
||||
if(cookie.name.IsEmpty() || cookie.expires <= 0 /*|| cookie.httpOnly < 0 || cookie.secure < 0*/ || cookie.sameSite < CCookie::SS_UNKNOWN || cookie.sameSite > CCookie::SS_NONE)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_DATA);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CCookieMgr::AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull)
|
||||
{
|
||||
if(!bKeepNull || lpszDomain)
|
||||
{
|
||||
strDomain = lpszDomain;
|
||||
|
||||
if(!CCookie::AdjustDomain(strDomain))
|
||||
return FALSE;
|
||||
|
||||
lpszDomain = (LPCSTR)strDomain;
|
||||
}
|
||||
|
||||
if(!bKeepNull || lpszPath)
|
||||
{
|
||||
strPath = lpszPath;
|
||||
|
||||
if(!CCookie::AdjustPath(strPath))
|
||||
return FALSE;
|
||||
|
||||
lpszPath = (LPCSTR)strPath;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
CCookieMgr::CCookieMgr(BOOL bEnableThirdPartyCookie)
|
||||
: m_bEnableThirdPartyCookie(bEnableThirdPartyCookie)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
194
HttpCookie.h
Normal file
194
HttpCookie.h
Normal file
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPTypeDef.h"
|
||||
#include "common/GeneralHelper.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
#define COOKIE_DOMAIN "domain"
|
||||
#define COOKIE_PATH "path"
|
||||
#define COOKIE_EXPIRES "expires"
|
||||
#define COOKIE_MAX_AGE "Max-Age"
|
||||
#define COOKIE_HTTPONLY "HttpOnly"
|
||||
#define COOKIE_SECURE "secure"
|
||||
#define COOKIE_SAMESITE "SameSite"
|
||||
#define COOKIE_SAMESITE_STRICT "Strict"
|
||||
#define COOKIE_SAMESITE_LAX "Lax"
|
||||
#define COOKIE_SAMESITE_NONE "None"
|
||||
#define COOKIE_DEFAULT_PATH "/"
|
||||
#define COOKIE_FIELD_SEP ";"
|
||||
#define COOKIE_DOMAIN_SEP_CHAR '.'
|
||||
#define COOKIE_PATH_SEP_CHAR '/'
|
||||
#define COOKIE_KV_SEP_CHAR '='
|
||||
|
||||
class CCookie
|
||||
{
|
||||
public:
|
||||
enum EnSameSite
|
||||
{
|
||||
SS_UNKNOWN = 0,
|
||||
SS_STRICT = 1,
|
||||
SS_LAX = 2,
|
||||
SS_NONE = 3
|
||||
};
|
||||
|
||||
CStringA name;
|
||||
CStringA value;
|
||||
CStringA domain;
|
||||
CStringA path;
|
||||
__time64_t expires;
|
||||
BOOL httpOnly;
|
||||
BOOL secure;
|
||||
EnSameSite sameSite;
|
||||
|
||||
CCookie(LPCSTR lpszName = nullptr, LPCSTR lpszValue = nullptr, LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN)
|
||||
: name (lpszName)
|
||||
, value (lpszValue)
|
||||
, domain (lpszDomain)
|
||||
, path (lpszPath)
|
||||
, expires (MaxAgeToExpires(iMaxAge))
|
||||
, httpOnly (bHttpOnly)
|
||||
, secure (bSecure)
|
||||
, sameSite (enSameSite)
|
||||
{
|
||||
AdjustDomain(domain);
|
||||
AdjustPath(path);
|
||||
}
|
||||
|
||||
static __time64_t CurrentUTCTime() {return _time64(nullptr);}
|
||||
static __time64_t MaxAgeToExpires(int iMaxAge) {return iMaxAge > 0 ? _time64(nullptr) + iMaxAge : (iMaxAge < 0 ? -1 : 0);}
|
||||
static int ExpiresToMaxAge(__time64_t tmExpires) {if(tmExpires < 0) return -1; __time64_t tmDiff = tmExpires - _time64(nullptr); return (tmDiff > 0 ? (int)tmDiff : 0);}
|
||||
|
||||
static __time64_t GetUTCTime(tm& t, int iSecondOffsetTZ);
|
||||
static void ParseFieldKV(const CStringA& strField, CStringA& strKey, CStringA& strVal, char chSep);
|
||||
static BOOL ParseExpires(LPCSTR lpszExpires, __time64_t& tmExpires);
|
||||
static BOOL AdjustDomain(CStringA& strDomain, LPCSTR lpszDefaultDomain = nullptr);
|
||||
static BOOL AdjustPath(CStringA& strPath, LPCSTR lpszDefaultPath = nullptr);
|
||||
static CStringA MakeExpiresStr(__time64_t tmExpires);
|
||||
static BOOL MakeExpiresStr(char lpszBuff[], int& iBuffLen, __time64_t tmExpires);
|
||||
static CCookie* FromString(const CStringA& strCookie, LPCSTR lpszDefaultDomain = nullptr, LPCSTR lpszDefaultPath = nullptr);
|
||||
static CStringA ToString(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN);
|
||||
static BOOL ToString(char lpszBuff[], int& iBuffLen, LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, EnSameSite enSameSite = SS_UNKNOWN);
|
||||
|
||||
CStringA ToString();
|
||||
BOOL Match(LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure);
|
||||
BOOL IsSameDomain(LPCSTR lpszDomain);
|
||||
|
||||
BOOL IsTransient() const {return expires < 0;}
|
||||
BOOL IsExpired() const {return !IsTransient() && expires <= _time64(nullptr);}
|
||||
BOOL IsValid() const {return !name.IsEmpty() && !domain.IsEmpty() && !path.IsEmpty();}
|
||||
};
|
||||
|
||||
struct ccookie_hash_func
|
||||
{
|
||||
struct hash
|
||||
{
|
||||
size_t operator() (const CCookie& c) const
|
||||
{
|
||||
return hash_value((LPCSTR)(c.name));
|
||||
}
|
||||
};
|
||||
|
||||
struct equal_to
|
||||
{
|
||||
bool operator () (const CCookie& cA, const CCookie& cB) const
|
||||
{
|
||||
return (cA.name == cB.name);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
typedef unordered_set<const CCookie*> CCookiePtrSet;
|
||||
typedef CCookiePtrSet::const_iterator CCookiePtrSetCI;
|
||||
typedef CCookiePtrSet::iterator CCookiePtrSetI;
|
||||
|
||||
typedef unordered_set<CCookie,
|
||||
ccookie_hash_func::hash, ccookie_hash_func::equal_to> CCookieSet;
|
||||
typedef CCookieSet::const_iterator CCookieSetCI;
|
||||
typedef CCookieSet::iterator CCookieSetI;
|
||||
|
||||
typedef unordered_map<CStringA, CCookieSet,
|
||||
cstringa_hash_func::hash, cstringa_hash_func::equal_to> CCookiePathMap;
|
||||
typedef CCookiePathMap::const_iterator CCookiePathMapCI;
|
||||
typedef CCookiePathMap::iterator CCookiePathMapI;
|
||||
|
||||
typedef unordered_map<CStringA, CCookiePathMap,
|
||||
cstringa_nc_hash_func::hash, cstringa_nc_hash_func::equal_to> CCookieDomainMap;
|
||||
typedef CCookieDomainMap::const_iterator CCookieDomainMapCI;
|
||||
typedef CCookieDomainMap::iterator CCookieDomainMapI;
|
||||
|
||||
class CCookieMgr
|
||||
{
|
||||
public:
|
||||
BOOL LoadFromFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE);
|
||||
BOOL SaveToFile(LPCSTR lpszFile, BOOL bKeepExists = TRUE);
|
||||
|
||||
BOOL ClearCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
|
||||
BOOL RemoveExpiredCookies(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
|
||||
|
||||
BOOL GetCookies(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp, BOOL bSecure);
|
||||
BOOL SetCookie(LPCSTR lpszName, LPCSTR lpszValue, LPCSTR lpszDomain, LPCSTR lpszPath, int iMaxAge = -1, BOOL bHttpOnly = FALSE, BOOL bSecure = FALSE, CCookie::EnSameSite enSameSite = CCookie::SS_UNKNOWN, BOOL bOnlyUpdateValueIfExists = TRUE);
|
||||
BOOL SetCookie(const CStringA& strCookie, BOOL bOnlyUpdateValueIfExists = TRUE);
|
||||
BOOL SetCookie(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists = TRUE);
|
||||
BOOL DeleteCookie(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName);
|
||||
BOOL DeleteCookie(const CCookie& cookie);
|
||||
|
||||
private:
|
||||
void ClearDomainCookiesNoLock(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
|
||||
void ClearPathCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath = nullptr);
|
||||
void RemoveExpiredCookiesNoLock(LPCSTR lpszDomain = nullptr, LPCSTR lpszPath = nullptr);
|
||||
void RemoveDomainExpiredCookiesNoLock(CCookiePathMap& paths, LPCSTR lpszPath = nullptr);
|
||||
void RemovePathExpiredCookiesNoLock(CCookieSet& cookies);
|
||||
const CCookie* GetCookieNoLock(LPCSTR lpszDomain, LPCSTR lpszPath, LPCSTR lpszName);
|
||||
const CCookie* GetCookieNoLock(const CCookie& cookie);
|
||||
void MatchCookiesNoLock(CCookieSet& cookies, LPCSTR lpszDomain, LPCSTR lpszPath, BOOL bHttp = TRUE, BOOL bSecure = FALSE);
|
||||
BOOL SetCookieNoLock(const CCookie& cookie, BOOL bOnlyUpdateValueIfExists = TRUE);
|
||||
BOOL DeleteCookieNoLock(const CCookie& cookie);
|
||||
CCookieSet* GetCookieSetNoLock(LPCSTR lpszDomain, LPCSTR lpszPath);
|
||||
|
||||
private:
|
||||
static BOOL LoadDomainAndPath(LPSTR lpszBuff, CStringA& strDomain, CStringA& strPath);
|
||||
static BOOL LoadCookie(LPSTR lpszBuff, LPCSTR lpszDomain, LPCSTR lpszPath, CCookie& cookie);
|
||||
static BOOL AdjustDomainAndPath(LPCSTR& lpszDomain, LPCSTR& lpszPath, CStringA& strDomain, CStringA& strPath, BOOL bKeepNull = TRUE);
|
||||
|
||||
public:
|
||||
CCookieMgr(BOOL bEnableThirdPartyCookie = TRUE);
|
||||
|
||||
void SetEnableThirdPartyCookie (BOOL bEnableThirdPartyCookie = TRUE) {m_bEnableThirdPartyCookie = bEnableThirdPartyCookie;}
|
||||
BOOL IsEnableThirdPartyCookie () {return m_bEnableThirdPartyCookie;}
|
||||
|
||||
private:
|
||||
CSimpleRWLock m_cs;
|
||||
CCookieDomainMap m_cookies;
|
||||
BOOL m_bEnableThirdPartyCookie;
|
||||
};
|
||||
|
||||
extern CCookieMgr g_CookieMgr;
|
||||
|
||||
#endif
|
||||
469
HttpHelper.cpp
Normal file
469
HttpHelper.cpp
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HttpHelper.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
CStringA& GetHttpVersionStr(EnHttpVersion enVersion, CStringA& strResult)
|
||||
{
|
||||
strResult.Format("HTTP/%d.%d", LOBYTE(enVersion), HIBYTE(enVersion));
|
||||
return strResult;
|
||||
}
|
||||
|
||||
CStringA& AdjustRequestPath(BOOL bConnect, LPCSTR lpszPath, CStringA& strPath)
|
||||
{
|
||||
strPath = lpszPath;
|
||||
|
||||
if(strPath.IsEmpty() || (!bConnect && strPath.GetAt(0) != HTTP_PATH_SEPARATOR_CHAR))
|
||||
strPath.Insert(0, HTTP_PATH_SEPARATOR_CHAR);
|
||||
|
||||
return strPath;
|
||||
}
|
||||
|
||||
LPCSTR GetHttpDefaultStatusCodeDesc(EnHttpStatusCode enCode)
|
||||
{
|
||||
switch(enCode)
|
||||
{
|
||||
case HSC_CONTINUE : return "Continue";
|
||||
case HSC_SWITCHING_PROTOCOLS : return "Switching Protocols";
|
||||
case HSC_PROCESSING : return "Processing";
|
||||
case HSC_EARLY_HINTS : return "Early Hints";
|
||||
case HSC_RESPONSE_IS_STALE : return "Response Is Stale";
|
||||
case HSC_REVALIDATION_FAILED : return "Revalidation Failed";
|
||||
case HSC_DISCONNECTED_OPERATION : return "Disconnected Operation";
|
||||
case HSC_HEURISTIC_EXPIRATION : return "Heuristic Expiration";
|
||||
case HSC_MISCELLANEOUS_WARNING : return "Miscellaneous Warning";
|
||||
|
||||
case HSC_OK : return "OK";
|
||||
case HSC_CREATED : return "Created";
|
||||
case HSC_ACCEPTED : return "Accepted";
|
||||
case HSC_NON_AUTHORITATIVE_INFORMATION : return "Non-Authoritative Information";
|
||||
case HSC_NO_CONTENT : return "No Content";
|
||||
case HSC_RESET_CONTENT : return "Reset Content";
|
||||
case HSC_PARTIAL_CONTENT : return "Partial Content";
|
||||
case HSC_MULTI_STATUS : return "Multi-Status";
|
||||
case HSC_ALREADY_REPORTED : return "Already Reported";
|
||||
case HSC_TRANSFORMATION_APPLIED : return "Transformation Applied";
|
||||
case HSC_IM_USED : return "IM Used";
|
||||
case HSC_MISCELLANEOUS_PERSISTENT_WARNING : return "Miscellaneous Persistent Warning";
|
||||
|
||||
case HSC_MULTIPLE_CHOICES : return "Multiple Choices";
|
||||
case HSC_MOVED_PERMANENTLY : return "Moved Permanently";
|
||||
case HSC_MOVED_TEMPORARILY : return "Move temporarily";
|
||||
case HSC_SEE_OTHER : return "See Other";
|
||||
case HSC_NOT_MODIFIED : return "Not Modified";
|
||||
case HSC_USE_PROXY : return "Use Proxy";
|
||||
case HSC_SWITCH_PROXY : return "Switch Proxy";
|
||||
case HSC_TEMPORARY_REDIRECT : return "Temporary Redirect";
|
||||
case HSC_PERMANENT_REDIRECT : return "Permanent Redirect";
|
||||
|
||||
case HSC_BAD_REQUEST : return "Bad Request";
|
||||
case HSC_UNAUTHORIZED : return "Unauthorized";
|
||||
case HSC_PAYMENT_REQUIRED : return "Payment Required";
|
||||
case HSC_FORBIDDEN : return "Forbidden";
|
||||
case HSC_NOT_FOUND : return "Not Found";
|
||||
case HSC_METHOD_NOT_ALLOWED : return "Method Not Allowed";
|
||||
case HSC_NOT_ACCEPTABLE : return "Not Acceptable";
|
||||
case HSC_PROXY_AUTHENTICATION_REQUIRED : return "Proxy Authentication Required";
|
||||
case HSC_REQUEST_TIMEOUT : return "Request Timeout";
|
||||
case HSC_CONFLICT : return "Conflict";
|
||||
case HSC_GONE : return "Gone";
|
||||
case HSC_LENGTH_REQUIRED : return "Length Required";
|
||||
case HSC_PRECONDITION_FAILED : return "Precondition Failed";
|
||||
case HSC_REQUEST_ENTITY_TOO_LARGE : return "Request Entity Too Large";
|
||||
case HSC_REQUEST_URI_TOO_LONG : return "Request-URI Too Long";
|
||||
case HSC_UNSUPPORTED_MEDIA_TYPE : return "Unsupported Media Type";
|
||||
case HSC_REQUESTED_RANGE_NOT_SATISFIABLE : return "Requested Range Not Satisfiable";
|
||||
case HSC_EXPECTATION_FAILED : return "Expectation Failed";
|
||||
case HSC_IM_A_TEAPOT : return "I'm a teapot";
|
||||
case HSC_PAGE_EXPIRED : return "Page Expired";
|
||||
case HSC_ENHANCE_YOUR_CALM : return "Enhance Your Calm";
|
||||
case HSC_MISDIRECTED_REQUEST : return "Misdirected Request";
|
||||
case HSC_UNPROCESSABLE_ENTITY : return "Unprocessable Entity";
|
||||
case HSC_LOCKED : return "Locked";
|
||||
case HSC_FAILED_DEPENDENCY : return "Failed Dependency";
|
||||
case HSC_UNORDERED_COLLECTION : return "Unordered Collection";
|
||||
case HSC_UPGRADE_REQUIRED : return "Upgrade Required";
|
||||
case HSC_PRECONDITION_REQUIRED : return "Precondition Required";
|
||||
case HSC_TOO_MANY_REQUESTS : return "Too Many Requests";
|
||||
case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL : return "Request Header Fields Too Large Unofficial";
|
||||
case HSC_REQUEST_HEADER_FIELDS_TOO_LARGE : return "Request Header Fields Too Large";
|
||||
case HSC_LOGIN_TIMEOUT : return "Login Timeout";
|
||||
case HSC_NO_RESPONSE : return "No Response";
|
||||
case HSC_RETRY_WITH : return "Retry With";
|
||||
case HSC_BLOCKED_BY_PARENTAL_CONTROL : return "Blocked By Parental Control";
|
||||
case HSC_UNAVAILABLE_FOR_LEGAL_REASONS : return "Unavailable For Legal Reasons";
|
||||
case HSC_CLIENT_CLOSED_LOAD_BALANCED_REQUEST : return "Client Closed Load Balance Request";
|
||||
case HSC_INVALID_X_FORWARDED_FOR : return "Invalid X-Forwarded-For";
|
||||
case HSC_REQUEST_HEADER_TOO_LARGE : return "Request Header Too Large";
|
||||
case HSC_SSL_CERTIFICATE_ERROR : return "SSL Certificate Error";
|
||||
case HSC_SSL_CERTIFICATE_REQUIRED : return "SSL Certificate Required";
|
||||
case HSC_HTTP_REQUEST_SENT_TO_HTTPS_PORT : return "HTTP Request Sent To HTTPS Port";
|
||||
case HSC_INVALID_TOKEN : return "Invalid Token";
|
||||
case HSC_CLIENT_CLOSED_REQUEST : return "Client Closed Request";
|
||||
|
||||
case HSC_INTERNAL_SERVER_ERROR : return "Internal Server Error";
|
||||
case HSC_NOT_IMPLEMENTED : return "Not Implemented";
|
||||
case HSC_BAD_GATEWAY : return "Bad Gateway";
|
||||
case HSC_SERVICE_UNAVAILABLE : return "Service Unavailable";
|
||||
case HSC_GATEWAY_TIMEOUT : return "Gateway Timeout";
|
||||
case HSC_HTTP_VERSION_NOT_SUPPORTED : return "HTTP Version Not Supported";
|
||||
case HSC_VARIANT_ALSO_NEGOTIATES : return "Variant Also Negotiates";
|
||||
case HSC_INSUFFICIENT_STORAGE : return "Insufficient Storage";
|
||||
case HSC_LOOP_DETECTED : return "Loop Detected";
|
||||
case HSC_BANDWIDTH_LIMIT_EXCEEDED : return "Bandwidth Limit Exceeded";
|
||||
case HSC_NOT_EXTENDED : return "Not Extended";
|
||||
case HSC_NETWORK_AUTHENTICATION_REQUIRED : return "Network Authentication Required";
|
||||
case HSC_WEB_SERVER_UNKNOWN_ERROR : return "Web Server Unknown Error";
|
||||
case HSC_WEB_SERVER_IS_DOWN : return "Web Server Is Down";
|
||||
case HSC_CONNECTION_TIMEOUT : return "Connection Timeout";
|
||||
case HSC_ORIGIN_IS_UNREACHABLE : return "Origin Is Unreachable";
|
||||
case HSC_TIMEOUT_OCCURED : return "Timeout Occured";
|
||||
case HSC_SSL_HANDSHAKE_FAILED : return "SSL Handshake Failed";
|
||||
case HSC_INVALID_SSL_CERTIFICATE : return "Invalid SSL Certificate";
|
||||
case HSC_RAILGUN_ERROR : return "Railgun Error";
|
||||
case HSC_SITE_IS_OVERLOADED : return "Site Is Overloaded";
|
||||
case HSC_SITE_IS_FROZEN : return "Site Is Frozen";
|
||||
case HSC_IDENTITY_PROVIDER_AUTHENTICATION_ERROR : return "Identity Provider Authentication Error";
|
||||
case HSC_NETWORK_READ_TIMEOUT : return "Network Read Timeout";
|
||||
case HSC_NETWORK_CONNECT_TIMEOUT : return "Network Connect Timeout";
|
||||
|
||||
case HSC_UNPARSEABLE_RESPONSE_HEADERS : return "Unparseable Response Headers";
|
||||
|
||||
default : return "***";
|
||||
}
|
||||
}
|
||||
|
||||
static inline CStringA& AppendHeader(LPCSTR lpszName, LPCSTR lpszValue, CStringA& strValue)
|
||||
{
|
||||
strValue.Append(lpszName);
|
||||
strValue.Append(HTTP_HEADER_SEPARATOR);
|
||||
strValue.Append(lpszValue);
|
||||
strValue.Append(HTTP_CRLF);
|
||||
|
||||
return strValue;
|
||||
}
|
||||
|
||||
void MakeRequestLine(LPCSTR lpszMethod, LPCSTR lpszPath, EnHttpVersion enVersion, CStringA& strValue)
|
||||
{
|
||||
ASSERT(lpszMethod);
|
||||
|
||||
strValue.Format("%s %s HTTP/%d.%d%s", (LPCSTR)(CStringA(lpszMethod).MakeUpper()), lpszPath, LOBYTE(enVersion), HIBYTE(enVersion), HTTP_CRLF);
|
||||
}
|
||||
|
||||
void MakeStatusLine(EnHttpVersion enVersion, USHORT usStatusCode, LPCSTR lpszDesc, CStringA& strValue)
|
||||
{
|
||||
if(!lpszDesc) lpszDesc = ::GetHttpDefaultStatusCodeDesc((EnHttpStatusCode)usStatusCode);
|
||||
strValue.Format("HTTP/%d.%d %d %s%s", LOBYTE(enVersion), HIBYTE(enVersion), usStatusCode, lpszDesc, HTTP_CRLF);
|
||||
}
|
||||
|
||||
void MakeHeaderLines(const THeader lpHeaders[], int iHeaderCount, const TCookieMap* pCookies, int iBodyLength, BOOL bRequest, int iConnFlag, LPCSTR lpszDefaultHost, USHORT usPort, CStringA& strValue)
|
||||
{
|
||||
unordered_set<LPCSTR, str_nc_hash_func::hash, str_nc_hash_func::equal_to> szHeaderNames;
|
||||
|
||||
if(iHeaderCount > 0)
|
||||
{
|
||||
ASSERT(lpHeaders);
|
||||
|
||||
for(int i = 0; i < iHeaderCount; i++)
|
||||
{
|
||||
const THeader& header = lpHeaders[i];
|
||||
|
||||
ASSERT(!::IsStrEmptyA(header.name));
|
||||
|
||||
if(!::IsStrEmptyA(header.name))
|
||||
{
|
||||
szHeaderNames.emplace(header.name);
|
||||
AppendHeader(header.name, header.value, strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( (!bRequest || iBodyLength > 0) &&
|
||||
(szHeaderNames.empty() ||
|
||||
(szHeaderNames.find(HTTP_HEADER_CONTENT_LENGTH) == szHeaderNames.end() &&
|
||||
szHeaderNames.find(HTTP_HEADER_TRANSFER_ENCODING) == szHeaderNames.end())))
|
||||
{
|
||||
char szBodyLength[16];
|
||||
itoa(iBodyLength, szBodyLength, 10);
|
||||
|
||||
AppendHeader(HTTP_HEADER_CONTENT_LENGTH, szBodyLength, strValue);
|
||||
}
|
||||
|
||||
if( (iConnFlag == 0 || iConnFlag == 1) &&
|
||||
(szHeaderNames.empty() ||
|
||||
szHeaderNames.find(HTTP_HEADER_CONNECTION) == szHeaderNames.end() ))
|
||||
{
|
||||
LPCSTR lpszValue = iConnFlag == 0 ? HTTP_CONNECTION_CLOSE_VALUE : HTTP_CONNECTION_KEEPALIVE_VALUE;
|
||||
AppendHeader(HTTP_HEADER_CONNECTION, lpszValue, strValue);
|
||||
}
|
||||
|
||||
if( bRequest && !::IsStrEmptyA(lpszDefaultHost) &&
|
||||
(szHeaderNames.empty() ||
|
||||
(szHeaderNames.find(HTTP_HEADER_HOST) == szHeaderNames.end()) ))
|
||||
{
|
||||
CStringA strHost(lpszDefaultHost);
|
||||
if(usPort != 0) strHost.AppendFormat(":%u", usPort);
|
||||
|
||||
AppendHeader(HTTP_HEADER_HOST, strHost, strValue);
|
||||
}
|
||||
|
||||
szHeaderNames.clear();
|
||||
|
||||
if(pCookies != nullptr)
|
||||
{
|
||||
DWORD dwSize = (DWORD)pCookies->size();
|
||||
|
||||
if(dwSize > 0)
|
||||
{
|
||||
strValue.Append(HTTP_HEADER_COOKIE);
|
||||
strValue.Append(HTTP_HEADER_SEPARATOR);
|
||||
|
||||
DWORD dwIndex = 0;
|
||||
|
||||
for(TCookieMapCI it = pCookies->begin(), end = pCookies->end(); it != end; ++it, ++dwIndex)
|
||||
{
|
||||
strValue.Append(it->first);
|
||||
strValue.AppendChar(COOKIE_KV_SEP_CHAR);
|
||||
strValue.Append(it->second);
|
||||
|
||||
if(dwIndex < dwSize - 1)
|
||||
strValue.Append(HTTP_COOKIE_SEPARATOR);
|
||||
}
|
||||
|
||||
strValue.Append(HTTP_CRLF);
|
||||
}
|
||||
}
|
||||
|
||||
strValue.Append(HTTP_CRLF);
|
||||
}
|
||||
|
||||
void MakeHttpPacket(const CStringA& strHeader, const BYTE* pBody, int iLength, WSABUF szBuffer[2])
|
||||
{
|
||||
ASSERT(pBody != nullptr || iLength == 0);
|
||||
|
||||
szBuffer[0].buf = (LPBYTE)(LPCSTR)strHeader;
|
||||
szBuffer[0].len = strHeader.GetLength();
|
||||
szBuffer[1].buf = (LPBYTE)pBody;
|
||||
szBuffer[1].len = iLength;
|
||||
}
|
||||
|
||||
int MakeChunkPackage(const BYTE* pData, int iLength, LPCSTR lpszExtensions, char szLen[12], WSABUF bufs[5])
|
||||
{
|
||||
ASSERT(iLength == 0 || pData != nullptr);
|
||||
|
||||
int i = 0;
|
||||
|
||||
if(::IsStrEmptyA(lpszExtensions))
|
||||
{
|
||||
sprintf(szLen, "%x" HTTP_CRLF, iLength);
|
||||
|
||||
bufs[i].buf = (LPBYTE)szLen;
|
||||
bufs[i].len = (int)strlen(szLen);
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
LPCSTR lpszSep = lpszExtensions[0] == ';' ? " " : " ;";
|
||||
|
||||
sprintf(szLen, "%x%s", iLength, lpszSep);
|
||||
|
||||
bufs[i].buf = (LPBYTE)szLen;
|
||||
bufs[i].len = (int)strlen(szLen);
|
||||
++i;
|
||||
|
||||
bufs[i].buf = (LPBYTE)lpszExtensions;
|
||||
bufs[i].len = (int)strlen(lpszExtensions);
|
||||
++i;
|
||||
|
||||
bufs[i].buf = (LPBYTE)HTTP_CRLF;
|
||||
bufs[i].len = 2;
|
||||
++i;
|
||||
}
|
||||
|
||||
if(iLength > 0)
|
||||
{
|
||||
bufs[i].buf = (LPBYTE)pData;
|
||||
bufs[i].len = iLength;
|
||||
++i;
|
||||
}
|
||||
|
||||
bufs[i].buf = (LPBYTE)HTTP_CRLF;
|
||||
bufs[i].len = 2;
|
||||
++i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
BOOL MakeWSPacket(BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], BYTE* pData, int iLength, ULONGLONG ullBodyLen, BYTE szHeader[HTTP_MAX_WS_HEADER_LEN], WSABUF szBuffer[2])
|
||||
{
|
||||
ULONGLONG ullLength = (ULONGLONG)iLength;
|
||||
|
||||
ASSERT(pData != nullptr || iLength == 0);
|
||||
ASSERT(ullBodyLen == 0 || ullBodyLen >= ullLength);
|
||||
|
||||
if(ullBodyLen == 0)
|
||||
ullBodyLen = ullLength;
|
||||
else if(ullBodyLen < ullLength)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TBaseWSHeader bh(szHeader, TRUE);
|
||||
|
||||
int iHeaderLen = HTTP_MIN_WS_HEADER_LEN;
|
||||
|
||||
bh.set_fin(bFinal);
|
||||
bh.set_rsv(iReserved);
|
||||
bh.set_code(iOperationCode);
|
||||
bh.set_mask(lpszMask ? TRUE : FALSE);
|
||||
|
||||
if(ullBodyLen < 126)
|
||||
bh.set_len((BYTE)ullBodyLen);
|
||||
else if(ullBodyLen <= 0xFFFF)
|
||||
{
|
||||
bh.set_len(126);
|
||||
bh.set_extlen((USHORT)ullBodyLen);
|
||||
|
||||
iHeaderLen += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
bh.set_len(127);
|
||||
*(ULONGLONG*)(szHeader + HTTP_MIN_WS_HEADER_LEN) = ::HToN64(ullBodyLen);
|
||||
|
||||
iHeaderLen += 8;
|
||||
}
|
||||
|
||||
if(lpszMask)
|
||||
{
|
||||
memcpy(szHeader + iHeaderLen, lpszMask, 4);
|
||||
|
||||
for(int i = 0; i < iLength; i++)
|
||||
pData[i] = pData[i] ^ lpszMask[i & 0x03];
|
||||
|
||||
iHeaderLen += 4;
|
||||
}
|
||||
|
||||
szBuffer[0].buf = szHeader;
|
||||
szBuffer[0].len = iHeaderLen;
|
||||
szBuffer[1].buf = (LPBYTE)pData;
|
||||
szBuffer[1].len = iLength;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ParseUrl(const CStringA& strUrl, BOOL& bHttps, CStringA& strHost, USHORT& usPort, CStringA& strPath)
|
||||
{
|
||||
int iSchemaLength = (int)strlen(HTTP_SCHEMA);
|
||||
|
||||
if(strnicmp(strUrl, HTTP_SCHEMA, iSchemaLength) == 0)
|
||||
bHttps = FALSE;
|
||||
else
|
||||
{
|
||||
iSchemaLength = (int)strlen(HTTPS_SCHEMA);
|
||||
|
||||
if(strnicmp(strUrl, HTTPS_SCHEMA, iSchemaLength) == 0)
|
||||
bHttps = TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CStringA strFullHost;
|
||||
int i = strUrl.Find(HTTP_PATH_SEPARATOR_CHAR, iSchemaLength);
|
||||
|
||||
if(i > 0)
|
||||
{
|
||||
strFullHost = strUrl.Mid(iSchemaLength, i - iSchemaLength);
|
||||
strPath = strUrl.Mid(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
strFullHost = strUrl.Mid(iSchemaLength);
|
||||
strPath = HTTP_PATH_SEPARATOR;
|
||||
}
|
||||
|
||||
if(strFullHost.IsEmpty())
|
||||
return FALSE;
|
||||
|
||||
CStringA strPort;
|
||||
|
||||
char c = strFullHost.GetAt(0);
|
||||
|
||||
if(!::isalnum(c))
|
||||
{
|
||||
if(c != IPV6_ADDR_BEGIN_CHAR)
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
i = strFullHost.ReverseFind(IPV6_ADDR_END_CHAR);
|
||||
|
||||
if(i < 0)
|
||||
return FALSE;
|
||||
else
|
||||
{
|
||||
if(strFullHost.GetLength() > i + 1)
|
||||
{
|
||||
if(strFullHost.GetAt(i + 1) != PORT_SEPARATOR_CHAR)
|
||||
return FALSE;
|
||||
|
||||
strPort = strFullHost.Mid(i + 2);
|
||||
|
||||
if(strPort.IsEmpty())
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
strHost = strFullHost.Mid(1, i - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i = strFullHost.Find(PORT_SEPARATOR_CHAR);
|
||||
|
||||
if(i < 0)
|
||||
strHost = strFullHost;
|
||||
else
|
||||
{
|
||||
strPort = strFullHost.Mid(i + 1);
|
||||
|
||||
if(strPort.IsEmpty())
|
||||
return FALSE;
|
||||
|
||||
strHost = strFullHost.Mid(0, i);
|
||||
}
|
||||
}
|
||||
|
||||
if(strPort.IsEmpty())
|
||||
usPort = bHttps ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT;
|
||||
else
|
||||
usPort = (USHORT)::atoi(strPort);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
1385
HttpHelper.h
Normal file
1385
HttpHelper.h
Normal file
File diff suppressed because it is too large
Load Diff
615
HttpServer.cpp
Normal file
615
HttpServer.cpp
Normal file
@@ -0,0 +1,615 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "HttpServer.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::Start(LPCTSTR lpszBindAddress, USHORT usPort)
|
||||
{
|
||||
BOOL isOK = __super::Start(lpszBindAddress, usPort);
|
||||
|
||||
if(isOK) VERIFY(m_thCleaner.Start(this, &CHttpServerT::CleanerThreadProc));
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::CheckParams()
|
||||
{
|
||||
if ((m_enLocalVersion != HV_1_1 && m_enLocalVersion != HV_1_0) ||
|
||||
(m_dwReleaseDelay < MIN_HTTP_RELEASE_DELAY || m_dwReleaseDelay > MAX_HTTP_RELEASE_DELAY))
|
||||
{
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpServerT<T, default_port>::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_objPool.SetHttpObjLockTime(GetFreeSocketObjLockTime());
|
||||
m_objPool.SetHttpObjPoolSize(GetFreeSocketObjPool());
|
||||
m_objPool.SetHttpObjPoolHold(GetFreeSocketObjHold());
|
||||
|
||||
m_objPool.Prepare();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendResponse(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc, const THeader lpHeaders[], int iHeaderCount, const BYTE* pData, int iLength)
|
||||
{
|
||||
WSABUF szBuffer[2];
|
||||
CStringA strHeader;
|
||||
|
||||
::MakeStatusLine(m_enLocalVersion, usStatusCode, lpszDesc, strHeader);
|
||||
::MakeHeaderLines(lpHeaders, iHeaderCount, nullptr, iLength, FALSE, IsKeepAlive(dwConnID), nullptr, 0, strHeader);
|
||||
::MakeHttpPacket(strHeader, pData, iLength, szBuffer);
|
||||
|
||||
return SendPackets(dwConnID, szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode, LPCSTR lpszDesc, const THeader lpHeaders[], int iHeaderCount)
|
||||
{
|
||||
CFile file;
|
||||
CFileMapping fmap;
|
||||
|
||||
HRESULT hr = ::ReadSmallFile(CA2T(lpszFileName), file, fmap);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
::SetLastError(hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return SendResponse(dwConnID, usStatusCode, lpszDesc, lpHeaders, iHeaderCount, (BYTE*)fmap, (int)fmap.Size());
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendChunkData(CONNID dwConnID, const BYTE* pData, int iLength, LPCSTR lpszExtensions)
|
||||
{
|
||||
char szLen[12];
|
||||
WSABUF bufs[5];
|
||||
|
||||
int iCount = MakeChunkPackage(pData, iLength, lpszExtensions, szLen, bufs);
|
||||
|
||||
return SendPackets(dwConnID, bufs, iCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::Release(CONNID dwConnID)
|
||||
{
|
||||
if(!HasStarted())
|
||||
return FALSE;
|
||||
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr || pHttpObj->HasReleased())
|
||||
return FALSE;
|
||||
|
||||
pHttpObj->Release();
|
||||
|
||||
m_lsDyingQueue.PushBack(TDyingConnection::Construct(dwConnID));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData, int iLength, ULONGLONG ullBodyLen)
|
||||
{
|
||||
WSABUF szBuffer[2];
|
||||
BYTE szHeader[HTTP_MAX_WS_HEADER_LEN];
|
||||
|
||||
if(!::MakeWSPacket(bFinal, iReserved, iOperationCode, nullptr, (BYTE*)pData, iLength, ullBodyLen, szHeader, szBuffer))
|
||||
return FALSE;
|
||||
|
||||
return SendPackets(dwConnID, szBuffer, 2);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> UINT CHttpServerT<T, default_port>::CleanerThreadProc(PVOID pv)
|
||||
{
|
||||
TRACE("---------------> Connection Cleaner Thread 0x%08X started <---------------", SELF_THREAD_ID);
|
||||
|
||||
pollfd pfd = {m_evCleaner.GetFD(), POLLIN};
|
||||
DWORD dwInterval = MAX(MIN_HTTP_RELEASE_CHECK_INTERVAL, (m_dwReleaseDelay - MIN_HTTP_RELEASE_DELAY / 2));
|
||||
|
||||
while(HasStarted())
|
||||
{
|
||||
int rs = (int)::PollForSingleObject(pfd, dwInterval);
|
||||
ASSERT(rs >= TIMEOUT);
|
||||
|
||||
if(rs < TIMEOUT)
|
||||
ERROR_ABORT();
|
||||
|
||||
if(rs == TIMEOUT)
|
||||
KillDyingConnection();
|
||||
else if(rs == 1)
|
||||
{
|
||||
m_evCleaner.Reset();
|
||||
goto END_DETECTOR;
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
END_DETECTOR:
|
||||
|
||||
ReleaseDyingConnection();
|
||||
VERIFY(!HasStarted());
|
||||
|
||||
TRACE("---------------> Connection Cleaner Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpServerT<T, default_port>::KillDyingConnection()
|
||||
{
|
||||
TDyingConnection* pDyingConn = nullptr;
|
||||
TDyingConnection* pFirstDyingConn = nullptr;
|
||||
DWORD now = ::TimeGetTime();
|
||||
|
||||
while(m_lsDyingQueue.UnsafePeekFront(&pDyingConn))
|
||||
{
|
||||
if((int)(now - pDyingConn->killTime) < (int)m_dwReleaseDelay)
|
||||
break;
|
||||
|
||||
BOOL bDisconnect = TRUE;
|
||||
BOOL bDestruct = TRUE;
|
||||
|
||||
VERIFY(m_lsDyingQueue.UnsafePopFront(&pDyingConn));
|
||||
|
||||
int iPending;
|
||||
if(!GetPendingDataLength(pDyingConn->connID, iPending))
|
||||
bDisconnect = FALSE;
|
||||
else if(iPending > 0)
|
||||
{
|
||||
bDisconnect = FALSE;
|
||||
bDestruct = FALSE;
|
||||
}
|
||||
|
||||
if(bDisconnect)
|
||||
Disconnect(pDyingConn->connID, TRUE);
|
||||
|
||||
if(bDestruct)
|
||||
{
|
||||
TDyingConnection::Destruct(pDyingConn);
|
||||
|
||||
if(pFirstDyingConn == pDyingConn)
|
||||
pFirstDyingConn = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lsDyingQueue.PushBack(pDyingConn);
|
||||
|
||||
if(pFirstDyingConn == nullptr)
|
||||
pFirstDyingConn = pDyingConn;
|
||||
else if(pFirstDyingConn == pDyingConn)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpServerT<T, default_port>::ReleaseDyingConnection()
|
||||
{
|
||||
TDyingConnection* pDyingConn = nullptr;
|
||||
|
||||
while(m_lsDyingQueue.UnsafePopFront(&pDyingConn))
|
||||
TDyingConnection::Destruct(pDyingConn);
|
||||
|
||||
VERIFY(m_lsDyingQueue.IsEmpty());
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::FireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
return m_bHttpAutoStart ? __super::FireAccept(pSocketObj) : __super::DoFireAccept(pSocketObj);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = DoStartHttp(pSocketObj);
|
||||
EnHandleResult result = __super::DoFireAccept(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
{
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireHandShake(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
{
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return DoFireSuperReceive(pSocketObj, pData, iLength);
|
||||
else
|
||||
{
|
||||
if(pHttpObj->HasReleased())
|
||||
return HR_ERROR;
|
||||
|
||||
return pHttpObj->Execute(pData, iLength);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
{
|
||||
m_objPool.PutFreeHttpObj(pHttpObj);
|
||||
SetConnectionReserved(pSocketObj, nullptr);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHandleResult CHttpServerT<T, default_port>::DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_objPool.Clear();
|
||||
WaitForCleanerThreadEnd();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpServerT<T, default_port>::ReleaseGCSocketObj(BOOL bForce)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_objPool.ReleaseGCHttpObj(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> void CHttpServerT<T, default_port>::WaitForCleanerThreadEnd()
|
||||
{
|
||||
if(m_thCleaner.IsRunning())
|
||||
{
|
||||
m_evCleaner.Set();
|
||||
m_thCleaner.Join();
|
||||
m_evCleaner.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::IsUpgrade(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->IsUpgrade();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::IsKeepAlive(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->IsKeepAlive();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetVersion(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetVersion();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetHost(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetHost();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> ULONGLONG CHttpServerT<T, default_port>::GetContentLength(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetContentLength();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetContentType(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetContentType();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetContentEncoding(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetContentEncoding();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetTransferEncoding(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetTransferEncoding();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> EnHttpUpgradeType CHttpServerT<T, default_port>::GetUpgradeType(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return HUT_NONE;
|
||||
|
||||
return pHttpObj->GetUpgradeType();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetParseErrorCode(lpszErrorDesc);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetHeader(lpszName, lpszValue);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetHeaders(lpszName, lpszValue, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllHeaders(lpHeaders, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllHeaderNames(lpszName, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetCookie(lpszName, lpszValue);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetAllCookies(lpCookies, dwCount);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> USHORT CHttpServerT<T, default_port>::GetUrlFieldSet(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return 0;
|
||||
|
||||
return pHttpObj->GetUrlFieldSet();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetUrlField(CONNID dwConnID, EnHttpUrlField enField)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetUrlField(enField);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> LPCSTR CHttpServerT<T, default_port>::GetMethod(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return pHttpObj->GetMethod();
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain)
|
||||
{
|
||||
THttpObj* pHttpObj = FindHttpObj(dwConnID);
|
||||
|
||||
if(pHttpObj == nullptr)
|
||||
return FALSE;
|
||||
|
||||
return pHttpObj->GetWSMessageState(lpbFinal, lpiReserved, lpiOperationCode, lpszMask, lpullBodyLen, lpullBodyRemain);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> inline typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::FindHttpObj(CONNID dwConnID)
|
||||
{
|
||||
THttpObj* pHttpObj = nullptr;
|
||||
GetConnectionReserved(dwConnID, (PVOID*)&pHttpObj);
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> inline typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::FindHttpObj(TSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pHttpObj);
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::StartHttp(CONNID dwConnID)
|
||||
{
|
||||
if(IsHttpAutoStart())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartHttp(pSocketObj);
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> BOOL CHttpServerT<T, default_port>::StartHttp(TSocketObj* pSocketObj)
|
||||
{
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock locallock(pSocketObj->csSend);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
THttpObj* pHttpObj = FindHttpObj(pSocketObj);
|
||||
|
||||
if(pHttpObj != nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoStartHttp(pSocketObj);
|
||||
|
||||
if(!IsSecure())
|
||||
FireHandShake(pSocketObj);
|
||||
else
|
||||
{
|
||||
#ifdef _SSL_SUPPORT
|
||||
if(IsSSLAutoHandShake())
|
||||
StartSSLHandShake(pSocketObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
template<class T, USHORT default_port> typename CHttpServerT<T, default_port>::THttpObj* CHttpServerT<T, default_port>::DoStartHttp(TSocketObj* pSocketObj)
|
||||
{
|
||||
THttpObj* pHttpObj = m_objPool.PickFreeHttpObj(this, pSocketObj);
|
||||
VERIFY(SetConnectionReserved(pSocketObj, pHttpObj));
|
||||
|
||||
return pHttpObj;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
template class CHttpServerT<CTcpServer, HTTP_DEFAULT_PORT>;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLServer.h"
|
||||
|
||||
template class CHttpServerT<CSSLServer, HTTPS_DEFAULT_PORT>;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
223
HttpServer.h
Normal file
223
HttpServer.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpServer.h"
|
||||
#include "HttpHelper.h"
|
||||
#include "common/Thread.h"
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class T, USHORT default_port> class CHttpServerT : public IComplexHttpResponder, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::SetLastError;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
using __super::SendPackets;
|
||||
using __super::Disconnect;
|
||||
using __super::HasStarted;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
using __super::GetPendingDataLength;
|
||||
|
||||
using __super::IsSecure;
|
||||
using __super::FireHandShake;
|
||||
using __super::FindSocketObj;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
using __super::StartSSLHandShake;
|
||||
using __super::IsSSLAutoHandShake;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
using CCleanThread = CThread<CHttpServerT, VOID, UINT>;
|
||||
using CHttpObjPool = CHttpObjPoolT<TRUE, CHttpServerT, TSocketObj>;
|
||||
using THttpObj = THttpObjT<CHttpServerT, TSocketObj>;
|
||||
|
||||
friend typename CHttpServerT::CCleanThread;
|
||||
friend typename CHttpServerT::THttpObj;
|
||||
|
||||
public:
|
||||
|
||||
virtual BOOL Start(LPCTSTR lpszBindAddress, USHORT usPort);
|
||||
|
||||
virtual BOOL SendResponse(CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0, const BYTE* pData = nullptr, int iLength = 0);
|
||||
virtual BOOL SendLocalFile(CONNID dwConnID, LPCSTR lpszFileName, USHORT usStatusCode = HSC_OK, LPCSTR lpszDesc = nullptr, const THeader lpHeaders[] = nullptr, int iHeaderCount = 0);
|
||||
virtual BOOL SendChunkData(CONNID dwConnID, const BYTE* pData = nullptr, int iLength = 0, LPCSTR lpszExtensions = nullptr);
|
||||
|
||||
virtual BOOL Release(CONNID dwConnID);
|
||||
|
||||
virtual BOOL SendWSMessage(CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE* pData = nullptr, int iLength = 0, ULONGLONG ullBodyLen = 0);
|
||||
|
||||
virtual BOOL StartHttp(CONNID dwConnID);
|
||||
|
||||
public:
|
||||
|
||||
virtual void SetHttpAutoStart(BOOL bAutoStart) {ENSURE_HAS_STOPPED(); m_bHttpAutoStart = bAutoStart;}
|
||||
virtual void SetLocalVersion(EnHttpVersion enLocalVersion) {ENSURE_HAS_STOPPED(); m_enLocalVersion = enLocalVersion;}
|
||||
virtual void SetReleaseDelay(DWORD dwReleaseDelay) {ENSURE_HAS_STOPPED(); m_dwReleaseDelay = dwReleaseDelay;}
|
||||
|
||||
virtual BOOL IsHttpAutoStart () {return m_bHttpAutoStart;}
|
||||
virtual EnHttpVersion GetLocalVersion () {return m_enLocalVersion;}
|
||||
virtual DWORD GetReleaseDelay () {return m_dwReleaseDelay;}
|
||||
|
||||
virtual BOOL IsUpgrade(CONNID dwConnID);
|
||||
virtual BOOL IsKeepAlive(CONNID dwConnID);
|
||||
virtual USHORT GetVersion(CONNID dwConnID);
|
||||
virtual LPCSTR GetHost(CONNID dwConnID);
|
||||
virtual ULONGLONG GetContentLength(CONNID dwConnID);
|
||||
virtual LPCSTR GetContentType(CONNID dwConnID);
|
||||
virtual LPCSTR GetContentEncoding(CONNID dwConnID);
|
||||
virtual LPCSTR GetTransferEncoding(CONNID dwConnID);
|
||||
virtual EnHttpUpgradeType GetUpgradeType(CONNID dwConnID);
|
||||
virtual USHORT GetParseErrorCode(CONNID dwConnID, LPCSTR* lpszErrorDesc = nullptr);
|
||||
|
||||
virtual BOOL GetHeader(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
|
||||
virtual BOOL GetHeaders(CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue[], DWORD& dwCount);
|
||||
virtual BOOL GetAllHeaders(CONNID dwConnID, THeader lpHeaders[], DWORD& dwCount);
|
||||
virtual BOOL GetAllHeaderNames(CONNID dwConnID, LPCSTR lpszName[], DWORD& dwCount);
|
||||
|
||||
virtual BOOL GetCookie(CONNID dwConnID, LPCSTR lpszName, LPCSTR* lpszValue);
|
||||
virtual BOOL GetAllCookies(CONNID dwConnID, TCookie lpCookies[], DWORD& dwCount);
|
||||
|
||||
virtual USHORT GetUrlFieldSet(CONNID dwConnID);
|
||||
virtual LPCSTR GetUrlField(CONNID dwConnID, EnHttpUrlField enField);
|
||||
virtual LPCSTR GetMethod(CONNID dwConnID);
|
||||
|
||||
virtual BOOL GetWSMessageState(CONNID dwConnID, BOOL* lpbFinal, BYTE* lpiReserved, BYTE* lpiOperationCode, LPCBYTE* lpszMask, ULONGLONG* lpullBodyLen, ULONGLONG* lpullBodyRemain);
|
||||
|
||||
private:
|
||||
BOOL StartHttp(TSocketObj* pSocketObj);
|
||||
THttpObj* DoStartHttp(TSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual EnHandleResult FireAccept(TSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj);
|
||||
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
virtual EnHandleResult DoFireShutdown();
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
EnHandleResult DoFireSuperReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return __super::DoFireReceive(pSocketObj, pData, iLength);}
|
||||
|
||||
EnHttpParseResult FireMessageBegin(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnMessageBegin((IHttpServer*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireRequestLine(TSocketObj* pSocketObj, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_pListener->OnRequestLine((IHttpServer*)this, pSocketObj->connID, lpszMethod, lpszUrl);}
|
||||
EnHttpParseResult FireStatusLine(TSocketObj* pSocketObj, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_pListener->OnStatusLine((IHttpServer*)this, pSocketObj->connID, usStatusCode, lpszDesc);}
|
||||
EnHttpParseResult FireHeader(TSocketObj* pSocketObj, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_pListener->OnHeader((IHttpServer*)this, pSocketObj->connID, lpszName, lpszValue);}
|
||||
EnHttpParseResult FireHeadersComplete(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnHeadersComplete((IHttpServer*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireBody(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnBody((IHttpServer*)this, pSocketObj->connID, pData, iLength);}
|
||||
EnHttpParseResult FireChunkHeader(TSocketObj* pSocketObj, int iLength)
|
||||
{return m_pListener->OnChunkHeader((IHttpServer*)this, pSocketObj->connID, iLength);}
|
||||
EnHttpParseResult FireChunkComplete(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnChunkComplete((IHttpServer*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireMessageComplete(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnMessageComplete((IHttpServer*)this, pSocketObj->connID);}
|
||||
EnHttpParseResult FireUpgrade(TSocketObj* pSocketObj, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_pListener->OnUpgrade((IHttpServer*)this, pSocketObj->connID, enUpgradeType);}
|
||||
EnHttpParseResult FireParseError(TSocketObj* pSocketObj, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_pListener->OnParseError((IHttpServer*)this, pSocketObj->connID, iErrorCode, lpszErrorDesc);}
|
||||
|
||||
EnHandleResult FireWSMessageHeader(TSocketObj* pSocketObj, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_pListener->OnWSMessageHeader((IHttpServer*)this, pSocketObj->connID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
EnHandleResult FireWSMessageBody(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnWSMessageBody((IHttpServer*)this, pSocketObj->connID, pData, iLength);}
|
||||
EnHandleResult FireWSMessageComplete(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnWSMessageComplete((IHttpServer*)this, pSocketObj->connID);}
|
||||
|
||||
inline THttpObj* FindHttpObj(CONNID dwConnID);
|
||||
inline THttpObj* FindHttpObj(TSocketObj* pSocketObj);
|
||||
|
||||
CCookieMgr* GetCookieMgr() {return nullptr;}
|
||||
LPCSTR GetRemoteDomain(TSocketObj* pSocketObj) {return nullptr;}
|
||||
|
||||
private:
|
||||
void KillDyingConnection();
|
||||
void ReleaseDyingConnection();
|
||||
|
||||
UINT CleanerThreadProc(PVOID pv = nullptr);
|
||||
void WaitForCleanerThreadEnd();
|
||||
|
||||
public:
|
||||
CHttpServerT(IHttpServerListener* pListener)
|
||||
: T (pListener)
|
||||
, m_pListener (pListener)
|
||||
, m_bHttpAutoStart (TRUE)
|
||||
, m_enLocalVersion (DEFAULT_HTTP_VERSION)
|
||||
, m_dwReleaseDelay (DEFAULT_HTTP_RELEASE_DELAY)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CHttpServerT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
IHttpServerListener* m_pListener;
|
||||
|
||||
CEvt m_evCleaner;
|
||||
CCleanThread m_thCleaner;
|
||||
|
||||
EnHttpVersion m_enLocalVersion;
|
||||
DWORD m_dwReleaseDelay;
|
||||
|
||||
BOOL m_bHttpAutoStart;
|
||||
|
||||
CCASQueue<TDyingConnection> m_lsDyingQueue;
|
||||
|
||||
CHttpObjPool m_objPool;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
typedef CHttpServerT<CTcpServer, HTTP_DEFAULT_PORT> CHttpServer;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLServer.h"
|
||||
|
||||
typedef CHttpServerT<CSSLServer, HTTPS_DEFAULT_PORT> CHttpsServer;
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
132
InternalDef.h
Normal file
132
InternalDef.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPTypeDef.h"
|
||||
|
||||
/************************************************************************
|
||||
名称:全局常量
|
||||
描述:声明组件的公共全局常量
|
||||
************************************************************************/
|
||||
|
||||
/* Socket 缓冲区最小值 */
|
||||
#define MIN_SOCKET_BUFFER_SIZE 8
|
||||
/* 小文件最大字节数 */
|
||||
#define MAX_SMALL_FILE_SIZE 0x3FFFFF
|
||||
/* 最大连接时长 */
|
||||
#define MAX_CONNECTION_PERIOD (MAXINT / 2)
|
||||
/* 处理接收事件时最大读取次数 */
|
||||
#define MAX_CONTINUE_READS 100
|
||||
/* 处理发送事件时最大写入次数 */
|
||||
#define MAX_CONTINUE_WRITES 100
|
||||
|
||||
/* 默认工作队列等待的最大描述符事件数量 */
|
||||
#define DEFAULT_WORKER_MAX_EVENT_COUNT CIODispatcher::DEF_WORKER_MAX_EVENTS
|
||||
|
||||
/* Server/Agent 最大连接数 */
|
||||
#define MAX_CONNECTION_COUNT (5 * 1000 * 1000)
|
||||
/* Server/Agent 默认最大连接数 */
|
||||
#define DEFAULT_CONNECTION_COUNT 10000
|
||||
/* Server/Agent 默认 Socket 对象缓存锁定时间 */
|
||||
#define DEFAULT_FREE_SOCKETOBJ_LOCK_TIME DEFAULT_OBJECT_CACHE_LOCK_TIME
|
||||
/* Server/Agent 默认 Socket 缓存池大小 */
|
||||
#define DEFAULT_FREE_SOCKETOBJ_POOL DEFAULT_OBJECT_CACHE_POOL_SIZE
|
||||
/* Server/Agent 默认 Socket 缓存池回收阀值 */
|
||||
#define DEFAULT_FREE_SOCKETOBJ_HOLD DEFAULT_OBJECT_CACHE_POOL_HOLD
|
||||
/* Server/Agent 默认内存块缓存池大小 */
|
||||
#define DEFAULT_FREE_BUFFEROBJ_POOL DEFAULT_BUFFER_CACHE_POOL_SIZE
|
||||
/* Server/Agent 默认内存块缓存池回收阀值 */
|
||||
#define DEFAULT_FREE_BUFFEROBJ_HOLD DEFAULT_BUFFER_CACHE_POOL_HOLD
|
||||
/* Client 默认内存块缓存池大小 */
|
||||
#define DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE 60
|
||||
/* Client 默认内存块缓存池回收阀值 */
|
||||
#define DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD 60
|
||||
/* Client/Agent 默认同步连接超时时间 */
|
||||
#define DEFAULT_SYNC_CONNECT_TIMEOUT 10000
|
||||
/* IPv4 默认绑定地址 */
|
||||
#define DEFAULT_IPV4_BIND_ADDRESS _T("0.0.0.0")
|
||||
/* IPv6 默认绑定地址 */
|
||||
#define DEFAULT_IPV6_BIND_ADDRESS _T("::")
|
||||
/* IPv4 广播地址 */
|
||||
#define DEFAULT_IPV4_BROAD_CAST_ADDRESS _T("255.255.255.255")
|
||||
|
||||
/* TCP 默认通信数据缓冲区大小 */
|
||||
#define DEFAULT_TCP_SOCKET_BUFFER_SIZE DEFAULT_BUFFER_CACHE_CAPACITY
|
||||
/* TCP 默认心跳包间隔 */
|
||||
#define DEFALUT_TCP_KEEPALIVE_TIME (60 * 1000)
|
||||
/* TCP 默认心跳确认包检测间隔 */
|
||||
#define DEFALUT_TCP_KEEPALIVE_INTERVAL (20 * 1000)
|
||||
/* TCP Server 默认 Listen 队列大小 */
|
||||
#define DEFAULT_TCP_SERVER_SOCKET_LISTEN_QUEUE SOMAXCONN
|
||||
|
||||
/* UDP 最大数据报文最大长度 */
|
||||
#define MAXIMUM_UDP_MAX_DATAGRAM_SIZE (16 * DEFAULT_BUFFER_CACHE_CAPACITY)
|
||||
/* UDP 默认数据报文最大长度 */
|
||||
#define DEFAULT_UDP_MAX_DATAGRAM_SIZE 1432
|
||||
/* UDP 默认 Receive 预投递数量 */
|
||||
#define DEFAULT_UDP_POST_RECEIVE_COUNT DEFAULT_WORKER_MAX_EVENT_COUNT
|
||||
/* UDP 默认监测包尝试次数 */
|
||||
#define DEFAULT_UDP_DETECT_ATTEMPTS 3
|
||||
/* UDP 默认监测包发送间隔 */
|
||||
#define DEFAULT_UDP_DETECT_INTERVAL (60 * 1000)
|
||||
|
||||
/* TCP Pack 包长度位数 */
|
||||
#define TCP_PACK_LENGTH_BITS 22
|
||||
/* TCP Pack 包长度掩码 */
|
||||
#define TCP_PACK_LENGTH_MASK 0x3FFFFF
|
||||
/* TCP Pack 包最大长度硬限制 */
|
||||
#define TCP_PACK_MAX_SIZE_LIMIT 0x3FFFFF
|
||||
/* TCP Pack 包默认最大长度 */
|
||||
#define TCP_PACK_DEFAULT_MAX_SIZE 0x040000
|
||||
/* TCP Pack 包头标识值硬限制 */
|
||||
#define TCP_PACK_HEADER_FLAG_LIMIT 0x0003FF
|
||||
/* TCP Pack 包头默认标识值 */
|
||||
#define TCP_PACK_DEFAULT_HEADER_FLAG 0x000000
|
||||
|
||||
/* 默认压缩/解压数据缓冲器长度 */
|
||||
#define DEFAULT_COMPRESS_BUFFER_SIZE (16 * 1024)
|
||||
|
||||
/* 垃圾回收检查间隔(毫秒) */
|
||||
#define GC_CHECK_INTERVAL (15 * 1000)
|
||||
|
||||
#define HOST_SEPARATOR_CHAR '^'
|
||||
#define PORT_SEPARATOR_CHAR ':'
|
||||
#define IPV6_ADDR_BEGIN_CHAR '['
|
||||
#define IPV6_ADDR_END_CHAR ']'
|
||||
#define IPV4_ADDR_SEPARATOR_CHAR '.'
|
||||
#define IPV6_ADDR_SEPARATOR_CHAR ':'
|
||||
#define IPV6_ZONE_INDEX_CHAR '%'
|
||||
|
||||
#define CST_CONNECTING (-1)
|
||||
#define INVALID_SOCKET INVALID_FD
|
||||
#define SOCKET_ERROR HAS_ERROR
|
||||
#define WSASetLastError SetLastError
|
||||
#define WSAGetLastError GetLastError
|
||||
#define InetPton inet_pton
|
||||
#define InetNtop inet_ntop
|
||||
#define closesocket close
|
||||
|
||||
#define ENSURE_STOP() {if(GetState() != SS_STOPPED) {Stop();} Wait();}
|
||||
#define ENSURE_HAS_STOPPED() {ASSERT(GetState() == SS_STOPPED); if(GetState() != SS_STOPPED) return;}
|
||||
#define WAIT_FOR_STOP_PREDICATE [this]() {return GetState() == SS_STOPPED;}
|
||||
51
MiscHelper.cpp
Normal file
51
MiscHelper.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "MiscHelper.h"
|
||||
|
||||
BOOL AddPackHeader(const WSABUF * pBuffers, int iCount, unique_ptr<WSABUF[]>& buffers, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, DWORD& dwHeader)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
DWORD iLength = 0;
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
const WSABUF& buf = pBuffers[i];
|
||||
buffers[i + 1] = buf;
|
||||
iLength += buf.len;
|
||||
}
|
||||
|
||||
if(iLength == 0 || iLength > dwMaxPackSize)
|
||||
{
|
||||
::SetLastError(ERROR_BAD_LENGTH);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
dwHeader = ::HToLE32((usPackHeaderFlag << TCP_PACK_LENGTH_BITS) | iLength);
|
||||
|
||||
buffers[0].len = sizeof(dwHeader);
|
||||
buffers[0].buf = (LPBYTE)&dwHeader;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
157
MiscHelper.h
Normal file
157
MiscHelper.h
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
|
||||
/* Pack Data Info */
|
||||
template<typename B = void> struct TPackInfo
|
||||
{
|
||||
bool header;
|
||||
DWORD length;
|
||||
B* pBuffer;
|
||||
|
||||
static TPackInfo* Construct(B* pbuf = nullptr, bool head = true, DWORD len = sizeof(DWORD))
|
||||
{
|
||||
return new TPackInfo(pbuf, head, len);
|
||||
}
|
||||
|
||||
static void Destruct(TPackInfo* pPackInfo)
|
||||
{
|
||||
if(pPackInfo)
|
||||
delete pPackInfo;
|
||||
}
|
||||
|
||||
TPackInfo(B* pbuf = nullptr, bool head = true, DWORD len = sizeof(DWORD))
|
||||
: header(head), length(len), pBuffer(pbuf)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
header = true;
|
||||
length = sizeof(DWORD);
|
||||
pBuffer = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
typedef TPackInfo<TBuffer> TBufferPackInfo;
|
||||
|
||||
BOOL AddPackHeader(const WSABUF * pBuffers, int iCount, unique_ptr<WSABUF[]>& buffers, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, DWORD& dwHeader);
|
||||
|
||||
template<class B> EnFetchResult FetchBuffer(B* pBuffer, BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(pBuffer != nullptr);
|
||||
ASSERT(pData != nullptr && iLength > 0);
|
||||
|
||||
EnFetchResult result = FR_OK;
|
||||
|
||||
if(pBuffer->Length() >= iLength)
|
||||
pBuffer->Fetch(pData, iLength);
|
||||
else
|
||||
result = FR_LENGTH_TOO_LONG;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class B> EnFetchResult PeekBuffer(B* pBuffer, BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(pBuffer != nullptr);
|
||||
ASSERT(pData != nullptr && iLength > 0);
|
||||
|
||||
EnFetchResult result = FR_OK;
|
||||
|
||||
if(pBuffer->Length() >= iLength)
|
||||
pBuffer->Peek(pData, iLength);
|
||||
else
|
||||
result = FR_LENGTH_TOO_LONG;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, class B, class S> EnHandleResult ParsePack(T* pThis, TPackInfo<B>* pInfo, B* pBuffer, S* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag)
|
||||
{
|
||||
EnHandleResult rs = HR_OK;
|
||||
|
||||
int required = pInfo->length;
|
||||
int remain = pBuffer->Length();
|
||||
|
||||
while(remain >= required)
|
||||
{
|
||||
if(pSocket->IsPaused())
|
||||
break;
|
||||
|
||||
remain -= required;
|
||||
CBufferPtr buffer(required);
|
||||
|
||||
pBuffer->Fetch(buffer, (int)buffer.Size());
|
||||
|
||||
if(pInfo->header)
|
||||
{
|
||||
DWORD header = ::HToLE32(*((DWORD*)(BYTE*)buffer));
|
||||
|
||||
if(usPackHeaderFlag != 0)
|
||||
{
|
||||
USHORT flag = (USHORT)(header >> TCP_PACK_LENGTH_BITS);
|
||||
|
||||
if(flag != usPackHeaderFlag)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_DATA);
|
||||
return HR_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD len = header & TCP_PACK_LENGTH_MASK;
|
||||
|
||||
if(len == 0 || len > dwMaxPackSize)
|
||||
{
|
||||
::SetLastError(ERROR_BAD_LENGTH);
|
||||
return HR_ERROR;
|
||||
}
|
||||
|
||||
required = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
rs = pThis->DoFireSuperReceive(pSocket, (const BYTE*)buffer, (int)buffer.Size());
|
||||
|
||||
if(rs == HR_ERROR)
|
||||
return rs;
|
||||
|
||||
required = sizeof(DWORD);
|
||||
}
|
||||
|
||||
pInfo->header = !pInfo->header;
|
||||
pInfo->length = required;
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
template<class T, class B, class S> EnHandleResult ParsePack(T* pThis, TPackInfo<B>* pInfo, B* pBuffer, S* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag, const BYTE* pData, int iLength)
|
||||
{
|
||||
pBuffer->Cat(pData, iLength);
|
||||
|
||||
return ParsePack(pThis, pInfo, pBuffer, pSocket, dwMaxPackSize, usPackHeaderFlag);
|
||||
}
|
||||
229
SSLAgent.cpp
Normal file
229
SSLAgent.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SSLAgent.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
BOOL CSSLAgent::CheckParams()
|
||||
{
|
||||
if(!m_sslCtx.IsValid())
|
||||
{
|
||||
SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
void CSSLAgent::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_sslPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_sslPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_sslPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_sslPool.SetSessionLockTime(GetFreeSocketObjLockTime());
|
||||
m_sslPool.SetSessionPoolSize(GetFreeSocketObjPool());
|
||||
m_sslPool.SetSessionPoolHold(GetFreeSocketObjHold());
|
||||
|
||||
m_sslPool.Prepare();
|
||||
}
|
||||
|
||||
void CSSLAgent::Reset()
|
||||
{
|
||||
m_sslPool.Clear();
|
||||
m_sslCtx.RemoveThreadLocalState();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
void CSSLAgent::OnWorkerThreadEnd(THR_ID dwThreadID)
|
||||
{
|
||||
m_sslCtx.RemoveThreadLocalState();
|
||||
|
||||
__super::OnWorkerThreadEnd(dwThreadID);
|
||||
}
|
||||
|
||||
void CSSLAgent::ReleaseGCSocketObj(BOOL bForce)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_sslPool.ReleaseGCSession(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL CSSLAgent::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
return ::ProcessSend(this, pSocketObj, pSession, pBuffers, iCount);
|
||||
}
|
||||
|
||||
return DoSendPackets(pSocketObj, pBuffers, iCount);
|
||||
}
|
||||
|
||||
EnHandleResult CSSLAgent::FireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = DoFireConnect(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR && m_bSSLAutoHandShake)
|
||||
DoSSLHandShake(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EnHandleResult CSSLAgent::FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
return ::ProcessReceive(this, pSocketObj, pSession, pData, iLength);
|
||||
}
|
||||
|
||||
return DoFireReceive(pSocketObj, pData, iLength);
|
||||
}
|
||||
|
||||
EnHandleResult CSSLAgent::FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
m_sslPool.PutFreeSession(pSession);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL CSSLAgent::StartSSLHandShake(CONNID dwConnID)
|
||||
{
|
||||
if(IsSSLAutoHandShake())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartSSLHandShake(pSocketObj);
|
||||
}
|
||||
|
||||
BOOL CSSLAgent::StartSSLHandShake(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock locallock(pSocketObj->csSend);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoSSLHandShake(pSocketObj);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CSSLAgent::DoSSLHandShake(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
CSSLSession* pSession = m_sslPool.PickFreeSession(pSocketObj->host);
|
||||
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
ENSURE(SetConnectionReserved2(pSocketObj, pSession));
|
||||
ENSURE(::ProcessHandShake(this, pSocketObj, pSession) == HR_OK);
|
||||
}
|
||||
|
||||
BOOL CSSLAgent::GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
ASSERT(lppInfo != nullptr);
|
||||
|
||||
*lppInfo = nullptr;
|
||||
TAgentSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession == nullptr || !pSession->IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return pSession->GetSessionInfo(enInfo, lppInfo);
|
||||
}
|
||||
|
||||
#endif
|
||||
107
SSLAgent.h
Normal file
107
SSLAgent.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpAgent.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
class CSSLAgent : public CTcpAgent
|
||||
{
|
||||
using __super = CTcpAgent;
|
||||
|
||||
public:
|
||||
using __super::Wait;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure() {return TRUE;}
|
||||
virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
|
||||
virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, nullptr);}
|
||||
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, nullptr);}
|
||||
|
||||
|
||||
virtual void CleanupSSLContext()
|
||||
{m_sslCtx.Cleanup();}
|
||||
|
||||
virtual BOOL StartSSLHandShake(CONNID dwConnID);
|
||||
|
||||
public:
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
|
||||
virtual BOOL IsSSLAutoHandShake () {return m_bSSLAutoHandShake;}
|
||||
virtual LPCTSTR GetSSLCipherList() {return m_sslCtx.GetCipherList();}
|
||||
|
||||
virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo);
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj);
|
||||
virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShake(TAgentSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
void DoSSLHandShake(TAgentSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
friend EnHandleResult ProcessHandShake<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession);
|
||||
friend EnHandleResult ProcessReceive<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
|
||||
friend BOOL ProcessSend<>(CSSLAgent* pThis, TAgentSocketObj* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
|
||||
|
||||
public:
|
||||
CSSLAgent(ITcpAgentListener* pListener)
|
||||
: CTcpAgent(pListener)
|
||||
, m_sslPool(m_sslCtx)
|
||||
, m_bSSLAutoHandShake(TRUE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CSSLAgent()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL m_bSSLAutoHandShake;
|
||||
|
||||
CSSLContext m_sslCtx;
|
||||
CSSLSessionPool m_sslPool;
|
||||
};
|
||||
|
||||
#endif
|
||||
150
SSLClient.cpp
Normal file
150
SSLClient.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SSLClient.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
BOOL CSSLClient::CheckParams()
|
||||
{
|
||||
if(!m_sslCtx.IsValid())
|
||||
{
|
||||
SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
void CSSLClient::PrepareStart()
|
||||
{
|
||||
m_dwMainThreadID = SELF_THREAD_ID;
|
||||
|
||||
__super::PrepareStart();
|
||||
}
|
||||
|
||||
void CSSLClient::Reset()
|
||||
{
|
||||
m_sslSession.Reset();
|
||||
|
||||
if(m_dwMainThreadID != 0)
|
||||
{
|
||||
m_sslCtx.RemoveThreadLocalState(m_dwMainThreadID);
|
||||
m_dwMainThreadID = 0;
|
||||
}
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
void CSSLClient::OnWorkerThreadEnd(THR_ID dwThreadID)
|
||||
{
|
||||
m_sslCtx.RemoveThreadLocalState();
|
||||
|
||||
__super::OnWorkerThreadEnd(dwThreadID);
|
||||
}
|
||||
|
||||
BOOL CSSLClient::SendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(m_sslSession.IsValid())
|
||||
return ::ProcessSend(this, this, &m_sslSession, pBuffers, iCount);
|
||||
else
|
||||
return DoSendPackets(this, pBuffers, iCount);
|
||||
}
|
||||
|
||||
EnHandleResult CSSLClient::FireConnect()
|
||||
{
|
||||
EnHandleResult result = DoFireConnect(this);
|
||||
|
||||
if(result != HR_ERROR && m_bSSLAutoHandShake)
|
||||
DoSSLHandShake();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EnHandleResult CSSLClient::FireReceive(const BYTE* pData, int iLength)
|
||||
{
|
||||
if(m_sslSession.IsValid())
|
||||
return ::ProcessReceive(this, this, &m_sslSession, pData, iLength);
|
||||
else
|
||||
return DoFireReceive(this, pData, iLength);
|
||||
}
|
||||
|
||||
BOOL CSSLClient::StartSSLHandShake()
|
||||
{
|
||||
if(IsSSLAutoHandShake())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartSSLHandShakeNoCheck();
|
||||
}
|
||||
|
||||
BOOL CSSLClient::StartSSLHandShakeNoCheck()
|
||||
{
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CCriSecLock locallock(m_sslSession.GetSendLock());
|
||||
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_sslSession.IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoSSLHandShake();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CSSLClient::DoSSLHandShake()
|
||||
{
|
||||
m_sslSession.Renew(m_sslCtx, m_strHost);
|
||||
ENSURE(::ProcessHandShake(this, this, &m_sslSession) == HR_OK);
|
||||
}
|
||||
|
||||
BOOL CSSLClient::GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
if(!m_sslSession.IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return m_sslSession.GetSessionInfo(enInfo, lppInfo);
|
||||
}
|
||||
|
||||
#endif
|
||||
105
SSLClient.h
Normal file
105
SSLClient.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpClient.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
class CSSLClient : public CTcpClient
|
||||
{
|
||||
using __super = CTcpClient;
|
||||
|
||||
public:
|
||||
using __super::Wait;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure() {return TRUE;}
|
||||
virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount);
|
||||
|
||||
virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, nullptr);}
|
||||
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_CLIENT, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, nullptr);}
|
||||
|
||||
virtual void CleanupSSLContext()
|
||||
{m_sslCtx.Cleanup();}
|
||||
|
||||
virtual BOOL StartSSLHandShake();
|
||||
|
||||
public:
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
|
||||
virtual BOOL IsSSLAutoHandShake () {return m_bSSLAutoHandShake;}
|
||||
virtual LPCTSTR GetSSLCipherList() {return m_sslCtx.GetCipherList();}
|
||||
|
||||
virtual BOOL GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo);
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FireConnect();
|
||||
virtual EnHandleResult FireReceive(const BYTE* pData, int iLength);
|
||||
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShakeNoCheck();
|
||||
|
||||
private:
|
||||
void DoSSLHandShake();
|
||||
|
||||
private:
|
||||
friend EnHandleResult ProcessHandShake<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession);
|
||||
friend EnHandleResult ProcessReceive<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
|
||||
friend BOOL ProcessSend<>(CSSLClient* pThis, CSSLClient* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
|
||||
|
||||
public:
|
||||
CSSLClient(ITcpClientListener* pListener)
|
||||
: CTcpClient (pListener)
|
||||
, m_sslSession (m_itPool)
|
||||
, m_dwMainThreadID (0)
|
||||
, m_bSSLAutoHandShake (TRUE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CSSLClient()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
THR_ID m_dwMainThreadID;
|
||||
BOOL m_bSSLAutoHandShake;
|
||||
|
||||
CSSLContext m_sslCtx;
|
||||
CSSLSession m_sslSession;
|
||||
};
|
||||
|
||||
#endif
|
||||
1200
SSLHelper.cpp
Normal file
1200
SSLHelper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
496
SSLHelper.h
Normal file
496
SSLHelper.h
Normal file
@@ -0,0 +1,496 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPTypeDef.h"
|
||||
#include "common/BufferPool.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "openssl/ssl.h"
|
||||
|
||||
#define OPENSSL_VERSION_1_0_2 0x10002000L
|
||||
#define OPENSSL_VERSION_1_1_0 0x10100000L
|
||||
#define OPENSSL_VERSION_3_0_0 0x30000000L
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
|
||||
#define DEFAULT_CIPHER_LIST _T("DEFAULT:!aNULL:!eNULL:!SSLv2")
|
||||
#else
|
||||
#define DEFAULT_CIPHER_LIST _T("DEFAULT:!aNULL:!eNULL:!SSLv2:!SSLv3")
|
||||
#endif
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_3_0_0
|
||||
#define FN_X509_NAME_HASH X509_NAME_hash
|
||||
#else
|
||||
inline unsigned long FN_X509_NAME_HASH(const X509_NAME *x) {return X509_NAME_hash_ex(x, nullptr, nullptr, nullptr);}
|
||||
#endif
|
||||
|
||||
/************************************************************************
|
||||
名称:SSL 全局常量
|
||||
描述:声明 SSL 组件的公共全局常量
|
||||
************************************************************************/
|
||||
|
||||
#define SSL_DOMAIN_SEP_CHAR '.'
|
||||
|
||||
/************************************************************************
|
||||
名称:SSL 握手状态
|
||||
描述:标识当前连接的 SSL 握手状态
|
||||
************************************************************************/
|
||||
enum EnSSLHandShakeStatus
|
||||
{
|
||||
SSL_HSS_INIT = 0, // 初始状态
|
||||
SSL_HSS_PROC = 1, // 正在握手
|
||||
SSL_HSS_SUCC = 2, // 握手成功
|
||||
};
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
|
||||
|
||||
/* SSL CRYPTO DYNLOCK 结构 */
|
||||
typedef struct CRYPTO_dynlock_value
|
||||
{
|
||||
CSimpleRWLock cs;
|
||||
} DynamicLock;
|
||||
|
||||
#endif
|
||||
|
||||
class CSSLInitializer
|
||||
{
|
||||
public:
|
||||
|
||||
static void CleanupThreadState(THR_ID dwThreadID = 0);
|
||||
|
||||
private:
|
||||
|
||||
CSSLInitializer();
|
||||
~CSSLInitializer();
|
||||
|
||||
private:
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
|
||||
static void ssl_lock_callback(int mode, int n, const char *file, int line);
|
||||
static CRYPTO_dynlock_value* ssl_lock_dyn_create_callback(const char *file, int line);
|
||||
static void ssl_lock_dyn_callback(int mode, CRYPTO_dynlock_value* l, const char *file, int line);
|
||||
static void ssl_lock_dyn_destroy_callback(CRYPTO_dynlock_value* l, const char *file, int line);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_1_1_0
|
||||
static int sm_iLockNum;
|
||||
static CSimpleRWLock* sm_pcsLocks;
|
||||
#endif
|
||||
|
||||
static CSSLInitializer sm_instance;
|
||||
|
||||
};
|
||||
|
||||
/************************************************************************
|
||||
名称:SSL Context
|
||||
描述:初始化和清理 SSL 运行环境
|
||||
************************************************************************/
|
||||
class CSSLContext
|
||||
{
|
||||
typedef unordered_map<CString, int, cstring_nc_hash_func::hash, cstring_nc_hash_func::equal_to> CServerNameMap;
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* 名称:初始化 SSL 环境参数
|
||||
* 描述:SSL 环境参数必须在 SSL 通信组件启动前完成初始化,否则启动失败
|
||||
*
|
||||
* 参数: enSessionMode -- SSL 工作模式(参考 EnSSLSessionMode)
|
||||
* iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
|
||||
* lpPemCert -- 证书文件(客户端可选)
|
||||
* lpPemKey -- 私钥文件(客户端可选)
|
||||
* lpKeyPasswod -- 私钥密码(没有密码则为空)
|
||||
* lpCAPemCert -- CA 证书文件或目录(单向验证或客户端可选)
|
||||
* fnServerNameCallback -- SNI 回调函数指针(可选,只用于服务端,如果为 nullptr 则使用 SNI 默认回调函数)
|
||||
*
|
||||
* 返回值: TRUE -- 成功
|
||||
* FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
|
||||
*/
|
||||
BOOL Initialize(EnSSLSessionMode enSessionMode, int iVerifyMode = SSL_VM_NONE, BOOL bMemory = FALSE, LPVOID lpPemCert = nullptr, LPVOID lpPemKey = nullptr, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr);
|
||||
|
||||
/*
|
||||
* 名称:增加 SNI 主机证书(只用于服务端)
|
||||
* 描述:SSL 服务端在 Initialize() 成功后可以调用本方法增加多个 SNI 主机证书
|
||||
*
|
||||
* 参数: iVerifyMode -- SSL 验证模式(参考 EnSSLVerifyMode)
|
||||
* lpPemCert -- 证书文件
|
||||
* lpPemKey -- 私钥文件
|
||||
* lpKeyPasswod -- 私钥密码(没有密码则为空)
|
||||
* lpCAPemCert -- CA 证书文件或目录(单向验证可选)
|
||||
*
|
||||
* 返回值: 正数 -- 成功,并返回 SNI 主机证书对应的索引,该索引用于在 SNI 回调函数中定位 SNI 主机
|
||||
* 负数 -- 失败,可通过 ::GetLastError() 获取失败原因
|
||||
*/
|
||||
int AddServerContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod = nullptr, LPVOID lpCAPemCert = nullptr);
|
||||
|
||||
/*
|
||||
* 名称:绑定 SNI 主机域名
|
||||
* 描述:SSL 服务端在 AddServerContext() 成功后可以调用本方法绑定主机域名到 SNI 主机证书
|
||||
*
|
||||
* 参数: lpszServerName -- 主机域名
|
||||
* iContextIndex -- SNI 主机证书对应的索引
|
||||
*
|
||||
* 返回值: TRUE -- 成功
|
||||
* FALSE -- 失败,可通过 ::GetLastError() 获取失败原因
|
||||
*/
|
||||
virtual BOOL BindServerName(LPCTSTR lpszServerName, int iContextIndex);
|
||||
|
||||
/*
|
||||
* 名称:清理 SSL 运行环境
|
||||
* 描述:清理 SSL 运行环境,回收 SSL 相关内存
|
||||
* 1、CSSLContext 的析构函数会自动调用本方法
|
||||
* 2、当要重新设置 SSL 环境参数时,需要先调用本方法清理原先的环境参数
|
||||
*
|
||||
* 参数: 无
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
void Cleanup();
|
||||
|
||||
/* 获取 SSL 运行环境 SSL_CTX 对象 */
|
||||
SSL_CTX* GetContext (int i) const;
|
||||
/* 获取 SSL 运行环境默认 SSL_CTX 对象 */
|
||||
SSL_CTX* GetDefaultContext () const {return m_sslCtx;}
|
||||
/* 获取 SSL 运行环境的配置模式,配置模式参考:EnSSLSessionMode */
|
||||
EnSSLSessionMode GetSessionMode () const {return m_enSessionMode;}
|
||||
/* 检查 SSL 运行环境是否初始化完成 */
|
||||
BOOL IsValid () const {return m_sslCtx != nullptr;}
|
||||
|
||||
/* 设置 SSL 加密算法列表 */
|
||||
void SetCipherList(LPCTSTR lpszCipherList) {m_strCipherList = lpszCipherList;}
|
||||
/* 获取 SSL 加密算法列表 */
|
||||
LPCTSTR GetCipherList() {return m_strCipherList;}
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* 名称:清理线程局部环境 SSL 资源
|
||||
* 描述:任何一个操作 SSL 的线程,在通信结束时都需要清理线程局部环境 SSL 资源
|
||||
* 1、主线程和 HP-Socket 工作线程在通信结束时会自动清理线程局部环境 SSL 资源。因此,一般情况下不必手工调用本方法
|
||||
* 2、特殊情况下,当自定义线程参与 HP-Socket 通信操作并检查到 SSL 内存泄漏时,需在每次通信结束时自定义线程调用本方法
|
||||
*
|
||||
* 参数: dwThreadID -- 线程 ID(0:当前线程)
|
||||
*
|
||||
* 返回值:无
|
||||
*/
|
||||
static void RemoveThreadLocalState(THR_ID dwThreadID = 0) {CSSLInitializer::CleanupThreadState(dwThreadID);}
|
||||
|
||||
public:
|
||||
|
||||
CSSLContext()
|
||||
: m_strCipherList (DEFAULT_CIPHER_LIST)
|
||||
, m_enSessionMode (SSL_SM_SERVER)
|
||||
, m_sslCtx (nullptr)
|
||||
, m_fnServerNameCallback(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CSSLContext() {Cleanup();}
|
||||
|
||||
private:
|
||||
|
||||
void SetServerNameCallback(Fn_SNI_ServerNameCallback fn);
|
||||
int AddContext(int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
|
||||
BOOL LoadCertAndKey(SSL_CTX* sslCtx, int iVerifyMode, BOOL bMemory, LPVOID lpPemCert, LPVOID lpPemKey, LPVOID lpKeyPasswod, LPVOID lpCAPemCert);
|
||||
BOOL LoadCertAndKeyByFile(SSL_CTX* sslCtx, int iVerifyMode, LPCTSTR lpszPemCertFile, LPCTSTR lpszPemKeyFile, LPCTSTR lpszKeyPassword, LPCTSTR lpszCAPemCertFileOrPath);
|
||||
BOOL LoadCertAndKeyByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword, LPCSTR lpszCAPemCert);
|
||||
BOOL LoadCAPemCertByMemory(SSL_CTX* sslCtx, int iVerifyMode, LPCSTR lpszCAPemCert);
|
||||
BOOL LoadPemCertAndKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert, LPCSTR lpszPemKey, LPCSTR lpszKeyPassword);
|
||||
BOOL AddCAPemCertToStoreByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
|
||||
BOOL SetClientCAListByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
|
||||
BOOL SetPrivateKeyByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemKey);
|
||||
BOOL SetCertChainByMemory(SSL_CTX* sslCtx, LPCSTR lpszPemCert);
|
||||
|
||||
private:
|
||||
|
||||
static int InternalServerNameCallback(SSL* ssl, int* ad, void* arg);
|
||||
|
||||
public:
|
||||
|
||||
/*
|
||||
* 名称:SNI 默认回调函数
|
||||
* 描述:Initialize 方法中如果不指定 SNI 回调函数则使用此 SNI 默认回调函数
|
||||
*
|
||||
* 参数: lpszServerName -- 请求域名
|
||||
* pContext -- SSL Context 对象
|
||||
*
|
||||
* 返回值:SNI 主机证书对应的索引
|
||||
*/
|
||||
static int __HP_CALL DefaultServerNameCallback(LPCTSTR lpszServerName, PVOID pContext);
|
||||
|
||||
private:
|
||||
|
||||
CString m_strCipherList;
|
||||
EnSSLSessionMode m_enSessionMode;
|
||||
CServerNameMap m_sslServerNames;
|
||||
vector<SSL_CTX*> m_lsSslCtxs;
|
||||
SSL_CTX* m_sslCtx;
|
||||
|
||||
Fn_SNI_ServerNameCallback m_fnServerNameCallback;
|
||||
};
|
||||
|
||||
class CSSLSession : public CSafeCounter
|
||||
{
|
||||
public:
|
||||
|
||||
BOOL WriteRecvChannel(const BYTE* pData, int iLength);
|
||||
BOOL ReadRecvChannel();
|
||||
|
||||
BOOL WriteSendChannel(const BYTE* pData, int iLength);
|
||||
BOOL WriteSendChannel(const WSABUF pBuffers[], int iCount);
|
||||
BOOL ReadSendChannel();
|
||||
|
||||
const WSABUF& GetRecvBuffer() const {return m_bufRecv;}
|
||||
const WSABUF& GetSendBuffer() const {return m_bufSend;}
|
||||
|
||||
CSSLSession* Renew(const CSSLContext& sslCtx, LPCSTR lpszHostName = nullptr);
|
||||
BOOL Reset();
|
||||
BOOL IsValid() const {return GetStatus() != SSL_HSS_INIT;}
|
||||
BOOL IsHandShaking() const {return GetStatus() == SSL_HSS_PROC;}
|
||||
BOOL IsReady() const {return GetStatus() == SSL_HSS_SUCC;}
|
||||
EnSSLHandShakeStatus GetStatus() const {return m_enStatus;}
|
||||
DWORD GetFreeTime() const {return m_dwFreeTime;}
|
||||
CCriSec& GetSendLock() {return m_csSend;}
|
||||
BOOL GetSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo);
|
||||
|
||||
private:
|
||||
|
||||
BOOL IsFatalError(int iBytes);
|
||||
|
||||
public:
|
||||
|
||||
CSSLSession(CItemPool& itPool)
|
||||
: m_enStatus(SSL_HSS_INIT)
|
||||
, m_itPool (itPool)
|
||||
, m_ssl (nullptr)
|
||||
, m_bioSend (nullptr)
|
||||
, m_bioRecv (nullptr)
|
||||
, m_pitSend (nullptr)
|
||||
, m_pitRecv (nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CSSLSession()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
static CSSLSession* Construct(CItemPool& itPool)
|
||||
{return new CSSLSession(itPool);}
|
||||
|
||||
static void Destruct(CSSLSession* pSession)
|
||||
{if(pSession) delete pSession;}
|
||||
|
||||
private:
|
||||
CItemPool& m_itPool;
|
||||
CCriSec m_csSend;
|
||||
|
||||
DWORD m_dwFreeTime;
|
||||
EnSSLHandShakeStatus m_enStatus;
|
||||
|
||||
SSL* m_ssl;
|
||||
BIO* m_bioSend;
|
||||
BIO* m_bioRecv;
|
||||
|
||||
TItem* m_pitSend;
|
||||
TItem* m_pitRecv;
|
||||
WSABUF m_bufSend;
|
||||
WSABUF m_bufRecv;
|
||||
};
|
||||
|
||||
class CSSLSessionPool
|
||||
{
|
||||
typedef CRingPool<CSSLSession> TSSLSessionList;
|
||||
typedef CCASQueue<CSSLSession> TSSLSessionQueue;
|
||||
|
||||
public:
|
||||
CSSLSession* PickFreeSession (LPCSTR lpszHostName = nullptr);
|
||||
void PutFreeSession (CSSLSession* pSession);
|
||||
|
||||
void Prepare ();
|
||||
void Clear ();
|
||||
|
||||
void ReleaseGCSession (BOOL bForce = FALSE);
|
||||
|
||||
public:
|
||||
void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
|
||||
void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
|
||||
void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
|
||||
|
||||
void SetSessionLockTime (DWORD dwSessionLockTime) {m_dwSessionLockTime = dwSessionLockTime;}
|
||||
void SetSessionPoolSize (DWORD dwSessionPoolSize) {m_dwSessionPoolSize = dwSessionPoolSize;}
|
||||
void SetSessionPoolHold (DWORD dwSessionPoolHold) {m_dwSessionPoolHold = dwSessionPoolHold;}
|
||||
|
||||
DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
|
||||
DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
|
||||
DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
|
||||
|
||||
DWORD GetSessionLockTime() {return m_dwSessionLockTime;}
|
||||
DWORD GetSessionPoolSize() {return m_dwSessionPoolSize;}
|
||||
DWORD GetSessionPoolHold() {return m_dwSessionPoolHold;}
|
||||
|
||||
public:
|
||||
CSSLSessionPool(const CSSLContext& sslCtx,
|
||||
DWORD dwPoolSize = DEFAULT_SESSION_POOL_SIZE,
|
||||
DWORD dwPoolHold = DEFAULT_SESSION_POOL_HOLD,
|
||||
DWORD dwLockTime = DEFAULT_SESSION_LOCK_TIME)
|
||||
: m_sslCtx(sslCtx)
|
||||
, m_dwSessionPoolSize(dwPoolSize)
|
||||
, m_dwSessionPoolHold(dwPoolHold)
|
||||
, m_dwSessionLockTime(dwLockTime)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CSSLSessionPool() {Clear();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSSLSessionPool)
|
||||
|
||||
public:
|
||||
static const DWORD DEFAULT_ITEM_CAPACITY;
|
||||
static const DWORD DEFAULT_ITEM_POOL_SIZE;
|
||||
static const DWORD DEFAULT_ITEM_POOL_HOLD;
|
||||
static const DWORD DEFAULT_SESSION_LOCK_TIME;
|
||||
static const DWORD DEFAULT_SESSION_POOL_SIZE;
|
||||
static const DWORD DEFAULT_SESSION_POOL_HOLD;
|
||||
|
||||
private:
|
||||
CItemPool m_itPool;
|
||||
const CSSLContext& m_sslCtx;
|
||||
|
||||
DWORD m_dwSessionLockTime;
|
||||
DWORD m_dwSessionPoolSize;
|
||||
DWORD m_dwSessionPoolHold;
|
||||
|
||||
TSSLSessionList m_lsFreeSession;
|
||||
TSSLSessionQueue m_lsGCSession;
|
||||
};
|
||||
|
||||
template<class T, class S> EnHandleResult ProcessHandShake(T* pThis, S* pSocketObj, CSSLSession* pSession)
|
||||
{
|
||||
EnHandleResult result = HR_OK;
|
||||
|
||||
CCriSecLock locallock(pSession->GetSendLock());
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
VERIFY(pSession->ReadSendChannel());
|
||||
const WSABUF& buffer = pSession->GetSendBuffer();
|
||||
|
||||
if(buffer.len == 0)
|
||||
break;
|
||||
|
||||
if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
|
||||
{
|
||||
result = HR_ERROR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, class S> EnHandleResult ProcessReceive(T* pThis, S* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength)
|
||||
{
|
||||
if(!pSession->WriteRecvChannel(pData, iLength))
|
||||
return HR_ERROR;
|
||||
|
||||
EnHandleResult result = HR_OK;
|
||||
EnSSLHandShakeStatus enStatus = pSession->GetStatus();
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
if(!pSession->ReadRecvChannel())
|
||||
return HR_ERROR;
|
||||
|
||||
if(enStatus == SSL_HSS_PROC && pSession->IsReady())
|
||||
{
|
||||
result = ProcessHandShake(pThis, pSocketObj, pSession);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
break;
|
||||
|
||||
enStatus = SSL_HSS_SUCC;
|
||||
result = pThis->DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
const WSABUF& buffer = pSession->GetRecvBuffer();
|
||||
|
||||
if(buffer.len == 0)
|
||||
break;
|
||||
|
||||
result = pThis->DoFireReceive(pSocketObj, (const BYTE*)buffer.buf, buffer.len);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
break;
|
||||
}
|
||||
|
||||
if(result != HR_ERROR && pSession->IsHandShaking())
|
||||
result = ::ProcessHandShake(pThis, pSocketObj, pSession);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<class T, class S> BOOL ProcessSend(T* pThis, S* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount)
|
||||
{
|
||||
if(pSession == nullptr || !pSession->IsReady())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CCriSecLock locallock(pSession->GetSendLock());
|
||||
|
||||
if(!pSession->IsReady())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VERIFY(pSession->WriteSendChannel(pBuffers, iCount));
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
VERIFY(pSession->ReadSendChannel());
|
||||
const WSABUF& buffer = pSession->GetSendBuffer();
|
||||
|
||||
if(buffer.len == 0)
|
||||
break;
|
||||
|
||||
if(!pThis->DoSendPackets(pSocketObj, &buffer, 1))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
229
SSLServer.cpp
Normal file
229
SSLServer.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SSLServer.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
BOOL CSSLServer::CheckParams()
|
||||
{
|
||||
if(!m_sslCtx.IsValid())
|
||||
{
|
||||
SetLastError(SE_SSL_ENV_NOT_READY, __FUNCTION__, ERROR_NOT_READY);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return __super::CheckParams();
|
||||
}
|
||||
|
||||
void CSSLServer::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_sslPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_sslPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_sslPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_sslPool.SetSessionLockTime(GetFreeSocketObjLockTime());
|
||||
m_sslPool.SetSessionPoolSize(GetFreeSocketObjPool());
|
||||
m_sslPool.SetSessionPoolHold(GetFreeSocketObjHold());
|
||||
|
||||
m_sslPool.Prepare();
|
||||
}
|
||||
|
||||
void CSSLServer::Reset()
|
||||
{
|
||||
m_sslPool.Clear();
|
||||
m_sslCtx.RemoveThreadLocalState();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
void CSSLServer::OnWorkerThreadEnd(THR_ID dwThreadID)
|
||||
{
|
||||
m_sslCtx.RemoveThreadLocalState();
|
||||
|
||||
__super::OnWorkerThreadEnd(dwThreadID);
|
||||
}
|
||||
|
||||
void CSSLServer::ReleaseGCSocketObj(BOOL bForce)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_sslPool.ReleaseGCSession(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL CSSLServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
TSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
return ::ProcessSend(this, pSocketObj, pSession, pBuffers, iCount);
|
||||
}
|
||||
|
||||
return DoSendPackets(pSocketObj, pBuffers, iCount);
|
||||
}
|
||||
|
||||
EnHandleResult CSSLServer::FireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = DoFireAccept(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR && m_bSSLAutoHandShake)
|
||||
DoSSLHandShake(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EnHandleResult CSSLServer::FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
return ::ProcessReceive(this, pSocketObj, pSession, pData, iLength);
|
||||
}
|
||||
|
||||
return DoFireReceive(pSocketObj, pData, iLength);
|
||||
}
|
||||
|
||||
EnHandleResult CSSLServer::FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
m_sslPool.PutFreeSession(pSession);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL CSSLServer::StartSSLHandShake(CONNID dwConnID)
|
||||
{
|
||||
if(IsSSLAutoHandShake())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return StartSSLHandShake(pSocketObj);
|
||||
}
|
||||
|
||||
BOOL CSSLServer::StartSSLHandShake(TSocketObj* pSocketObj)
|
||||
{
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CReentrantCriSecLock locallock(pSocketObj->csSend);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(!pSocketObj->HasConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_ALREADY_INITIALIZED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DoSSLHandShake(pSocketObj);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CSSLServer::DoSSLHandShake(TSocketObj* pSocketObj)
|
||||
{
|
||||
CSSLSession* pSession = m_sslPool.PickFreeSession();
|
||||
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
ENSURE(SetConnectionReserved2(pSocketObj, pSession));
|
||||
ENSURE(::ProcessHandShake(this, pSocketObj, pSession) == HR_OK);
|
||||
}
|
||||
|
||||
BOOL CSSLServer::GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo)
|
||||
{
|
||||
ASSERT(lppInfo != nullptr);
|
||||
|
||||
*lppInfo = nullptr;
|
||||
TSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CSSLSession* pSession = nullptr;
|
||||
GetConnectionReserved2(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession == nullptr || !pSession->IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return pSession->GetSessionInfo(enInfo, lppInfo);
|
||||
}
|
||||
|
||||
#endif
|
||||
115
SSLServer.h
Normal file
115
SSLServer.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpServer.h"
|
||||
#include "SSLHelper.h"
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
class CSSLServer : public CTcpServer
|
||||
{
|
||||
using __super = CTcpServer;
|
||||
|
||||
public:
|
||||
using __super::Wait;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure() {return TRUE;}
|
||||
virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
|
||||
virtual BOOL SetupSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_SERVER, iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath, fnServerNameCallback);}
|
||||
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr)
|
||||
{return m_sslCtx.Initialize(SSL_SM_SERVER, iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert, fnServerNameCallback);}
|
||||
|
||||
virtual int AddSSLContext(int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr)
|
||||
{return m_sslCtx.AddServerContext(iVerifyMode, FALSE, (LPVOID)lpszPemCertFile, (LPVOID)lpszPemKeyFile, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCertFileOrPath);}
|
||||
|
||||
virtual int AddSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr)
|
||||
{return m_sslCtx.AddServerContext(iVerifyMode, TRUE, (LPVOID)lpszPemCert, (LPVOID)lpszPemKey, (LPVOID)lpszKeyPassword, (LPVOID)lpszCAPemCert);}
|
||||
|
||||
virtual BOOL BindSSLServerName(LPCTSTR lpszServerName, int iContextIndex)
|
||||
{return m_sslCtx.BindServerName(lpszServerName, iContextIndex);}
|
||||
|
||||
virtual void CleanupSSLContext()
|
||||
{m_sslCtx.Cleanup();}
|
||||
|
||||
virtual BOOL StartSSLHandShake(CONNID dwConnID);
|
||||
|
||||
public:
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {ENSURE_HAS_STOPPED(); m_bSSLAutoHandShake = bAutoHandShake;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){ENSURE_HAS_STOPPED(); m_sslCtx.SetCipherList(lpszCipherList);}
|
||||
virtual BOOL IsSSLAutoHandShake () {return m_bSSLAutoHandShake;}
|
||||
virtual LPCTSTR GetSSLCipherList() {return m_sslCtx.GetCipherList();}
|
||||
|
||||
virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo);
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FireAccept(TSocketObj* pSocketObj);
|
||||
virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShake(TSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
void DoSSLHandShake(TSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
friend EnHandleResult ProcessHandShake<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession);
|
||||
friend EnHandleResult ProcessReceive<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession, const BYTE* pData, int iLength);
|
||||
friend BOOL ProcessSend<>(CSSLServer* pThis, TSocketObj* pSocketObj, CSSLSession* pSession, const WSABUF * pBuffers, int iCount);
|
||||
|
||||
public:
|
||||
CSSLServer(ITcpServerListener* pListener)
|
||||
: CTcpServer(pListener)
|
||||
, m_sslPool(m_sslCtx)
|
||||
, m_bSSLAutoHandShake(TRUE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CSSLServer()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL m_bSSLAutoHandShake;
|
||||
|
||||
CSSLContext m_sslCtx;
|
||||
CSSLSessionPool m_sslPool;
|
||||
};
|
||||
|
||||
#endif
|
||||
1669
SocketHelper.cpp
Normal file
1669
SocketHelper.cpp
Normal file
File diff suppressed because it is too large
Load Diff
959
SocketHelper.h
Normal file
959
SocketHelper.h
Normal file
@@ -0,0 +1,959 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPTypeDef.h"
|
||||
#include "hpsocket/SocketInterface.h"
|
||||
#include "common/StringT.h"
|
||||
#include "common/SysHelper.h"
|
||||
#include "common/BufferPtr.h"
|
||||
#include "common/BufferPool.h"
|
||||
#include "common/RingBuffer.h"
|
||||
#include "common/FileHelper.h"
|
||||
#include "InternalDef.h"
|
||||
|
||||
#include <netdb.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#ifdef _ZLIB_SUPPORT
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#ifdef _BROTLI_SUPPORT
|
||||
#include <brotli/decode.h>
|
||||
#include <brotli/encode.h>
|
||||
#endif
|
||||
|
||||
using ADDRESS_FAMILY = sa_family_t;
|
||||
using IN_ADDR = in_addr;
|
||||
using IN6_ADDR = in6_addr;
|
||||
using SOCKADDR = sockaddr;
|
||||
using SOCKADDR_IN = sockaddr_in;
|
||||
using SOCKADDR_IN6 = sockaddr_in6;
|
||||
|
||||
typedef struct hp_addr
|
||||
{
|
||||
ADDRESS_FAMILY family;
|
||||
|
||||
union
|
||||
{
|
||||
ULONG_PTR addr;
|
||||
IN_ADDR addr4;
|
||||
IN6_ADDR addr6;
|
||||
};
|
||||
|
||||
static const hp_addr ANY_ADDR4;
|
||||
static const hp_addr ANY_ADDR6;
|
||||
|
||||
inline int AddrSize() const
|
||||
{
|
||||
return AddrSize(family);
|
||||
}
|
||||
|
||||
inline static int AddrSize(ADDRESS_FAMILY f)
|
||||
{
|
||||
if(f == AF_INET)
|
||||
return sizeof(IN_ADDR);
|
||||
|
||||
return sizeof(IN6_ADDR);
|
||||
}
|
||||
|
||||
inline static const hp_addr& AnyAddr(ADDRESS_FAMILY f)
|
||||
{
|
||||
if(f == AF_INET)
|
||||
return ANY_ADDR4;
|
||||
|
||||
return ANY_ADDR6;
|
||||
}
|
||||
|
||||
inline const ULONG_PTR* Addr() const {return &addr;}
|
||||
inline ULONG_PTR* Addr() {return &addr;}
|
||||
|
||||
inline BOOL IsIPv4() const {return family == AF_INET;}
|
||||
inline BOOL IsIPv6() const {return family == AF_INET6;}
|
||||
inline BOOL IsSpecified() const {return IsIPv4() || IsIPv6();}
|
||||
inline void ZeroAddr() {::ZeroMemory(&addr6, sizeof(addr6));}
|
||||
inline void Reset() {::ZeroMemory(this, sizeof(*this));}
|
||||
|
||||
inline hp_addr& Copy(hp_addr& other) const
|
||||
{
|
||||
if(this != &other)
|
||||
memcpy(&other, this, offsetof(hp_addr, addr) + AddrSize());
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
hp_addr(ADDRESS_FAMILY f = AF_UNSPEC, BOOL bZeroAddr = FALSE)
|
||||
{
|
||||
family = f;
|
||||
|
||||
if(bZeroAddr) ZeroAddr();
|
||||
}
|
||||
|
||||
} HP_ADDR, *HP_PADDR;
|
||||
|
||||
typedef struct hp_sockaddr
|
||||
{
|
||||
union
|
||||
{
|
||||
ADDRESS_FAMILY family;
|
||||
SOCKADDR addr;
|
||||
SOCKADDR_IN addr4;
|
||||
SOCKADDR_IN6 addr6;
|
||||
};
|
||||
|
||||
inline int AddrSize() const
|
||||
{
|
||||
return AddrSize(family);
|
||||
}
|
||||
|
||||
inline static int AddrSize(ADDRESS_FAMILY f)
|
||||
{
|
||||
if(f == AF_INET)
|
||||
return sizeof(SOCKADDR_IN);
|
||||
|
||||
return sizeof(SOCKADDR_IN6);
|
||||
}
|
||||
|
||||
inline int EffectAddrSize() const
|
||||
{
|
||||
return EffectAddrSize(family);
|
||||
}
|
||||
|
||||
inline static int EffectAddrSize(ADDRESS_FAMILY f)
|
||||
{
|
||||
return (f == AF_INET) ? offsetof(SOCKADDR_IN, sin_zero) : sizeof(SOCKADDR_IN6);
|
||||
}
|
||||
|
||||
inline static const hp_sockaddr& AnyAddr(ADDRESS_FAMILY f)
|
||||
{
|
||||
static const hp_sockaddr s_any_addr4(AF_INET, TRUE);
|
||||
static const hp_sockaddr s_any_addr6(AF_INET6, TRUE);
|
||||
|
||||
if(f == AF_INET)
|
||||
return s_any_addr4;
|
||||
|
||||
return s_any_addr6;
|
||||
}
|
||||
|
||||
inline static int AddrMinStrLength(ADDRESS_FAMILY f)
|
||||
{
|
||||
if(f == AF_INET)
|
||||
return INET_ADDRSTRLEN;
|
||||
|
||||
return INET6_ADDRSTRLEN;
|
||||
}
|
||||
|
||||
inline BOOL IsIPv4() const {return family == AF_INET;}
|
||||
inline BOOL IsIPv6() const {return family == AF_INET6;}
|
||||
inline BOOL IsSpecified() const {return IsIPv4() || IsIPv6();}
|
||||
inline USHORT Port() const {return ntohs(addr4.sin_port);}
|
||||
inline void SetPort(USHORT usPort) {addr4.sin_port = htons(usPort);}
|
||||
inline void* SinAddr() const {return IsIPv4() ? (void*)&addr4.sin_addr : (void*)&addr6.sin6_addr;}
|
||||
inline void* SinAddr() {return IsIPv4() ? (void*)&addr4.sin_addr : (void*)&addr6.sin6_addr;}
|
||||
|
||||
inline const SOCKADDR* Addr() const {return &addr;}
|
||||
inline SOCKADDR* Addr() {return &addr;}
|
||||
inline void ZeroAddr() {::ZeroMemory(((char*)this) + sizeof(family), sizeof(*this) - sizeof(family));}
|
||||
inline void Reset() {::ZeroMemory(this, sizeof(*this));}
|
||||
|
||||
inline hp_sockaddr& Copy(hp_sockaddr& other) const
|
||||
{
|
||||
if(this != &other)
|
||||
memcpy(&other, this, AddrSize());
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
size_t Hash() const
|
||||
{
|
||||
ASSERT(IsSpecified());
|
||||
|
||||
size_t _Val = 2166136261U;
|
||||
const int size = EffectAddrSize();
|
||||
const BYTE* pAddr = (const BYTE*)Addr();
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
_Val = 16777619U * _Val ^ (size_t)pAddr[i];
|
||||
|
||||
return (_Val);
|
||||
}
|
||||
|
||||
bool EqualTo(const hp_sockaddr& other) const
|
||||
{
|
||||
ASSERT(IsSpecified() && other.IsSpecified());
|
||||
|
||||
return EqualMemory(this, &other, EffectAddrSize());
|
||||
}
|
||||
|
||||
hp_sockaddr(ADDRESS_FAMILY f = AF_UNSPEC, BOOL bZeroAddr = FALSE)
|
||||
{
|
||||
family = f;
|
||||
|
||||
if(bZeroAddr) ZeroAddr();
|
||||
}
|
||||
|
||||
} HP_SOCKADDR, *HP_PSOCKADDR;
|
||||
|
||||
typedef struct hp_scope_host
|
||||
{
|
||||
LPCTSTR addr;
|
||||
LPCTSTR name;
|
||||
|
||||
BOOL bNeedFree;
|
||||
|
||||
hp_scope_host(LPCTSTR lpszOriginAddress)
|
||||
{
|
||||
ASSERT(lpszOriginAddress != nullptr);
|
||||
|
||||
LPCTSTR lpszFind = ::StrChr(lpszOriginAddress, HOST_SEPARATOR_CHAR);
|
||||
|
||||
if(lpszFind == nullptr)
|
||||
{
|
||||
addr = lpszOriginAddress;
|
||||
name = lpszOriginAddress;
|
||||
bNeedFree = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = (int)(lpszFind - lpszOriginAddress);
|
||||
int iSize = (int)lstrlen(lpszOriginAddress) + 1;
|
||||
LPTSTR lpszCopy = new TCHAR[iSize];
|
||||
|
||||
::memcpy((PVOID)lpszCopy, (PVOID)lpszOriginAddress, iSize * sizeof(TCHAR));
|
||||
|
||||
lpszCopy[i] = 0;
|
||||
addr = lpszCopy;
|
||||
name = lpszCopy + i + 1;
|
||||
bNeedFree = TRUE;
|
||||
|
||||
if(::IsStrEmpty(name))
|
||||
name = addr;
|
||||
}
|
||||
}
|
||||
|
||||
~hp_scope_host()
|
||||
{
|
||||
if(bNeedFree)
|
||||
delete[] addr;
|
||||
}
|
||||
|
||||
} HP_SCOPE_HOST, *HP_PSCOPE_HOST;
|
||||
|
||||
struct TNodeBufferObj : public TItem
|
||||
{
|
||||
using __super = TItem;
|
||||
|
||||
HP_SOCKADDR remoteAddr;
|
||||
|
||||
public:
|
||||
void Reset(int first = 0, int last = 0)
|
||||
{
|
||||
__super::Reset(first, last);
|
||||
remoteAddr.Reset();
|
||||
}
|
||||
|
||||
public:
|
||||
static TNodeBufferObj* Construct(CPrivateHeap& heap,
|
||||
int capacity = DEFAULT_ITEM_CAPACITY,
|
||||
BYTE* pData = nullptr,
|
||||
int length = 0)
|
||||
{
|
||||
return ::ConstructItemT((TNodeBufferObj*)(nullptr), heap, capacity, pData, length);
|
||||
}
|
||||
|
||||
static void Destruct(TNodeBufferObj* pBufferObj)
|
||||
{
|
||||
::DestructItemT(pBufferObj);
|
||||
}
|
||||
|
||||
TNodeBufferObj(CPrivateHeap& hp, BYTE* pHead, int cap = DEFAULT_ITEM_CAPACITY, BYTE* pData = nullptr, int length = 0)
|
||||
: TItem(hp, pHead, cap, pData, length)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~TNodeBufferObj()
|
||||
{
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TNodeBufferObj)
|
||||
};
|
||||
|
||||
typedef TItemPtrT<TNodeBufferObj> TNodeBufferObjPtr;
|
||||
typedef CNodePoolT<TNodeBufferObj> CNodeBufferObjPool;
|
||||
typedef CCASQueue<TNodeBufferObj> CNodeRecvQueue;
|
||||
typedef TItemListExT<TNodeBufferObj, volatile int> TNodeBufferObjList;
|
||||
|
||||
typedef unique_ptr<CCriSec[]> CNodeCriSecs;
|
||||
typedef unique_ptr<TNodeBufferObjList[]> TNodeBufferObjLists;
|
||||
|
||||
/* Server 组件和 Agent 组件内部使用的事件处理结果常量 */
|
||||
|
||||
// 连接已关闭
|
||||
#define HR_CLOSED 0xFF
|
||||
|
||||
/* 命令类型 */
|
||||
enum EnDispCmdType
|
||||
{
|
||||
DISP_CMD_SEND = 0x01, // 发送数据
|
||||
DISP_CMD_RECEIVE = 0x02, // 接收数据
|
||||
DISP_CMD_UNPAUSE = 0x03, // 恢复接收数据
|
||||
DISP_CMD_DISCONNECT = 0x04, // 断开连接
|
||||
DISP_CMD_TIMEOUT = 0x05, // 保活超时
|
||||
};
|
||||
|
||||
/* 关闭连接标识 */
|
||||
enum EnSocketCloseFlag
|
||||
{
|
||||
SCF_NONE = 0, // 不触发事件
|
||||
SCF_CLOSE = 1, // 触发 正常关闭 OnClose 事件
|
||||
SCF_ERROR = 2 // 触发 异常关闭 OnClose 事件
|
||||
};
|
||||
|
||||
/* 监听 Socket 数组智能指针 */
|
||||
typedef unique_ptr<SOCKET[]> ListenSocketsPtr;
|
||||
|
||||
/* 数据缓冲节点 */
|
||||
typedef TItem TBufferObj;
|
||||
/* 数据缓冲节点智能指针 */
|
||||
typedef TItemPtr TBufferObjPtr;
|
||||
/* 数据缓冲区对象池 */
|
||||
typedef CItemPool CBufferObjPool;
|
||||
/* 数据缓冲区链表模板 */
|
||||
typedef TItemListExV TBufferObjList;
|
||||
|
||||
/* 接收缓冲区数组智能指针 */
|
||||
typedef unique_ptr<CBufferPtr[]> CReceiveBuffersPtr;
|
||||
|
||||
/* 线程 ID - 接收缓冲区哈希表 */
|
||||
typedef unordered_map<THR_ID, CBufferPtr*> TReceiveBufferMap;
|
||||
/* 线程 ID - 接收缓冲区哈希表迭代器 */
|
||||
typedef TReceiveBufferMap::iterator TReceiveBufferMapI;
|
||||
/* 线程 ID - 接收缓冲区哈希表 const 迭代器 */
|
||||
typedef TReceiveBufferMap::const_iterator TReceiveBufferMapCI;
|
||||
|
||||
/* Socket 缓冲区基础结构 */
|
||||
struct TSocketObjBase : public CSafeCounter
|
||||
{
|
||||
CPrivateHeap& heap;
|
||||
CReentrantCriSec csSend;
|
||||
TBufferObjList sndBuff;
|
||||
|
||||
|
||||
CONNID connID;
|
||||
HP_SOCKADDR remoteAddr;
|
||||
PVOID extra;
|
||||
PVOID reserved;
|
||||
PVOID reserved2;
|
||||
DWORD activeTime;
|
||||
|
||||
union
|
||||
{
|
||||
DWORD freeTime;
|
||||
DWORD connTime;
|
||||
};
|
||||
|
||||
volatile BOOL valid;
|
||||
volatile BOOL connected;
|
||||
volatile BOOL paused;
|
||||
|
||||
TSocketObjBase(CPrivateHeap& hp, CBufferObjPool& bfPool) : heap(hp), sndBuff(bfPool) {}
|
||||
|
||||
static BOOL IsExist(TSocketObjBase* pSocketObj)
|
||||
{return pSocketObj != nullptr;}
|
||||
|
||||
static BOOL IsValid(TSocketObjBase* pSocketObj)
|
||||
{return (IsExist(pSocketObj) && pSocketObj->valid == TRUE);}
|
||||
|
||||
static void Invalid(TSocketObjBase* pSocketObj)
|
||||
{ASSERT(IsExist(pSocketObj)); pSocketObj->valid = FALSE;}
|
||||
|
||||
static void Release(TSocketObjBase* pSocketObj)
|
||||
{
|
||||
ASSERT(IsExist(pSocketObj));
|
||||
|
||||
pSocketObj->freeTime = ::TimeGetTime();
|
||||
pSocketObj->sndBuff.Release();
|
||||
}
|
||||
|
||||
static BOOL InvalidSocketObj(TSocketObjBase* pSocketObj)
|
||||
{
|
||||
BOOL bDone = FALSE;
|
||||
|
||||
if(TSocketObjBase::IsValid(pSocketObj))
|
||||
{
|
||||
pSocketObj->SetConnected(FALSE);
|
||||
|
||||
CReentrantCriSecLock locallock(pSocketObj->csSend);
|
||||
|
||||
if(TSocketObjBase::IsValid(pSocketObj))
|
||||
{
|
||||
TSocketObjBase::Invalid(pSocketObj);
|
||||
bDone = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return bDone;
|
||||
}
|
||||
|
||||
DWORD GetConnTime () const {return connTime;}
|
||||
DWORD GetFreeTime () const {return freeTime;}
|
||||
DWORD GetActiveTime () const {return activeTime;}
|
||||
BOOL IsPaused () const {return paused;}
|
||||
|
||||
int Pending () const {return sndBuff.Length();}
|
||||
BOOL IsPending () const {return Pending() > 0;}
|
||||
|
||||
BOOL HasConnected() {return connected == TRUE;}
|
||||
BOOL IsConnecting() {return connected == CST_CONNECTING;}
|
||||
void SetConnected(BOOL bConnected = TRUE) {connected = bConnected;}
|
||||
|
||||
void Reset(CONNID dwConnID)
|
||||
{
|
||||
ResetCount();
|
||||
|
||||
connID = dwConnID;
|
||||
connected = FALSE;
|
||||
valid = TRUE;
|
||||
paused = FALSE;
|
||||
extra = nullptr;
|
||||
reserved = nullptr;
|
||||
reserved2 = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/* 数据缓冲区结构 */
|
||||
struct TSocketObj : public TSocketObjBase
|
||||
{
|
||||
using __super = TSocketObjBase;
|
||||
|
||||
SOCKET socket;
|
||||
|
||||
static TSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
{
|
||||
TSocketObj* pSocketObj = (TSocketObj*)hp.Alloc(sizeof(TSocketObj));
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
return new (pSocketObj) TSocketObj(hp, bfPool);
|
||||
}
|
||||
|
||||
static void Destruct(TSocketObj* pSocketObj)
|
||||
{
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
CPrivateHeap& heap = pSocketObj->heap;
|
||||
pSocketObj->TSocketObj::~TSocketObj();
|
||||
heap.Free(pSocketObj);
|
||||
}
|
||||
|
||||
TSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
: __super(hp, bfPool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Reset(CONNID dwConnID, SOCKET soClient)
|
||||
{
|
||||
__super::Reset(dwConnID);
|
||||
|
||||
socket = soClient;
|
||||
}
|
||||
};
|
||||
|
||||
/* Agent 数据缓冲区结构 */
|
||||
struct TAgentSocketObj : public TSocketObj
|
||||
{
|
||||
using __super = TSocketObj;
|
||||
|
||||
CStringA host;
|
||||
|
||||
static TAgentSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
{
|
||||
TAgentSocketObj* pSocketObj = (TAgentSocketObj*)hp.Alloc(sizeof(TAgentSocketObj));
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
return new (pSocketObj) TAgentSocketObj(hp, bfPool);
|
||||
}
|
||||
|
||||
static void Destruct(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
CPrivateHeap& heap = pSocketObj->heap;
|
||||
pSocketObj->TAgentSocketObj::~TAgentSocketObj();
|
||||
heap.Free(pSocketObj);
|
||||
}
|
||||
|
||||
TAgentSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
: __super(hp, bfPool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Reset(CONNID dwConnID, SOCKET soClient)
|
||||
{
|
||||
__super::Reset(dwConnID, soClient);
|
||||
|
||||
host.Empty();
|
||||
}
|
||||
|
||||
BOOL GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort = nullptr)
|
||||
{
|
||||
*lpszHost = host;
|
||||
|
||||
if(pusPort)
|
||||
*pusPort = remoteAddr.Port();
|
||||
|
||||
return (!host.IsEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
/* UDP 数据缓冲区结构 */
|
||||
struct TUdpSocketObj : public TSocketObjBase
|
||||
{
|
||||
using __super = TSocketObjBase;
|
||||
|
||||
int index;
|
||||
PVOID pHolder;
|
||||
FD fdTimer;
|
||||
|
||||
volatile DWORD detectFails;
|
||||
|
||||
static TUdpSocketObj* Construct(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
{
|
||||
TUdpSocketObj* pSocketObj = (TUdpSocketObj*)hp.Alloc(sizeof(TUdpSocketObj));
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
return new (pSocketObj) TUdpSocketObj(hp, bfPool);
|
||||
}
|
||||
|
||||
static void Destruct(TUdpSocketObj* pSocketObj)
|
||||
{
|
||||
ASSERT(pSocketObj);
|
||||
|
||||
CPrivateHeap& heap = pSocketObj->heap;
|
||||
pSocketObj->TUdpSocketObj::~TUdpSocketObj();
|
||||
heap.Free(pSocketObj);
|
||||
}
|
||||
|
||||
TUdpSocketObj(CPrivateHeap& hp, CBufferObjPool& bfPool)
|
||||
: __super(hp, bfPool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Reset(CONNID dwConnID)
|
||||
{
|
||||
__super::Reset(dwConnID);
|
||||
|
||||
index = -1;
|
||||
detectFails = 0;
|
||||
fdTimer = INVALID_FD;
|
||||
pHolder = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/* 有效 TSocketObj 缓存 */
|
||||
typedef CRingCache2<TSocketObj, CONNID, true> TSocketObjPtrPool;
|
||||
/* 失效 TSocketObj 缓存 */
|
||||
typedef CRingPool<TSocketObj> TSocketObjPtrList;
|
||||
/* 失效 TSocketObj 垃圾回收结构链表 */
|
||||
typedef CCASQueue<TSocketObj> TSocketObjPtrQueue;
|
||||
|
||||
/* 有效 TSocketObj 缓存 */
|
||||
typedef CRingCache2<TAgentSocketObj, CONNID, true> TAgentSocketObjPtrPool;
|
||||
/* 失效 TSocketObj 缓存 */
|
||||
typedef CRingPool<TAgentSocketObj> TAgentSocketObjPtrList;
|
||||
/* 失效 TSocketObj 垃圾回收结构链表 */
|
||||
typedef CCASQueue<TAgentSocketObj> TAgentSocketObjPtrQueue;
|
||||
|
||||
/* 有效 TUdpSocketObj 缓存 */
|
||||
typedef CRingCache2<TUdpSocketObj, CONNID, true> TUdpSocketObjPtrPool;
|
||||
/* 失效 TUdpSocketObj 缓存 */
|
||||
typedef CRingPool<TUdpSocketObj> TUdpSocketObjPtrList;
|
||||
/* 失效 TUdpSocketObj 垃圾回收结构链表 */
|
||||
typedef CCASQueue<TUdpSocketObj> TUdpSocketObjPtrQueue;
|
||||
|
||||
/* HP_SOCKADDR 比较器 */
|
||||
struct hp_sockaddr_func
|
||||
{
|
||||
struct hash
|
||||
{
|
||||
size_t operator() (const HP_SOCKADDR* pA) const
|
||||
{
|
||||
return pA->Hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct equal_to
|
||||
{
|
||||
bool operator () (const HP_SOCKADDR* pA, const HP_SOCKADDR* pB) const
|
||||
{
|
||||
return pA->EqualTo(*pB);
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/* 地址-连接 ID 哈希表 */
|
||||
typedef unordered_map<const HP_SOCKADDR*, CONNID, hp_sockaddr_func::hash, hp_sockaddr_func::equal_to>
|
||||
TSockAddrMap;
|
||||
/* 地址-连接 ID 哈希表迭代器 */
|
||||
typedef TSockAddrMap::iterator TSockAddrMapI;
|
||||
/* 地址-连接 ID 哈希表 const 迭代器 */
|
||||
typedef TSockAddrMap::const_iterator TSockAddrMapCI;
|
||||
|
||||
/* IClient 组件关闭上下文 */
|
||||
struct TClientCloseContext
|
||||
{
|
||||
BOOL bFireOnClose;
|
||||
EnSocketOperation enOperation;
|
||||
int iErrorCode;
|
||||
BOOL bNotify;
|
||||
|
||||
TClientCloseContext(BOOL bFire = TRUE, EnSocketOperation enOp = SO_CLOSE, int iCode = SE_OK, BOOL bNtf = TRUE)
|
||||
{
|
||||
Reset(bFire, enOp, iCode, bNtf);
|
||||
}
|
||||
|
||||
void Reset(BOOL bFire = TRUE, EnSocketOperation enOp = SO_CLOSE, int iCode = SE_OK, BOOL bNtf = TRUE)
|
||||
{
|
||||
bFireOnClose = bFire;
|
||||
enOperation = enOp;
|
||||
iErrorCode = iCode;
|
||||
bNotify = bNtf;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*****************************************************************************************************/
|
||||
/******************************************** 公共帮助方法 ********************************************/
|
||||
/*****************************************************************************************************/
|
||||
|
||||
/* 默认工作线程前缀 */
|
||||
#define DEFAULT_WORKER_THREAD_PREFIX "hp-worker-"
|
||||
|
||||
/* 设置当前工作线程名称 */
|
||||
BOOL SetCurrentWorkerThreadName();
|
||||
/* 设置工作线程默认名称 */
|
||||
BOOL SetWorkerThreadDefaultName(THR_ID tid);
|
||||
|
||||
/* 获取错误描述文本 */
|
||||
LPCTSTR GetSocketErrorDesc(EnSocketError enCode);
|
||||
/* 确定地址簇 */
|
||||
ADDRESS_FAMILY DetermineAddrFamily(LPCTSTR lpszAddress);
|
||||
/* 地址字符串地址转换为 HP_ADDR */
|
||||
BOOL GetInAddr(LPCTSTR lpszAddress, HP_ADDR& addr);
|
||||
/* 地址字符串地址转换为 HP_SOCKADDR */
|
||||
BOOL GetSockAddr(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr);
|
||||
/* 检查字符串是否符合 IP 地址格式 */
|
||||
BOOL IsIPAddress(LPCTSTR lpszAddress, EnIPAddrType* penType = nullptr);
|
||||
/* 通过主机名获取 IP 地址 */
|
||||
BOOL GetIPAddress(LPCTSTR lpszHost, LPTSTR lpszIP, int& iIPLenth, EnIPAddrType& enType);
|
||||
/* 通过主机名获取 HP_SOCKADDR */
|
||||
BOOL GetSockAddrByHostName(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR& addr);
|
||||
/* 通过主机名获取 HP_SOCKADDR */
|
||||
BOOL GetSockAddrByHostNameDirectly(LPCTSTR lpszHost, USHORT usPort, HP_SOCKADDR &addr);
|
||||
/* 枚举主机 IP 地址 */
|
||||
BOOL EnumHostIPAddresses(LPCTSTR lpszHost, EnIPAddrType enType, LPTIPAddr** lpppIPAddr, int& iIPAddrCount);
|
||||
/* 填充 LPTIPAddr* */
|
||||
BOOL RetrieveSockAddrIPAddresses(const vector<HP_PSOCKADDR>& vt, LPTIPAddr** lpppIPAddr, int& iIPAddrCount);
|
||||
/* 释放 LPTIPAddr* */
|
||||
BOOL FreeHostIPAddresses(LPTIPAddr* lppIPAddr);
|
||||
/* 把 HP_SOCKADDR 结构转换为地址字符串 */
|
||||
BOOL sockaddr_IN_2_A(const HP_SOCKADDR& addr, ADDRESS_FAMILY& usFamily, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
|
||||
/* 把地址字符串转换为 HP_SOCKADDR 结构 */
|
||||
BOOL sockaddr_A_2_IN(LPCTSTR lpszAddress, USHORT usPort, HP_SOCKADDR& addr);
|
||||
/* 获取 Socket 的本地或远程地址信息 */
|
||||
BOOL GetSocketAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort, BOOL bLocal = TRUE);
|
||||
/* 获取 Socket 的本地地址信息 */
|
||||
BOOL GetSocketLocalAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
|
||||
/* 获取 Socket 的远程地址信息 */
|
||||
BOOL GetSocketRemoteAddress(SOCKET socket, LPTSTR lpszAddress, int& iAddressLen, USHORT& usPort);
|
||||
/* 设置组播选项 */
|
||||
BOOL SetMultiCastSocketOptions(SOCKET sock, const HP_SOCKADDR& bindAddr, const HP_SOCKADDR& castAddr, int iMCTtl, BOOL bMCLoop);
|
||||
/* 等待连接 */
|
||||
int WaitForSocketWrite(SOCKET sock, DWORD dwTimeout);
|
||||
|
||||
/* 64 位网络字节序转主机字节序 */
|
||||
ULONGLONG NToH64(ULONGLONG value);
|
||||
/* 64 位主机字节序转网络字节序 */
|
||||
ULONGLONG HToN64(ULONGLONG value);
|
||||
|
||||
/* 短整型高低字节交换 */
|
||||
#define ENDIAN_SWAP_16(A) ((USHORT)((((USHORT)(A) & 0xff00) >> 8) | (((USHORT)(A) & 0x00ff) << 8)))
|
||||
/* 长整型高低字节交换 */
|
||||
#define ENDIAN_SWAP_32(A) ((((DWORD)(A) & 0xff000000) >> 24) | \
|
||||
(((DWORD)(A) & 0x00ff0000) >> 8) | \
|
||||
(((DWORD)(A) & 0x0000ff00) << 8) | \
|
||||
(((DWORD)(A) & 0x000000ff) << 24) )
|
||||
|
||||
/* 检查是否小端字节序 */
|
||||
BOOL IsLittleEndian();
|
||||
/* 短整型主机字节序转小端字节序 */
|
||||
USHORT HToLE16(USHORT value);
|
||||
/* 短整型主机字节序转大端字节序 */
|
||||
USHORT HToBE16(USHORT value);
|
||||
/* 长整型主机字节序转小端字节序 */
|
||||
DWORD HToLE32(DWORD value);
|
||||
/* 长整型主机字节序转大端字节序 */
|
||||
DWORD HToBE32(DWORD value);
|
||||
|
||||
HRESULT ReadSmallFile(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, DWORD dwMaxFileSize = MAX_SMALL_FILE_SIZE);
|
||||
HRESULT MakeSmallFilePackage(LPCTSTR lpszFileName, CFile& file, CFileMapping& fmap, WSABUF szBuf[3], const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
|
||||
|
||||
/************************************************************************
|
||||
名称:setsockopt() 帮助方法
|
||||
描述:简化常用的 setsockopt() 调用
|
||||
************************************************************************/
|
||||
|
||||
int SSO_SetSocketOption (SOCKET sock, int level, int name, LPVOID val, int len);
|
||||
int SSO_GetSocketOption (SOCKET sock, int level, int name, LPVOID val, int* len);
|
||||
int SSO_IoctlSocket (SOCKET sock, long cmd, PVOID arg);
|
||||
|
||||
int SSO_NoBlock (SOCKET sock, BOOL bNoBlock = TRUE);
|
||||
int SSO_NoDelay (SOCKET sock, BOOL bNoDelay = TRUE);
|
||||
int SSO_DontLinger (SOCKET sock, BOOL bDont = TRUE);
|
||||
int SSO_Linger (SOCKET sock, int l_onoff, int l_linger);
|
||||
int SSO_KeepAlive (SOCKET sock, BOOL bKeepAlive = TRUE);
|
||||
int SSO_KeepAliveVals (SOCKET sock, BOOL bOnOff, DWORD dwIdle, DWORD dwInterval, DWORD dwCount = 5);
|
||||
int SSO_ReuseAddress (SOCKET sock, EnReuseAddressPolicy opt);
|
||||
int SSO_RecvBuffSize (SOCKET sock, int size);
|
||||
int SSO_SendBuffSize (SOCKET sock, int size);
|
||||
int SSO_RecvTimeOut (SOCKET sock, int ms);
|
||||
int SSO_SendTimeOut (SOCKET sock, int ms);
|
||||
int SSO_GetError (SOCKET sock);
|
||||
|
||||
/* 生成 Connection ID */
|
||||
CONNID GenerateConnectionID();
|
||||
/* 检测 UDP 连接关闭通知 */
|
||||
int IsUdpCloseNotify(const BYTE* pData, int iLength);
|
||||
/* 发送 UDP 连接关闭通知 */
|
||||
int SendUdpCloseNotify(SOCKET sock);
|
||||
/* 发送 UDP 连接关闭通知 */
|
||||
int SendUdpCloseNotify(SOCKET sock, const HP_SOCKADDR& remoteAddr);
|
||||
/* 关闭 Socket */
|
||||
int ManualCloseSocket(SOCKET sock, int iShutdownFlag = 0xFF, BOOL bGraceful = TRUE);
|
||||
|
||||
#ifdef _ICONV_SUPPORT
|
||||
|
||||
#define CHARSET_GBK "GBK"
|
||||
#define CHARSET_UTF_8 "UTF-8"
|
||||
#define CHARSET_UTF_16LE "UTF-16LE"
|
||||
#define CHARSET_UTF_32LE "UTF-32LE"
|
||||
#define CHARSET_UTF_16BE "UTF-16BE"
|
||||
#define CHARSET_UTF_32BE "UTF-32BE"
|
||||
|
||||
// 系统 UNICODE 字符集
|
||||
#define SYSTEM_CHARSET_UNICODE ( (sizeof(WCHAR) == 4) \
|
||||
? (IsLittleEndian() ? CHARSET_UTF_32LE : CHARSET_UTF_32BE) \
|
||||
: (IsLittleEndian() ? CHARSET_UTF_16LE : CHARSET_UTF_16BE) )
|
||||
|
||||
// Charset A -> Charset B
|
||||
BOOL CharsetConvert(LPCSTR lpszFromCharset, LPCSTR lpszToCharset, LPCSTR lpszInBuf, int iInBufLen, LPSTR lpszOutBuf, int& iOutBufLen);
|
||||
|
||||
// GBK -> UNICODE
|
||||
BOOL GbkToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength);
|
||||
// UNICODE -> GBK
|
||||
BOOL UnicodeToGbkEx(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength);
|
||||
// UTF8 -> UNICODE
|
||||
BOOL Utf8ToUnicodeEx(const char szSrc[], int iSrcLength, WCHAR szDest[], int& iDestLength);
|
||||
// UNICODE -> UTF8
|
||||
BOOL UnicodeToUtf8Ex(const WCHAR szSrc[], int iSrcLength, char szDest[], int& iDestLength);
|
||||
// GBK -> UTF8
|
||||
BOOL GbkToUtf8Ex(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength);
|
||||
// UTF8 -> GBK
|
||||
BOOL Utf8ToGbkEx(const char szSrc[], int iSrcLength, char szDest[], int& iDestLength);
|
||||
|
||||
// GBK -> UNICODE
|
||||
BOOL GbkToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength);
|
||||
// UNICODE -> GBK
|
||||
BOOL UnicodeToGbk(const WCHAR szSrc[], char szDest[], int& iDestLength);
|
||||
// UTF8 -> UNICODE
|
||||
BOOL Utf8ToUnicode(const char szSrc[], WCHAR szDest[], int& iDestLength);
|
||||
// UNICODE -> UTF8
|
||||
BOOL UnicodeToUtf8(const WCHAR szSrc[], char szDest[], int& iDestLength);
|
||||
// GBK -> UTF8
|
||||
BOOL GbkToUtf8(const char szSrc[], char szDest[], int& iDestLength);
|
||||
// UTF8 -> GBK
|
||||
BOOL Utf8ToGbk(const char szSrc[], char szDest[], int& iDestLength);
|
||||
|
||||
#endif
|
||||
|
||||
// 计算 Base64 编码后长度
|
||||
DWORD GuessBase64EncodeBound(DWORD dwSrcLen);
|
||||
// 计算 Base64 解码后长度
|
||||
DWORD GuessBase64DecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
|
||||
// Base64 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int Base64Encode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// Base64 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int Base64Decode(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
|
||||
// 计算 URL 编码后长度
|
||||
DWORD GuessUrlEncodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
|
||||
// 计算 URL 解码后长度
|
||||
DWORD GuessUrlDecodeBound(const BYTE* lpszSrc, DWORD dwSrcLen);
|
||||
// URL 编码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int UrlEncode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// URL 解码(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int UrlDecode(BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
|
||||
/* 销毁压缩器对象 */
|
||||
void DestroyCompressor(IHPCompressor* pCompressor);
|
||||
/* 销毁解压器对象 */
|
||||
void DestroyDecompressor(IHPDecompressor* pDecompressor);
|
||||
|
||||
#ifdef _ZLIB_SUPPORT
|
||||
|
||||
/* ZLib 压缩器 */
|
||||
class CHPZLibCompressor : public IHPCompressor
|
||||
{
|
||||
public:
|
||||
virtual BOOL Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext = nullptr);
|
||||
virtual BOOL ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush = FALSE, PVOID pContext = nullptr);
|
||||
virtual BOOL IsValid() {return m_bValid;}
|
||||
virtual BOOL Reset();
|
||||
|
||||
public:
|
||||
CHPZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
virtual ~CHPZLibCompressor();
|
||||
|
||||
private:
|
||||
Fn_CompressDataCallback m_fnCallback;
|
||||
z_stream m_Stream;
|
||||
BOOL m_bValid;
|
||||
DWORD m_dwBuffSize;
|
||||
};
|
||||
|
||||
/* ZLib 解压器 */
|
||||
class CHPZLibDecompressor : public IHPDecompressor
|
||||
{
|
||||
public:
|
||||
virtual BOOL Process(const BYTE* pData, int iLength, PVOID pContext = nullptr);
|
||||
virtual BOOL IsValid() {return m_bValid;}
|
||||
virtual BOOL Reset();
|
||||
|
||||
public:
|
||||
CHPZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
virtual ~CHPZLibDecompressor();
|
||||
|
||||
private:
|
||||
Fn_DecompressDataCallback m_fnCallback;
|
||||
z_stream m_Stream;
|
||||
BOOL m_bValid;
|
||||
DWORD m_dwBuffSize;
|
||||
};
|
||||
|
||||
/* 创建 ZLib 压缩器对象 */
|
||||
IHPCompressor* CreateZLibCompressor(Fn_CompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
/* 创建 GZip 压缩器对象 */
|
||||
IHPCompressor* CreateGZipCompressor(Fn_CompressDataCallback fnCallback, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
/* 创建 ZLib 解压器对象 */
|
||||
IHPDecompressor* CreateZLibDecompressor(Fn_DecompressDataCallback fnCallback, int iWindowBits = MAX_WBITS, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
/* 创建 GZip 解压器对象 */
|
||||
IHPDecompressor* CreateGZipDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
|
||||
// 普通压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int Compress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int CompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iLevel = Z_DEFAULT_COMPRESSION, int iMethod = Z_DEFLATED, int iWindowBits = MAX_WBITS, int iMemLevel = MAX_MEM_LEVEL, int iStrategy = Z_DEFAULT_STRATEGY);
|
||||
// 普通解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int Uncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// 高级解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int UncompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iWindowBits = MAX_WBITS);
|
||||
// 推测压缩结果长度
|
||||
DWORD GuessCompressBound(DWORD dwSrcLen, BOOL bGZip = FALSE);
|
||||
|
||||
// Gzip 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int GZipCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// Gzip 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int GZipUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// 推测 Gzip 解压结果长度(如果返回 0 或不合理值则说明输入内容并非有效的 Gzip 格式)
|
||||
DWORD GZipGuessUncompressBound(const BYTE* lpszSrc, DWORD dwSrcLen);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _BROTLI_SUPPORT
|
||||
|
||||
/* Brotli 压缩器 */
|
||||
class CHPBrotliCompressor : public IHPCompressor
|
||||
{
|
||||
public:
|
||||
virtual BOOL Process(const BYTE* pData, int iLength, BOOL bLast, PVOID pContext = nullptr);
|
||||
virtual BOOL ProcessEx(const BYTE* pData, int iLength, BOOL bLast, BOOL bFlush = FALSE, PVOID pContext = nullptr);
|
||||
virtual BOOL IsValid() {return m_bValid;}
|
||||
virtual BOOL Reset();
|
||||
|
||||
public:
|
||||
CHPBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
virtual ~CHPBrotliCompressor();
|
||||
|
||||
private:
|
||||
Fn_CompressDataCallback m_fnCallback;
|
||||
BrotliEncoderState* m_pState;
|
||||
BOOL m_bValid;
|
||||
|
||||
int m_iQuality;
|
||||
int m_iWindow;
|
||||
int m_iMode;
|
||||
DWORD m_dwBuffSize;
|
||||
};
|
||||
|
||||
/* Brotli 解压器 */
|
||||
class CHPBrotliDecompressor : public IHPDecompressor
|
||||
{
|
||||
public:
|
||||
virtual BOOL Process(const BYTE* pData, int iLength, PVOID pContext = nullptr);
|
||||
virtual BOOL IsValid() {return m_bValid;}
|
||||
virtual BOOL Reset();
|
||||
|
||||
public:
|
||||
CHPBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
virtual ~CHPBrotliDecompressor();
|
||||
|
||||
private:
|
||||
Fn_DecompressDataCallback m_fnCallback;
|
||||
BrotliDecoderState* m_pState;
|
||||
BOOL m_bValid;
|
||||
DWORD m_dwBuffSize;
|
||||
};
|
||||
|
||||
/* 创建 Brotli 压缩器对象 */
|
||||
IHPCompressor* CreateBrotliCompressor(Fn_CompressDataCallback fnCallback, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
/* 创建 Brotli 解压器对象 */
|
||||
IHPDecompressor* CreateBrotliDecompressor(Fn_DecompressDataCallback fnCallback, DWORD dwBuffSize = DEFAULT_COMPRESS_BUFFER_SIZE);
|
||||
|
||||
// Brotli 压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int BrotliCompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// Brotli 高级压缩(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int BrotliCompressEx(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen, int iQuality = BROTLI_DEFAULT_QUALITY, int iWindow = BROTLI_DEFAULT_WINDOW, int iMode = BROTLI_DEFAULT_MODE);
|
||||
// Brotli 解压(返回值:0 -> 成功,-3 -> 输入数据不正确,-5 -> 输出缓冲区不足)
|
||||
int BrotliUncompress(const BYTE* lpszSrc, DWORD dwSrcLen, BYTE* lpszDest, DWORD& dwDestLen);
|
||||
// Brotli 推测压缩结果长度
|
||||
DWORD BrotliGuessCompressBound(DWORD dwSrcLen);
|
||||
|
||||
#endif
|
||||
770
SocketObject4C.h
Normal file
770
SocketObject4C.h
Normal file
@@ -0,0 +1,770 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/HPSocket4C.h"
|
||||
#include "hpsocket/SocketInterface.h"
|
||||
#include "common/FuncHelper.h"
|
||||
|
||||
class C_HP_Object
|
||||
{
|
||||
public:
|
||||
template<class T> static inline HP_Object FromFirst(T* pFirst)
|
||||
{
|
||||
return (HP_Object)((char*)pFirst - first);
|
||||
}
|
||||
|
||||
template<class T> static inline T* ToFirst(HP_Object pObject)
|
||||
{
|
||||
return (T*)((char*)pObject + first);
|
||||
}
|
||||
|
||||
template<size_t offset, class T> static inline HP_Object FromSecond(T* pSecond)
|
||||
{
|
||||
return (C_HP_Object*)((char*)pSecond - first - (offset + __DUAL_VPTR_GAP__));
|
||||
}
|
||||
|
||||
template<class T> static inline T* ToSecond(HP_Object pObject)
|
||||
{
|
||||
return (T*)((char*)pObject + ((C_HP_Object*)pObject)->second);
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_Object(int offset = 0) : second(first + (offset + __DUAL_VPTR_GAP__)) {}
|
||||
virtual ~C_HP_Object() {}
|
||||
|
||||
private:
|
||||
static const size_t first = (sizeof(PVOID) + sizeof(size_t));
|
||||
size_t second;
|
||||
};
|
||||
|
||||
template<class T, class L, int offset = 0> class C_HP_ObjectT : private C_HP_Object, public T
|
||||
{
|
||||
public:
|
||||
C_HP_ObjectT(L* pListener) : C_HP_Object(offset), T(pListener) {}
|
||||
};
|
||||
|
||||
template<class T, class L, size_t offset = 0> class C_HP_ServerListenerT : public L
|
||||
{
|
||||
public:
|
||||
virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen)
|
||||
{
|
||||
return (m_fnOnPrepareListen)
|
||||
? m_fnOnPrepareListen(C_HP_Object::FromSecond<offset>(pSender), soListen)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnAccept(T* pSender, CONNID dwConnID, UINT_PTR soClient)
|
||||
{
|
||||
return (m_fnOnAccept)
|
||||
? m_fnOnAccept(C_HP_Object::FromSecond<offset>(pSender), dwConnID, soClient)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnHandShake)
|
||||
? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
return (m_fnOnSend)
|
||||
? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnReceive);
|
||||
|
||||
return (m_fnOnReceive)
|
||||
? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnPullReceive);
|
||||
|
||||
return (m_fnOnPullReceive)
|
||||
? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
ASSERT(m_fnOnClose);
|
||||
|
||||
return (m_fnOnClose)
|
||||
? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnShutdown(T* pSender)
|
||||
{
|
||||
return (m_fnOnShutdown)
|
||||
? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_ServerListenerT()
|
||||
: m_fnOnPrepareListen (nullptr)
|
||||
, m_fnOnAccept (nullptr)
|
||||
, m_fnOnHandShake (nullptr)
|
||||
, m_fnOnSend (nullptr)
|
||||
, m_fnOnReceive (nullptr)
|
||||
, m_fnOnPullReceive (nullptr)
|
||||
, m_fnOnClose (nullptr)
|
||||
, m_fnOnShutdown (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_Server_OnPrepareListen m_fnOnPrepareListen ;
|
||||
HP_FN_Server_OnAccept m_fnOnAccept ;
|
||||
HP_FN_Server_OnHandShake m_fnOnHandShake ;
|
||||
HP_FN_Server_OnSend m_fnOnSend ;
|
||||
HP_FN_Server_OnReceive m_fnOnReceive ;
|
||||
HP_FN_Server_OnPullReceive m_fnOnPullReceive ;
|
||||
HP_FN_Server_OnClose m_fnOnClose ;
|
||||
HP_FN_Server_OnShutdown m_fnOnShutdown ;
|
||||
};
|
||||
|
||||
template<class T, class L, size_t offset = 0> class C_HP_AgentListenerT : public L
|
||||
{
|
||||
public:
|
||||
virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket)
|
||||
{
|
||||
return (m_fnOnPrepareConnect)
|
||||
? m_fnOnPrepareConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID, socket)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnConnect)
|
||||
? m_fnOnConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnHandShake)
|
||||
? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
return (m_fnOnSend)
|
||||
? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnReceive);
|
||||
|
||||
return (m_fnOnReceive)
|
||||
? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnPullReceive);
|
||||
|
||||
return (m_fnOnPullReceive)
|
||||
? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
ASSERT(m_fnOnClose);
|
||||
|
||||
return (m_fnOnClose)
|
||||
? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnShutdown(T* pSender)
|
||||
{
|
||||
return (m_fnOnShutdown)
|
||||
? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_AgentListenerT()
|
||||
: m_fnOnPrepareConnect (nullptr)
|
||||
, m_fnOnConnect (nullptr)
|
||||
, m_fnOnHandShake (nullptr)
|
||||
, m_fnOnSend (nullptr)
|
||||
, m_fnOnReceive (nullptr)
|
||||
, m_fnOnPullReceive (nullptr)
|
||||
, m_fnOnClose (nullptr)
|
||||
, m_fnOnShutdown (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_Agent_OnPrepareConnect m_fnOnPrepareConnect;
|
||||
HP_FN_Agent_OnConnect m_fnOnConnect ;
|
||||
HP_FN_Agent_OnHandShake m_fnOnHandShake ;
|
||||
HP_FN_Agent_OnSend m_fnOnSend ;
|
||||
HP_FN_Agent_OnReceive m_fnOnReceive ;
|
||||
HP_FN_Agent_OnPullReceive m_fnOnPullReceive ;
|
||||
HP_FN_Agent_OnClose m_fnOnClose ;
|
||||
HP_FN_Agent_OnShutdown m_fnOnShutdown ;
|
||||
};
|
||||
|
||||
template<class T, class L, size_t offset = 0> class C_HP_ClientListenerT : public L
|
||||
{
|
||||
public:
|
||||
virtual EnHandleResult OnPrepareConnect(T* pSender, CONNID dwConnID, SOCKET socket)
|
||||
{
|
||||
return (m_fnOnPrepareConnect)
|
||||
? m_fnOnPrepareConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID, socket)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnConnect(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnConnect)
|
||||
? m_fnOnConnect(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnHandShake(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnHandShake)
|
||||
? m_fnOnHandShake(C_HP_Object::FromSecond<offset>(pSender), dwConnID)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnSend(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
return (m_fnOnSend)
|
||||
? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnReceive);
|
||||
|
||||
return (m_fnOnReceive)
|
||||
? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, CONNID dwConnID, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnPullReceive);
|
||||
|
||||
return (m_fnOnPullReceive)
|
||||
? m_fnOnPullReceive(C_HP_Object::FromSecond<offset>(pSender), dwConnID, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnClose(T* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
ASSERT(m_fnOnClose);
|
||||
|
||||
return (m_fnOnClose)
|
||||
? m_fnOnClose(C_HP_Object::FromSecond<offset>(pSender), dwConnID, enOperation, iErrorCode)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_ClientListenerT()
|
||||
: m_fnOnPrepareConnect (nullptr)
|
||||
, m_fnOnConnect (nullptr)
|
||||
, m_fnOnHandShake (nullptr)
|
||||
, m_fnOnSend (nullptr)
|
||||
, m_fnOnReceive (nullptr)
|
||||
, m_fnOnPullReceive (nullptr)
|
||||
, m_fnOnClose (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_Client_OnPrepareConnect m_fnOnPrepareConnect;
|
||||
HP_FN_Client_OnConnect m_fnOnConnect ;
|
||||
HP_FN_Client_OnHandShake m_fnOnHandShake ;
|
||||
HP_FN_Client_OnSend m_fnOnSend ;
|
||||
HP_FN_Client_OnReceive m_fnOnReceive ;
|
||||
HP_FN_Client_OnPullReceive m_fnOnPullReceive ;
|
||||
HP_FN_Client_OnClose m_fnOnClose ;
|
||||
};
|
||||
|
||||
typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener> C_HP_TcpServerListener;
|
||||
typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IPullSocket)> C_HP_TcpPullServerListener;
|
||||
typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IPackSocket)> C_HP_TcpPackServerListener;
|
||||
|
||||
typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener> C_HP_TcpAgentListener;
|
||||
typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IPullSocket)> C_HP_TcpPullAgentListener;
|
||||
typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IPackSocket)> C_HP_TcpPackAgentListener;
|
||||
|
||||
typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener> C_HP_TcpClientListener;
|
||||
typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IPullClient)> C_HP_TcpPullClientListener;
|
||||
typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IPackClient)> C_HP_TcpPackClientListener;
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
template<class T, class L, size_t offset = 0> class C_HP_UdpNodeListenerT : public L
|
||||
{
|
||||
public:
|
||||
virtual EnHandleResult OnPrepareListen(T* pSender, SOCKET soListen)
|
||||
{
|
||||
return (m_fnOnPrepareListen)
|
||||
? m_fnOnPrepareListen(C_HP_Object::FromSecond<offset>(pSender), soListen)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnSend(T* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
|
||||
{
|
||||
return (m_fnOnSend)
|
||||
? m_fnOnSend(C_HP_Object::FromSecond<offset>(pSender), lpszRemoteAddress, usRemotePort, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnReceive(T* pSender, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnReceive);
|
||||
|
||||
return (m_fnOnReceive)
|
||||
? m_fnOnReceive(C_HP_Object::FromSecond<offset>(pSender), lpszRemoteAddress, usRemotePort, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnError(T* pSender, EnSocketOperation enOperation, int iErrorCode, LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnError);
|
||||
|
||||
return (m_fnOnError)
|
||||
? m_fnOnError(C_HP_Object::FromSecond<offset>(pSender), enOperation, iErrorCode, lpszRemoteAddress, usRemotePort, pData, iLength)
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnShutdown(T* pSender)
|
||||
{
|
||||
return (m_fnOnShutdown)
|
||||
? m_fnOnShutdown(C_HP_Object::FromSecond<offset>(pSender))
|
||||
: HR_IGNORE;
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_UdpNodeListenerT()
|
||||
: m_fnOnPrepareListen (nullptr)
|
||||
, m_fnOnSend (nullptr)
|
||||
, m_fnOnReceive (nullptr)
|
||||
, m_fnOnError (nullptr)
|
||||
, m_fnOnShutdown (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_UdpNode_OnPrepareListen m_fnOnPrepareListen ;
|
||||
HP_FN_UdpNode_OnSend m_fnOnSend ;
|
||||
HP_FN_UdpNode_OnReceive m_fnOnReceive ;
|
||||
HP_FN_UdpNode_OnError m_fnOnError ;
|
||||
HP_FN_UdpNode_OnShutdown m_fnOnShutdown ;
|
||||
};
|
||||
|
||||
typedef C_HP_ServerListenerT<IUdpServer, IUdpServerListener> C_HP_UdpServerListener;
|
||||
typedef C_HP_ClientListenerT<IUdpClient, IUdpClientListener> C_HP_UdpClientListener;
|
||||
typedef C_HP_ClientListenerT<IUdpCast, IUdpCastListener> C_HP_UdpCastListener;
|
||||
typedef C_HP_UdpNodeListenerT<IUdpNode, IUdpNodeListener> C_HP_UdpNodeListener;
|
||||
|
||||
typedef C_HP_ServerListenerT<IUdpServer, IUdpServerListener, sizeof(IArqSocket)> C_HP_UdpArqServerListener;
|
||||
typedef C_HP_ClientListenerT<IUdpClient, IUdpClientListener, sizeof(IArqClient)> C_HP_UdpArqClientListener;
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _HTTP_SUPPORT
|
||||
|
||||
template<class T> class C_HP_HttpListenerT : public IHttpListenerT<T>
|
||||
{
|
||||
public:
|
||||
virtual EnHttpParseResult OnMessageBegin(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnMessageBegin)
|
||||
? m_fnOnMessageBegin(C_HP_Object::FromFirst(pSender), dwConnID)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnRequestLine(T* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{
|
||||
return (m_fnOnRequestLine)
|
||||
? m_fnOnRequestLine(C_HP_Object::FromFirst(pSender), dwConnID, lpszMethod, lpszUrl)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnStatusLine(T* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{
|
||||
return (m_fnOnStatusLine)
|
||||
? m_fnOnStatusLine(C_HP_Object::FromFirst(pSender), dwConnID, usStatusCode, lpszDesc)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnHeader(T* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{
|
||||
return (m_fnOnHeader)
|
||||
? m_fnOnHeader(C_HP_Object::FromFirst(pSender), dwConnID, lpszName, lpszValue)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnHeadersComplete(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
ASSERT(m_fnOnHeadersComplete);
|
||||
|
||||
return (m_fnOnHeadersComplete)
|
||||
? m_fnOnHeadersComplete(C_HP_Object::FromFirst(pSender), dwConnID)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
ASSERT(m_fnOnBody);
|
||||
|
||||
return (m_fnOnBody)
|
||||
? m_fnOnBody(C_HP_Object::FromFirst(pSender), dwConnID, pData, iLength)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnChunkHeader(T* pSender, CONNID dwConnID, int iLength)
|
||||
{
|
||||
return (m_fnOnChunkHeader)
|
||||
? m_fnOnChunkHeader(C_HP_Object::FromFirst(pSender), dwConnID, iLength)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnChunkComplete(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnChunkComplete)
|
||||
? m_fnOnChunkComplete(C_HP_Object::FromFirst(pSender), dwConnID)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnMessageComplete(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
ASSERT(m_fnOnMessageComplete);
|
||||
|
||||
return (m_fnOnMessageComplete)
|
||||
? m_fnOnMessageComplete(C_HP_Object::FromFirst(pSender), dwConnID)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnUpgrade(T* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
|
||||
{
|
||||
return (m_fnOnUpgrade)
|
||||
? m_fnOnUpgrade(C_HP_Object::FromFirst(pSender), dwConnID, enUpgradeType)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHttpParseResult OnParseError(T* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{
|
||||
ASSERT(m_fnOnParseError);
|
||||
|
||||
return (m_fnOnParseError)
|
||||
? m_fnOnParseError(C_HP_Object::FromFirst(pSender), dwConnID, iErrorCode, lpszErrorDesc)
|
||||
: HPR_OK;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnWSMessageHeader(T* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{
|
||||
return (m_fnOnWSMessageHeader)
|
||||
? m_fnOnWSMessageHeader(C_HP_Object::FromFirst(pSender), dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen)
|
||||
: HR_OK;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnWSMessageBody(T* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{
|
||||
return (m_fnOnWSMessageBody)
|
||||
? m_fnOnWSMessageBody(C_HP_Object::FromFirst(pSender), dwConnID, pData, iLength)
|
||||
: HR_OK;
|
||||
}
|
||||
|
||||
virtual EnHandleResult OnWSMessageComplete(T* pSender, CONNID dwConnID)
|
||||
{
|
||||
return (m_fnOnWSMessageComplete)
|
||||
? m_fnOnWSMessageComplete(C_HP_Object::FromFirst(pSender), dwConnID)
|
||||
: HR_OK;
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_HttpListenerT()
|
||||
: m_fnOnMessageBegin (nullptr)
|
||||
, m_fnOnRequestLine (nullptr)
|
||||
, m_fnOnStatusLine (nullptr)
|
||||
, m_fnOnHeader (nullptr)
|
||||
, m_fnOnHeadersComplete (nullptr)
|
||||
, m_fnOnBody (nullptr)
|
||||
, m_fnOnChunkHeader (nullptr)
|
||||
, m_fnOnChunkComplete (nullptr)
|
||||
, m_fnOnMessageComplete (nullptr)
|
||||
, m_fnOnUpgrade (nullptr)
|
||||
, m_fnOnParseError (nullptr)
|
||||
, m_fnOnWSMessageHeader (nullptr)
|
||||
, m_fnOnWSMessageBody (nullptr)
|
||||
, m_fnOnWSMessageComplete(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_Http_OnMessageBegin m_fnOnMessageBegin ;
|
||||
HP_FN_Http_OnRequestLine m_fnOnRequestLine ;
|
||||
HP_FN_Http_OnStatusLine m_fnOnStatusLine ;
|
||||
HP_FN_Http_OnHeader m_fnOnHeader ;
|
||||
HP_FN_Http_OnHeadersComplete m_fnOnHeadersComplete ;
|
||||
HP_FN_Http_OnBody m_fnOnBody ;
|
||||
HP_FN_Http_OnChunkHeader m_fnOnChunkHeader ;
|
||||
HP_FN_Http_OnChunkComplete m_fnOnChunkComplete ;
|
||||
HP_FN_Http_OnMessageComplete m_fnOnMessageComplete ;
|
||||
HP_FN_Http_OnUpgrade m_fnOnUpgrade ;
|
||||
HP_FN_Http_OnParseError m_fnOnParseError ;
|
||||
HP_FN_Http_OnWSMessageHeader m_fnOnWSMessageHeader ;
|
||||
HP_FN_Http_OnWSMessageBody m_fnOnWSMessageBody ;
|
||||
HP_FN_Http_OnWSMessageComplete m_fnOnWSMessageComplete ;
|
||||
};
|
||||
|
||||
typedef C_HP_HttpListenerT<IHttpServer> C_HP_HttpServerBaseListener1;
|
||||
typedef C_HP_HttpListenerT<IHttpAgent> C_HP_HttpAgentBaseListener1;
|
||||
typedef C_HP_HttpListenerT<IHttpClient> C_HP_HttpClientBaseListener1;
|
||||
|
||||
typedef C_HP_ServerListenerT<ITcpServer, ITcpServerListener, sizeof(IComplexHttpResponder)> C_HP_HttpServerBaseListener2;
|
||||
typedef C_HP_AgentListenerT<ITcpAgent, ITcpAgentListener, sizeof(IComplexHttpRequester)> C_HP_HttpAgentBaseListener2;
|
||||
typedef C_HP_ClientListenerT<ITcpClient, ITcpClientListener, sizeof(IHttpRequester)> C_HP_HttpClientBaseListener2;
|
||||
|
||||
class C_HP_HttpServerListener : public IHttpServerListener
|
||||
{
|
||||
public:
|
||||
virtual EnHttpParseResult OnMessageBegin(IHttpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnRequestLine(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
|
||||
virtual EnHttpParseResult OnStatusLine(IHttpServer* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
|
||||
virtual EnHttpParseResult OnHeader(IHttpServer* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
|
||||
virtual EnHttpParseResult OnHeadersComplete(IHttpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnBody(IHttpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHttpParseResult OnChunkHeader(IHttpServer* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
|
||||
virtual EnHttpParseResult OnChunkComplete(IHttpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnMessageComplete(IHttpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnUpgrade(IHttpServer* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
|
||||
virtual EnHttpParseResult OnParseError(IHttpServer* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
|
||||
|
||||
virtual EnHandleResult OnWSMessageHeader(IHttpServer* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
virtual EnHandleResult OnWSMessageBody(IHttpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnWSMessageComplete(IHttpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
|
||||
|
||||
virtual EnHandleResult OnPrepareListen(ITcpServer* pSender, SOCKET soListen)
|
||||
{return m_lsnServer.OnPrepareListen(pSender, soListen);}
|
||||
virtual EnHandleResult OnAccept(ITcpServer* pSender, CONNID dwConnID, UINT_PTR soClient)
|
||||
{return m_lsnServer.OnAccept(pSender, dwConnID, soClient);}
|
||||
virtual EnHandleResult OnHandShake(ITcpServer* pSender, CONNID dwConnID)
|
||||
{return m_lsnServer.OnHandShake(pSender, dwConnID);}
|
||||
virtual EnHandleResult OnSend(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnServer.OnSend(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnServer.OnReceive(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpServer* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnServer.OnReceive(pSender, dwConnID, iLength);}
|
||||
virtual EnHandleResult OnClose(ITcpServer* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_lsnServer.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult OnShutdown(ITcpServer* pSender)
|
||||
{return m_lsnServer.OnShutdown(pSender);}
|
||||
|
||||
public:
|
||||
C_HP_HttpServerBaseListener1 m_lsnHttp;
|
||||
C_HP_HttpServerBaseListener2 m_lsnServer;
|
||||
};
|
||||
|
||||
class C_HP_HttpAgentListener : public IHttpAgentListener
|
||||
{
|
||||
public:
|
||||
virtual EnHttpParseResult OnMessageBegin(IHttpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnRequestLine(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
|
||||
virtual EnHttpParseResult OnStatusLine(IHttpAgent* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
|
||||
virtual EnHttpParseResult OnHeader(IHttpAgent* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
|
||||
virtual EnHttpParseResult OnHeadersComplete(IHttpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnBody(IHttpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHttpParseResult OnChunkHeader(IHttpAgent* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
|
||||
virtual EnHttpParseResult OnChunkComplete(IHttpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnMessageComplete(IHttpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnUpgrade(IHttpAgent* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
|
||||
virtual EnHttpParseResult OnParseError(IHttpAgent* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
|
||||
|
||||
virtual EnHandleResult OnWSMessageHeader(IHttpAgent* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
virtual EnHandleResult OnWSMessageBody(IHttpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnWSMessageComplete(IHttpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
|
||||
|
||||
virtual EnHandleResult OnPrepareConnect(ITcpAgent* pSender, CONNID dwConnID, SOCKET socket)
|
||||
{return m_lsnAgent.OnPrepareConnect(pSender, dwConnID, socket);}
|
||||
virtual EnHandleResult OnConnect(ITcpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnAgent.OnConnect(pSender, dwConnID);}
|
||||
virtual EnHandleResult OnHandShake(ITcpAgent* pSender, CONNID dwConnID)
|
||||
{return m_lsnAgent.OnHandShake(pSender, dwConnID);}
|
||||
virtual EnHandleResult OnSend(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnAgent.OnSend(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnAgent.OnReceive(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpAgent* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnAgent.OnReceive(pSender, dwConnID, iLength);}
|
||||
virtual EnHandleResult OnClose(ITcpAgent* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_lsnAgent.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult OnShutdown(ITcpAgent* pSender)
|
||||
{return m_lsnAgent.OnShutdown(pSender);}
|
||||
|
||||
public:
|
||||
C_HP_HttpAgentBaseListener1 m_lsnHttp;
|
||||
C_HP_HttpAgentBaseListener2 m_lsnAgent;
|
||||
};
|
||||
|
||||
class C_HP_HttpClientListener : public IHttpClientListener
|
||||
{
|
||||
public:
|
||||
virtual EnHttpParseResult OnMessageBegin(IHttpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageBegin(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnRequestLine(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszMethod, LPCSTR lpszUrl)
|
||||
{return m_lsnHttp.OnRequestLine(pSender, dwConnID, lpszMethod, lpszUrl);}
|
||||
virtual EnHttpParseResult OnStatusLine(IHttpClient* pSender, CONNID dwConnID, USHORT usStatusCode, LPCSTR lpszDesc)
|
||||
{return m_lsnHttp.OnStatusLine(pSender, dwConnID, usStatusCode, lpszDesc);}
|
||||
virtual EnHttpParseResult OnHeader(IHttpClient* pSender, CONNID dwConnID, LPCSTR lpszName, LPCSTR lpszValue)
|
||||
{return m_lsnHttp.OnHeader(pSender, dwConnID, lpszName, lpszValue);}
|
||||
virtual EnHttpParseResult OnHeadersComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnHeadersComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHttpParseResult OnChunkHeader(IHttpClient* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnHttp.OnChunkHeader(pSender, dwConnID, iLength);}
|
||||
virtual EnHttpParseResult OnChunkComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnChunkComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnMessageComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnMessageComplete(pSender, dwConnID);}
|
||||
virtual EnHttpParseResult OnUpgrade(IHttpClient* pSender, CONNID dwConnID, EnHttpUpgradeType enUpgradeType)
|
||||
{return m_lsnHttp.OnUpgrade(pSender, dwConnID, enUpgradeType);}
|
||||
virtual EnHttpParseResult OnParseError(IHttpClient* pSender, CONNID dwConnID, int iErrorCode, LPCSTR lpszErrorDesc)
|
||||
{return m_lsnHttp.OnParseError(pSender, dwConnID, iErrorCode, lpszErrorDesc);}
|
||||
|
||||
virtual EnHandleResult OnWSMessageHeader(IHttpClient* pSender, CONNID dwConnID, BOOL bFinal, BYTE iReserved, BYTE iOperationCode, const BYTE lpszMask[4], ULONGLONG ullBodyLen)
|
||||
{return m_lsnHttp.OnWSMessageHeader(pSender, dwConnID, bFinal, iReserved, iOperationCode, lpszMask, ullBodyLen);}
|
||||
virtual EnHandleResult OnWSMessageBody(IHttpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnHttp.OnWSMessageBody(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnWSMessageComplete(IHttpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnHttp.OnWSMessageComplete(pSender, dwConnID);}
|
||||
|
||||
virtual EnHandleResult OnPrepareConnect(ITcpClient* pSender, CONNID dwConnID, SOCKET socket)
|
||||
{return m_lsnClient.OnPrepareConnect(pSender, dwConnID, socket);}
|
||||
virtual EnHandleResult OnConnect(ITcpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnClient.OnConnect(pSender, dwConnID);}
|
||||
virtual EnHandleResult OnHandShake(ITcpClient* pSender, CONNID dwConnID)
|
||||
{return m_lsnClient.OnHandShake(pSender, dwConnID);}
|
||||
virtual EnHandleResult OnSend(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnClient.OnSend(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, const BYTE* pData, int iLength)
|
||||
{return m_lsnClient.OnReceive(pSender, dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult OnReceive(ITcpClient* pSender, CONNID dwConnID, int iLength)
|
||||
{return m_lsnClient.OnReceive(pSender, dwConnID, iLength);}
|
||||
virtual EnHandleResult OnClose(ITcpClient* pSender, CONNID dwConnID, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_lsnClient.OnClose(pSender, dwConnID, enOperation, iErrorCode);}
|
||||
|
||||
public:
|
||||
C_HP_HttpClientBaseListener1 m_lsnHttp;
|
||||
C_HP_HttpClientBaseListener2 m_lsnClient;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class C_HP_ThreadPoolListener : public IHPThreadPoolListener
|
||||
{
|
||||
public:
|
||||
virtual void OnStartup(IHPThreadPool* pThreadPool)
|
||||
{
|
||||
if(m_fnOnStartup)
|
||||
m_fnOnStartup((HP_ThreadPool)pThreadPool);
|
||||
}
|
||||
|
||||
virtual void OnShutdown(IHPThreadPool* pThreadPool)
|
||||
{
|
||||
if(m_fnOnShutdown)
|
||||
m_fnOnShutdown((HP_ThreadPool)pThreadPool);
|
||||
}
|
||||
|
||||
virtual void OnWorkerThreadStart(IHPThreadPool* pThreadPool, THR_ID dwThreadID)
|
||||
{
|
||||
if(m_fnOnWorkerThreadStart)
|
||||
m_fnOnWorkerThreadStart((HP_ThreadPool)pThreadPool, dwThreadID);
|
||||
}
|
||||
virtual void OnWorkerThreadEnd(IHPThreadPool* pThreadPool, THR_ID dwThreadID)
|
||||
{
|
||||
if(m_fnOnWorkerThreadEnd)
|
||||
m_fnOnWorkerThreadEnd((HP_ThreadPool)pThreadPool, dwThreadID);
|
||||
}
|
||||
|
||||
public:
|
||||
C_HP_ThreadPoolListener()
|
||||
: m_fnOnStartup (nullptr)
|
||||
, m_fnOnShutdown (nullptr)
|
||||
, m_fnOnWorkerThreadStart (nullptr)
|
||||
, m_fnOnWorkerThreadEnd (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
HP_FN_ThreadPool_OnStartup m_fnOnStartup;
|
||||
HP_FN_ThreadPool_OnShutdown m_fnOnShutdown;
|
||||
HP_FN_ThreadPool_OnWorkerThreadStart m_fnOnWorkerThreadStart;
|
||||
HP_FN_ThreadPool_OnWorkerThreadEnd m_fnOnWorkerThreadEnd;
|
||||
};
|
||||
1306
TcpAgent.cpp
Normal file
1306
TcpAgent.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
TcpAgent.h
Normal file
306
TcpAgent.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
#include "./common/IODispatcher.h"
|
||||
|
||||
class CTcpAgent : public ITcpAgent, private CIOHandler
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszBindAddress = nullptr, BOOL bAsyncConnect = TRUE);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Connect(LPCTSTR lpszRemoteAddress, USHORT usPort, CONNID* pdwConnID = nullptr, PVOID pExtra = nullptr, USHORT usLocalPort = 0, LPCTSTR lpszLocalAddress = nullptr);
|
||||
virtual BOOL Send (CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendSmallFile (CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
|
||||
virtual BOOL SendPackets (CONNID dwConnID, const WSABUF pBuffers[], int iCount) {return DoSendPackets(dwConnID, pBuffers, iCount);}
|
||||
virtual BOOL PauseReceive (CONNID dwConnID, BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual BOOL Disconnect (CONNID dwConnID, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectLongConnections (DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL GetLocalAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteHost (CONNID dwConnID, TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
|
||||
|
||||
virtual BOOL IsConnected (CONNID dwConnID);
|
||||
virtual BOOL IsPauseReceive (CONNID dwConnID, BOOL& bPaused);
|
||||
virtual BOOL GetPendingDataLength (CONNID dwConnID, int& iPending);
|
||||
virtual DWORD GetConnectionCount ();
|
||||
virtual BOOL GetAllConnectionIDs (CONNID pIDs[], DWORD& dwCount);
|
||||
virtual BOOL GetConnectPeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual BOOL GetSilencePeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
virtual BOOL SetupSSLContext (int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) {return FALSE;}
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) {return FALSE;}
|
||||
virtual void CleanupSSLContext () {}
|
||||
|
||||
virtual BOOL StartSSLHandShake (CONNID dwConnID) {return FALSE;}
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {}
|
||||
virtual BOOL IsSSLAutoHandShake () {return FALSE;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){}
|
||||
virtual LPCTSTR GetSSLCipherList() {return nullptr;}
|
||||
virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo) {return FALSE;}
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShake (TAgentSocketObj* pSocketObj) {return FALSE;}
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override;
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override;
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) override;
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) override;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
|
||||
virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy) {ENSURE_HAS_STOPPED(); m_enReusePolicy = enReusePolicy;}
|
||||
virtual void SetSendPolicy (EnSendPolicy enSendPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
|
||||
virtual void SetOnSendSyncPolicy (EnOnSendSyncPolicy enOnSendSyncPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
|
||||
virtual void SetSyncConnectTimeout (DWORD dwSyncConnectTimeout) {ENSURE_HAS_STOPPED(); m_dwSyncConnectTimeout = dwSyncConnectTimeout;}
|
||||
virtual void SetMaxConnectionCount (DWORD dwMaxConnectionCount) {ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount = dwMaxConnectionCount;}
|
||||
virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) {ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount = dwWorkerThreadCount;}
|
||||
virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) {ENSURE_HAS_STOPPED(); m_dwSocketBufferSize = dwSocketBufferSize;}
|
||||
virtual void SetFreeSocketObjLockTime (DWORD dwFreeSocketObjLockTime) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime = dwFreeSocketObjLockTime;}
|
||||
virtual void SetFreeSocketObjPool (DWORD dwFreeSocketObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool = dwFreeSocketObjPool;}
|
||||
virtual void SetFreeBufferObjPool (DWORD dwFreeBufferObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool = dwFreeBufferObjPool;}
|
||||
virtual void SetFreeSocketObjHold (DWORD dwFreeSocketObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold = dwFreeSocketObjHold;}
|
||||
virtual void SetFreeBufferObjHold (DWORD dwFreeBufferObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold = dwFreeBufferObjHold;}
|
||||
virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) {ENSURE_HAS_STOPPED(); m_dwKeepAliveTime = dwKeepAliveTime;}
|
||||
virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) {ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval = dwKeepAliveInterval;}
|
||||
virtual void SetMarkSilence (BOOL bMarkSilence) {ENSURE_HAS_STOPPED(); m_bMarkSilence = bMarkSilence;}
|
||||
virtual void SetNoDelay (BOOL bNoDelay) {ENSURE_HAS_STOPPED(); m_bNoDelay = bNoDelay;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual EnSendPolicy GetSendPolicy () {return m_enSendPolicy;}
|
||||
virtual EnOnSendSyncPolicy GetOnSendSyncPolicy () {return m_enOnSendSyncPolicy;}
|
||||
virtual DWORD GetSyncConnectTimeout () {return m_dwSyncConnectTimeout;}
|
||||
virtual DWORD GetMaxConnectionCount () {return m_dwMaxConnectionCount;}
|
||||
virtual DWORD GetWorkerThreadCount () {return m_dwWorkerThreadCount;}
|
||||
virtual DWORD GetSocketBufferSize () {return m_dwSocketBufferSize;}
|
||||
virtual DWORD GetFreeSocketObjLockTime () {return m_dwFreeSocketObjLockTime;}
|
||||
virtual DWORD GetFreeSocketObjPool () {return m_dwFreeSocketObjPool;}
|
||||
virtual DWORD GetFreeBufferObjPool () {return m_dwFreeBufferObjPool;}
|
||||
virtual DWORD GetFreeSocketObjHold () {return m_dwFreeSocketObjHold;}
|
||||
virtual DWORD GetFreeBufferObjHold () {return m_dwFreeBufferObjHold;}
|
||||
virtual DWORD GetKeepAliveTime () {return m_dwKeepAliveTime;}
|
||||
virtual DWORD GetKeepAliveInterval () {return m_dwKeepAliveInterval;}
|
||||
virtual BOOL IsMarkSilence () {return m_bMarkSilence;}
|
||||
virtual BOOL IsNoDelay () {return m_bNoDelay;}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareConnect(CONNID dwConnID, SOCKET socket)
|
||||
{return DoFirePrepareConnect(dwConnID, socket);}
|
||||
virtual EnHandleResult FireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult rs = DoFireConnect(pSocketObj);
|
||||
if(rs != HR_ERROR) rs = FireHandShake(pSocketObj);
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake(TAgentSocketObj* pSocketObj)
|
||||
{return DoFireHandShake(pSocketObj);}
|
||||
virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireReceive(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(TAgentSocketObj* pSocketObj, int iLength)
|
||||
{return DoFireReceive(pSocketObj, iLength);}
|
||||
virtual EnHandleResult FireSend(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireSend(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult FireShutdown()
|
||||
{return DoFireShutdown();}
|
||||
|
||||
virtual EnHandleResult DoFirePrepareConnect(CONNID dwConnID, SOCKET socket)
|
||||
{return m_pListener->OnPrepareConnect(this, dwConnID, socket);}
|
||||
virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnConnect(this, pSocketObj->connID);}
|
||||
virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
|
||||
{return m_pListener->OnHandShake(this, pSocketObj->connID);}
|
||||
virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
|
||||
virtual EnHandleResult DoFireSend(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{return m_pListener->OnShutdown(this);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual BOOL BeforeUnpause(TAgentSocketObj* pSocketObj) {return TRUE;}
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
BOOL DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
BOOL DoSendPackets(TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
|
||||
TAgentSocketObj* FindSocketObj(CONNID dwConnID);
|
||||
BOOL GetRemoteHost(CONNID dwConnID, LPCSTR* lpszHost, USHORT* pusPort = nullptr);
|
||||
|
||||
protected:
|
||||
BOOL SetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID pExtra);
|
||||
BOOL GetConnectionExtra(TAgentSocketObj* pSocketObj, PVOID* ppExtra);
|
||||
BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(TAgentSocketObj* pSocketObj, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
|
||||
BOOL SetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(TAgentSocketObj* pSocketObj, PVOID* ppReserved2);
|
||||
|
||||
private:
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL ParseBindAddress(LPCTSTR lpszBindAddress);
|
||||
BOOL CreateWorkerThreads();
|
||||
|
||||
void DisconnectClientSocket();
|
||||
void WaitForClientSocketClose();
|
||||
void ReleaseClientSocket();
|
||||
void ReleaseFreeSocket();
|
||||
void WaitForWorkerThreadEnd();
|
||||
|
||||
TAgentSocketObj* GetFreeSocketObj(CONNID dwConnID, SOCKET soClient);
|
||||
TAgentSocketObj* CreateSocketObj();
|
||||
void AddFreeSocketObj (TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0);
|
||||
void DeleteSocketObj (TAgentSocketObj* pSocketObj);
|
||||
BOOL InvalidSocketObj (TAgentSocketObj* pSocketObj);
|
||||
void AddClientSocketObj (CONNID dwConnID, TAgentSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr, LPCTSTR lpszRemoteHostName, PVOID pExtra);
|
||||
void CloseClientSocketObj(TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, int iShutdownFlag = SHUT_WR);
|
||||
|
||||
private:
|
||||
int CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszLocalAddress, USHORT usLocalPort, SOCKET& soClient, HP_SOCKADDR& addr);
|
||||
int PrepareConnect (CONNID& dwConnID, SOCKET soClient);
|
||||
int ConnectToServer (CONNID dwConnID, LPCTSTR lpszRemoteHostName, SOCKET& soClient, const HP_SOCKADDR& addr, PVOID pExtra);
|
||||
|
||||
VOID HandleCmdSend (const TDispContext* pContext, CONNID dwConnID);
|
||||
VOID HandleCmdUnpause (const TDispContext* pContext, CONNID dwConnID);
|
||||
VOID HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce);
|
||||
BOOL HandleConnect (const TDispContext* pContext, TAgentSocketObj* pSocketObj, UINT events);
|
||||
BOOL HandleReceive (const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag);
|
||||
BOOL HandleSend (const TDispContext* pContext, TAgentSocketObj* pSocketObj, int flag);
|
||||
BOOL HandleClose (const TDispContext* pContext, TAgentSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events);
|
||||
|
||||
int SendInternal (TAgentSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
|
||||
BOOL SendItem (TAgentSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
|
||||
|
||||
public:
|
||||
CTcpAgent(ITcpAgentListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_fdGCTimer (INVALID_FD)
|
||||
, m_bAsyncConnect (TRUE)
|
||||
, m_enReusePolicy (RAP_ADDR_ONLY)
|
||||
, m_enSendPolicy (SP_PACK)
|
||||
, m_enOnSendSyncPolicy (OSSP_RECEIVE)
|
||||
, m_dwSyncConnectTimeout (DEFAULT_SYNC_CONNECT_TIMEOUT)
|
||||
, m_dwMaxConnectionCount (DEFAULT_CONNECTION_COUNT)
|
||||
, m_dwWorkerThreadCount (DEFAULT_WORKER_THREAD_COUNT)
|
||||
, m_dwSocketBufferSize (DEFAULT_TCP_SOCKET_BUFFER_SIZE)
|
||||
, m_dwFreeSocketObjLockTime (DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
|
||||
, m_dwFreeSocketObjPool (DEFAULT_FREE_SOCKETOBJ_POOL)
|
||||
, m_dwFreeBufferObjPool (DEFAULT_FREE_BUFFEROBJ_POOL)
|
||||
, m_dwFreeSocketObjHold (DEFAULT_FREE_SOCKETOBJ_HOLD)
|
||||
, m_dwFreeBufferObjHold (DEFAULT_FREE_BUFFEROBJ_HOLD)
|
||||
, m_dwKeepAliveTime (DEFALUT_TCP_KEEPALIVE_TIME)
|
||||
, m_dwKeepAliveInterval (DEFALUT_TCP_KEEPALIVE_INTERVAL)
|
||||
, m_bMarkSilence (TRUE)
|
||||
, m_bNoDelay (FALSE)
|
||||
, m_soAddr (AF_UNSPEC, TRUE)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CTcpAgent()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
EnSendPolicy m_enSendPolicy;
|
||||
EnOnSendSyncPolicy m_enOnSendSyncPolicy;
|
||||
DWORD m_dwSyncConnectTimeout;
|
||||
DWORD m_dwMaxConnectionCount;
|
||||
DWORD m_dwWorkerThreadCount;
|
||||
DWORD m_dwSocketBufferSize;
|
||||
DWORD m_dwFreeSocketObjLockTime;
|
||||
DWORD m_dwFreeSocketObjPool;
|
||||
DWORD m_dwFreeBufferObjPool;
|
||||
DWORD m_dwFreeSocketObjHold;
|
||||
DWORD m_dwFreeBufferObjHold;
|
||||
DWORD m_dwKeepAliveTime;
|
||||
DWORD m_dwKeepAliveInterval;
|
||||
BOOL m_bMarkSilence;
|
||||
BOOL m_bNoDelay;
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
ITcpAgentListener* m_pListener;
|
||||
BOOL m_bAsyncConnect;
|
||||
EnServiceState m_enState;
|
||||
EnSocketError m_enLastError;
|
||||
HP_SOCKADDR m_soAddr;
|
||||
|
||||
CReceiveBuffersPtr m_rcBuffers;
|
||||
|
||||
CPrivateHeap m_phSocket;
|
||||
CBufferObjPool m_bfObjPool;
|
||||
|
||||
CSpinGuard m_csState;
|
||||
|
||||
FD m_fdGCTimer;
|
||||
|
||||
TAgentSocketObjPtrPool m_bfActiveSockets;
|
||||
|
||||
TAgentSocketObjPtrList m_lsFreeSocket;
|
||||
TAgentSocketObjPtrQueue m_lsGCSocket;
|
||||
|
||||
CIODispatcher m_ioDispatcher;
|
||||
};
|
||||
723
TcpClient.cpp
Normal file
723
TcpClient.cpp
Normal file
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpClient.h"
|
||||
|
||||
#include "./common/FileHelper.h"
|
||||
|
||||
BOOL CTcpClient::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
|
||||
{
|
||||
if(!CheckParams() || !CheckStarting())
|
||||
return FALSE;
|
||||
|
||||
PrepareStart();
|
||||
m_ccContext.Reset();
|
||||
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
HP_SOCKADDR addrRemote, addrBind;
|
||||
|
||||
if(CreateClientSocket(lpszRemoteAddress, addrRemote, usPort, lpszBindAddress, addrBind))
|
||||
{
|
||||
if(BindClientSocket(addrBind, addrRemote, usLocalPort))
|
||||
{
|
||||
if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
|
||||
{
|
||||
if(ConnectToServer(addrRemote, bAsyncConnect))
|
||||
{
|
||||
if(CreateWorkerThread())
|
||||
isOK = TRUE;
|
||||
else
|
||||
SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
|
||||
if(!isOK)
|
||||
{
|
||||
m_ccContext.Reset(FALSE);
|
||||
EXECUTE_RESTORE_ERROR(Stop());
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::CheckParams()
|
||||
{
|
||||
if (((int)m_dwSyncConnectTimeout > 0) &&
|
||||
((int)m_dwSocketBufferSize > 0) &&
|
||||
((int)m_dwFreeBufferPoolSize >= 0) &&
|
||||
((int)m_dwFreeBufferPoolHold >= 0) &&
|
||||
((int)m_dwKeepAliveTime >= 1000 || m_dwKeepAliveTime == 0) &&
|
||||
((int)m_dwKeepAliveInterval >= 1000 || m_dwKeepAliveInterval == 0) )
|
||||
return TRUE;
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CTcpClient::PrepareStart()
|
||||
{
|
||||
m_itPool.SetItemCapacity(m_dwSocketBufferSize);
|
||||
m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
|
||||
m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
|
||||
|
||||
m_itPool.Prepare();
|
||||
}
|
||||
|
||||
BOOL CTcpClient::CheckStarting()
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(m_enState == SS_STOPPED)
|
||||
m_enState = SS_STARTING;
|
||||
else
|
||||
{
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::CheckStoping()
|
||||
{
|
||||
if(m_enState != SS_STOPPED)
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(HasStarted())
|
||||
{
|
||||
m_enState = SS_STOPPING;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind)
|
||||
{
|
||||
HP_SCOPE_HOST host(lpszRemoteAddress);
|
||||
|
||||
if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote))
|
||||
return FALSE;
|
||||
|
||||
if(::IsStrNotEmpty(lpszBindAddress))
|
||||
{
|
||||
if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind))
|
||||
return FALSE;
|
||||
|
||||
if(addrRemote.family != addrBind.family)
|
||||
{
|
||||
::WSASetLastError(ERROR_AFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
m_soClient = socket(addrRemote.family, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if(m_soClient == INVALID_SOCKET)
|
||||
return FALSE;
|
||||
|
||||
BOOL bOnOff = (m_dwKeepAliveTime > 0 && m_dwKeepAliveInterval > 0);
|
||||
VERIFY(::SSO_KeepAliveVals(m_soClient, bOnOff, m_dwKeepAliveTime, m_dwKeepAliveInterval) == NO_ERROR);
|
||||
VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
|
||||
VERIFY(::SSO_NoDelay(m_soClient, m_bNoDelay) == NO_ERROR);
|
||||
|
||||
SetRemoteHost(host.name, usPort);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort)
|
||||
{
|
||||
if(addrBind.IsSpecified() && usLocalPort == 0)
|
||||
{
|
||||
if(::bind(m_soClient, addrBind.Addr(), addrBind.AddrSize()) == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
}
|
||||
else if(usLocalPort != 0)
|
||||
{
|
||||
HP_SOCKADDR realBindAddr = addrBind.IsSpecified() ? addrBind : HP_SOCKADDR::AnyAddr(addrRemote.family);
|
||||
|
||||
realBindAddr.SetPort(usLocalPort);
|
||||
|
||||
if(::bind(m_soClient, realBindAddr.Addr(), realBindAddr.AddrSize()) == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_dwConnID = ::GenerateConnectionID();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
|
||||
|
||||
int rc = ::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize());
|
||||
|
||||
if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
|
||||
{
|
||||
if(bAsyncConnect)
|
||||
{
|
||||
m_nEvents = POLLOUT;
|
||||
isOK = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IS_HAS_ERROR(rc))
|
||||
rc = ::WaitForSocketWrite(m_soClient, m_dwSyncConnectTimeout);
|
||||
|
||||
if(!IS_NO_ERROR(rc))
|
||||
::WSASetLastError(rc);
|
||||
else
|
||||
{
|
||||
SetConnected();
|
||||
|
||||
if(TRIGGER(FireConnect()) == HR_ERROR)
|
||||
::WSASetLastError(ENSURE_ERROR_CANCELLED);
|
||||
else
|
||||
{
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::Stop()
|
||||
{
|
||||
if(!CheckStoping())
|
||||
return FALSE;
|
||||
|
||||
WaitForWorkerThreadEnd();
|
||||
|
||||
SetConnected(FALSE);
|
||||
|
||||
if(m_ccContext.bFireOnClose)
|
||||
FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
|
||||
|
||||
if(m_soClient != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(m_soClient, SHUT_WR);
|
||||
closesocket(m_soClient);
|
||||
|
||||
m_soClient = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CTcpClient::Reset()
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
m_evSend.Reset();
|
||||
m_evRecv.Reset();
|
||||
m_evStop.Reset();
|
||||
|
||||
m_lsSend.Clear();
|
||||
m_itPool.Clear();
|
||||
m_rcBuffer.Free();
|
||||
|
||||
m_strHost.Empty();
|
||||
|
||||
m_usPort = 0;
|
||||
m_nEvents = 0;
|
||||
m_bPaused = FALSE;
|
||||
m_enState = SS_STOPPED;
|
||||
|
||||
m_evWait.SyncNotifyAll();
|
||||
}
|
||||
|
||||
void CTcpClient::WaitForWorkerThreadEnd()
|
||||
{
|
||||
if(!m_thWorker.IsRunning())
|
||||
return;
|
||||
|
||||
if(m_thWorker.IsInMyThread())
|
||||
m_thWorker.Detach();
|
||||
else
|
||||
{
|
||||
m_evStop.Set();
|
||||
m_thWorker.Join();
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CTcpClient::CreateWorkerThread()
|
||||
{
|
||||
return m_thWorker.Start(this, &CTcpClient::WorkerThreadProc);
|
||||
}
|
||||
|
||||
UINT WINAPI CTcpClient::WorkerThreadProc(LPVOID pv)
|
||||
{
|
||||
::SetCurrentWorkerThreadName();
|
||||
|
||||
TRACE("---------------> Client Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
|
||||
|
||||
OnWorkerThreadStart(SELF_THREAD_ID);
|
||||
|
||||
BOOL bCallStop = TRUE;
|
||||
pollfd pfds[] = { {m_soClient, m_nEvents},
|
||||
{m_evSend.GetFD(), POLLIN},
|
||||
{m_evRecv.GetFD(), POLLIN},
|
||||
{m_evStop.GetFD(), POLLIN} };
|
||||
int size = ARRAY_SIZE(pfds);
|
||||
|
||||
m_rcBuffer.Malloc(m_dwSocketBufferSize);
|
||||
|
||||
while(HasStarted())
|
||||
{
|
||||
int rs = (int)::PollForMultipleObjects(pfds, size);
|
||||
ASSERT(rs > TIMEOUT);
|
||||
|
||||
if(rs <= 0)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
if((1 << i) & rs)
|
||||
{
|
||||
SHORT revents = pfds[i].revents;
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
if(!ProcessNetworkEvent(revents))
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 1)
|
||||
{
|
||||
m_evSend.Reset();
|
||||
|
||||
if(!SendData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 2)
|
||||
{
|
||||
m_evRecv.Reset();
|
||||
|
||||
if(!BeforeUnpause())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
|
||||
if(!ReadData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 3)
|
||||
{
|
||||
m_evStop.Reset();
|
||||
|
||||
bCallStop = FALSE;
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else
|
||||
VERIFY(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
pfds[0].events = m_nEvents;
|
||||
}
|
||||
|
||||
EXIT_WORKER_THREAD:
|
||||
|
||||
OnWorkerThreadEnd(SELF_THREAD_ID);
|
||||
|
||||
if(bCallStop && HasStarted())
|
||||
Stop();
|
||||
|
||||
TRACE("---------------> Client Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::ProcessNetworkEvent(SHORT events)
|
||||
{
|
||||
BOOL bContinue = TRUE;
|
||||
|
||||
if(bContinue && events & POLLERR)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
if(bContinue && !IsConnected())
|
||||
bContinue = HandleConnect(events);
|
||||
|
||||
if(bContinue && events & POLLIN)
|
||||
bContinue = HandleRead(events);
|
||||
|
||||
if(bContinue && events & POLLOUT)
|
||||
bContinue = HandleWrite(events);
|
||||
|
||||
if(bContinue && events & _POLL_HUNGUP_EVENTS)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
return bContinue;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::HandleConnect(SHORT events)
|
||||
{
|
||||
ASSERT(events & POLLOUT);
|
||||
|
||||
int code = ::SSO_GetError(m_soClient);
|
||||
|
||||
if(!IS_NO_ERROR(code) || (events & _POLL_ERROR_EVENTS))
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CONNECT, code);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(events & _POLL_HUNGUP_EVENTS)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CONNECT, NO_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SetConnected();
|
||||
|
||||
if(TRIGGER(FireConnect()) == HR_ERROR)
|
||||
{
|
||||
m_ccContext.Reset(FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::HandleClose(SHORT events)
|
||||
{
|
||||
EnSocketOperation enOperation = SO_CLOSE;
|
||||
|
||||
if(events & _POLL_HUNGUP_EVENTS)
|
||||
enOperation = SO_CLOSE;
|
||||
else if(events & POLLIN)
|
||||
enOperation = SO_RECEIVE;
|
||||
else if(events & POLLOUT)
|
||||
enOperation = SO_SEND;
|
||||
|
||||
m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::HandleRead(SHORT events)
|
||||
{
|
||||
return ReadData();
|
||||
}
|
||||
|
||||
BOOL CTcpClient::HandleWrite(SHORT events)
|
||||
{
|
||||
return SendData();
|
||||
}
|
||||
|
||||
BOOL CTcpClient::ReadData()
|
||||
{
|
||||
while(TRUE)
|
||||
{
|
||||
if(m_bPaused)
|
||||
break;
|
||||
|
||||
int rc = (int)read(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwSocketBufferSize);
|
||||
|
||||
if(rc > 0)
|
||||
{
|
||||
if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
|
||||
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(rc == 0)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CLOSE, SE_OK);
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::PauseReceive(BOOL bPause)
|
||||
{
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_bPaused == bPause)
|
||||
return TRUE;
|
||||
|
||||
m_bPaused = bPause;
|
||||
|
||||
if(!bPause)
|
||||
return m_evRecv.Set();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::SendData()
|
||||
{
|
||||
BOOL bBlocked = FALSE;
|
||||
|
||||
while(m_lsSend.Length() > 0)
|
||||
{
|
||||
TItemPtr itPtr(m_itPool);
|
||||
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
itPtr = m_lsSend.PopFront();
|
||||
}
|
||||
|
||||
if(!itPtr.IsValid())
|
||||
break;
|
||||
|
||||
ASSERT(!itPtr->IsEmpty());
|
||||
|
||||
if(!DoSendData(itPtr, bBlocked))
|
||||
return FALSE;
|
||||
|
||||
if(bBlocked)
|
||||
{
|
||||
ASSERT(!itPtr->IsEmpty());
|
||||
|
||||
CCriSecLock locallock(m_csSend);
|
||||
m_lsSend.PushFront(itPtr.Detach());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::DoSendData(TItem* pItem, BOOL& bBlocked)
|
||||
{
|
||||
while(!pItem->IsEmpty())
|
||||
{
|
||||
int rc = (int)write(m_soClient, (char*)pItem->Ptr(), pItem->Size());
|
||||
|
||||
if(rc > 0)
|
||||
{
|
||||
if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
pItem->Reduce(rc);
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
{
|
||||
bBlocked = TRUE;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_SEND, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::Send(const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength > 0);
|
||||
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
|
||||
WSABUF buffer;
|
||||
buffer.len = iLength;
|
||||
buffer.buf = (BYTE*)pBuffer;
|
||||
|
||||
return SendPackets(&buffer, 1);
|
||||
}
|
||||
|
||||
BOOL CTcpClient::DoSendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(pBuffers && iCount > 0)
|
||||
{
|
||||
if(IsConnected())
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
if(IsConnected())
|
||||
result = SendInternal(pBuffers, iCount);
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CTcpClient::SendInternal(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(m_lsSend.Length() >= 0);
|
||||
|
||||
int iPending = m_lsSend.Length();
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
m_lsSend.Cat(pBuffer, iBufLen);
|
||||
ASSERT(m_lsSend.Length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::SendSmallFile(LPCTSTR lpszFileName, const LPWSABUF pHead, const LPWSABUF pTail)
|
||||
{
|
||||
CFile file;
|
||||
CFileMapping fmap;
|
||||
WSABUF szBuf[3];
|
||||
|
||||
HRESULT hr = ::MakeSmallFilePackage(lpszFileName, file, fmap, szBuf, pHead, pTail);
|
||||
|
||||
if(FAILED(hr))
|
||||
{
|
||||
::SetLastError(hr);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return SendPackets(szBuf, 3);
|
||||
}
|
||||
|
||||
void CTcpClient::SetLastError(EnSocketError code, LPCSTR func, int ec)
|
||||
{
|
||||
TRACE("%s --> Error: %d, EC: %d", func, code, ec);
|
||||
|
||||
m_enLastError = code;
|
||||
::SetLastError(ec);
|
||||
}
|
||||
|
||||
BOOL CTcpClient::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ASSERT(lpszAddress != nullptr && iAddressLen > 0);
|
||||
|
||||
return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
void CTcpClient::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
|
||||
{
|
||||
m_strHost = lpszHost;
|
||||
m_usPort = usPort;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
if(m_strHost.IsEmpty())
|
||||
return isOK;
|
||||
|
||||
int iLen = m_strHost.GetLength() + 1;
|
||||
|
||||
if(iHostLen >= iLen)
|
||||
{
|
||||
memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
|
||||
usPort = m_usPort;
|
||||
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
iHostLen = iLen;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CTcpClient::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
|
||||
{
|
||||
*lpszHost = m_strHost;
|
||||
|
||||
if(pusPort != nullptr)
|
||||
*pusPort = m_usPort;
|
||||
|
||||
return !m_strHost.IsEmpty();
|
||||
}
|
||||
248
TcpClient.h
Normal file
248
TcpClient.h
Normal file
@@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
|
||||
class CTcpClient : public ITcpClient
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendSmallFile (LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
|
||||
virtual BOOL SendPackets (const WSABUF pBuffers[], int iCount) {return DoSendPackets(pBuffers, iCount);}
|
||||
virtual BOOL PauseReceive (BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual CONNID GetConnectionID () {return m_dwConnID;}
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteHost (TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
|
||||
virtual BOOL GetPendingDataLength (int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
|
||||
virtual BOOL IsPauseReceive (BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
|
||||
virtual BOOL IsConnected () {return m_bConnected;}
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
virtual BOOL SetupSSLContext (int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) {return FALSE;}
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) {return FALSE;}
|
||||
virtual void CleanupSSLContext () {}
|
||||
|
||||
virtual BOOL StartSSLHandShake () {return FALSE;}
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {}
|
||||
virtual BOOL IsSSLAutoHandShake () {return FALSE;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){}
|
||||
virtual LPCTSTR GetSSLCipherList() {return nullptr;}
|
||||
virtual BOOL GetSSLSessionInfo(EnSSLSessionInfo enInfo, LPVOID* lppInfo) {return FALSE;}
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShakeNoCheck() {return FALSE;}
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy = enReusePolicy;}
|
||||
virtual void SetSyncConnectTimeout (DWORD dwSyncConnectTimeout) {ENSURE_HAS_STOPPED(); m_dwSyncConnectTimeout = dwSyncConnectTimeout;}
|
||||
virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) {ENSURE_HAS_STOPPED(); m_dwSocketBufferSize = dwSocketBufferSize;}
|
||||
virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) {ENSURE_HAS_STOPPED(); m_dwKeepAliveTime = dwKeepAliveTime;}
|
||||
virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) {ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval = dwKeepAliveInterval;}
|
||||
virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize = dwFreeBufferPoolSize;}
|
||||
virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold = dwFreeBufferPoolHold;}
|
||||
virtual void SetNoDelay (BOOL bNoDelay) {ENSURE_HAS_STOPPED(); m_bNoDelay = bNoDelay;}
|
||||
virtual void SetExtra (PVOID pExtra) {m_pExtra = pExtra;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual DWORD GetSyncConnectTimeout () {return m_dwSyncConnectTimeout;}
|
||||
virtual DWORD GetSocketBufferSize () {return m_dwSocketBufferSize;}
|
||||
virtual DWORD GetKeepAliveTime () {return m_dwKeepAliveTime;}
|
||||
virtual DWORD GetKeepAliveInterval () {return m_dwKeepAliveInterval;}
|
||||
virtual DWORD GetFreeBufferPoolSize () {return m_dwFreeBufferPoolSize;}
|
||||
virtual DWORD GetFreeBufferPoolHold () {return m_dwFreeBufferPoolHold;}
|
||||
virtual BOOL IsNoDelay () {return m_bNoDelay;}
|
||||
virtual PVOID GetExtra () {return m_pExtra;}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareConnect(SOCKET socket)
|
||||
{return DoFirePrepareConnect(this, socket);}
|
||||
virtual EnHandleResult FireConnect()
|
||||
{
|
||||
EnHandleResult rs = DoFireConnect(this);
|
||||
if(rs != HR_ERROR) rs = FireHandShake();
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake()
|
||||
{return DoFireHandShake(this);}
|
||||
virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
|
||||
{return DoFireSend(this, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
|
||||
{return DoFireReceive(this, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(int iLength)
|
||||
{return DoFireReceive(this, iLength);}
|
||||
virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
|
||||
{return DoFireClose(this, enOperation, iErrorCode);}
|
||||
|
||||
virtual EnHandleResult DoFirePrepareConnect(ITcpClient* pSender, SOCKET socket)
|
||||
{return m_pListener->OnPrepareConnect(pSender, pSender->GetConnectionID(), socket);}
|
||||
virtual EnHandleResult DoFireConnect(ITcpClient* pSender)
|
||||
{return m_pListener->OnConnect(pSender, pSender->GetConnectionID());}
|
||||
virtual EnHandleResult DoFireHandShake(ITcpClient* pSender)
|
||||
{return m_pListener->OnHandShake(pSender, pSender->GetConnectionID());}
|
||||
virtual EnHandleResult DoFireSend(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(ITcpClient* pSender, int iLength)
|
||||
{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), iLength);}
|
||||
virtual EnHandleResult DoFireClose(ITcpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(pSender, pSender->GetConnectionID(), enOperation, iErrorCode);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual BOOL BeforeUnpause() {return TRUE;}
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
BOOL DoSendPackets(const WSABUF pBuffers[], int iCount);
|
||||
|
||||
static BOOL DoSendPackets(CTcpClient* pClient, const WSABUF pBuffers[], int iCount)
|
||||
{return pClient->DoSendPackets(pBuffers, iCount);}
|
||||
|
||||
protected:
|
||||
BOOL IsPaused () {return m_bPaused;}
|
||||
void SetReserved (PVOID pReserved) {m_pReserved = pReserved;}
|
||||
PVOID GetReserved () {return m_pReserved;}
|
||||
BOOL GetRemoteHost (LPCSTR* lpszHost, USHORT* pusPort = nullptr);
|
||||
|
||||
private:
|
||||
void SetRemoteHost (LPCTSTR lpszHost, USHORT usPort);
|
||||
void SetConnected (BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
|
||||
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind);
|
||||
BOOL BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort);
|
||||
BOOL ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect);
|
||||
BOOL CreateWorkerThread();
|
||||
BOOL ProcessNetworkEvent(SHORT events);
|
||||
BOOL ReadData();
|
||||
BOOL SendData();
|
||||
BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
|
||||
int SendInternal(const WSABUF pBuffers[], int iCount);
|
||||
void WaitForWorkerThreadEnd();
|
||||
|
||||
BOOL HandleConnect (SHORT events);
|
||||
BOOL HandleClose (SHORT events);
|
||||
BOOL HandleRead (SHORT events);
|
||||
BOOL HandleWrite (SHORT events);
|
||||
|
||||
UINT WINAPI WorkerThreadProc(LPVOID pv);
|
||||
|
||||
public:
|
||||
CTcpClient(ITcpClientListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_lsSend (m_itPool)
|
||||
, m_soClient (INVALID_SOCKET)
|
||||
, m_nEvents (0)
|
||||
, m_dwConnID (0)
|
||||
, m_usPort (0)
|
||||
, m_bPaused (FALSE)
|
||||
, m_bConnected (FALSE)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_bNoDelay (FALSE)
|
||||
, m_pExtra (nullptr)
|
||||
, m_pReserved (nullptr)
|
||||
, m_enReusePolicy (RAP_ADDR_ONLY)
|
||||
, m_dwSyncConnectTimeout(DEFAULT_SYNC_CONNECT_TIMEOUT)
|
||||
, m_dwSocketBufferSize (DEFAULT_TCP_SOCKET_BUFFER_SIZE)
|
||||
, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
|
||||
, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
|
||||
, m_dwKeepAliveTime (DEFALUT_TCP_KEEPALIVE_TIME)
|
||||
, m_dwKeepAliveInterval (DEFALUT_TCP_KEEPALIVE_INTERVAL)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CTcpClient()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
ITcpClientListener* m_pListener;
|
||||
TClientCloseContext m_ccContext;
|
||||
|
||||
SOCKET m_soClient;
|
||||
SHORT m_nEvents;
|
||||
CONNID m_dwConnID;
|
||||
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
DWORD m_dwSyncConnectTimeout;
|
||||
DWORD m_dwSocketBufferSize;
|
||||
DWORD m_dwFreeBufferPoolSize;
|
||||
DWORD m_dwFreeBufferPoolHold;
|
||||
DWORD m_dwKeepAliveTime;
|
||||
DWORD m_dwKeepAliveInterval;
|
||||
BOOL m_bNoDelay;
|
||||
|
||||
EnSocketError m_enLastError;
|
||||
volatile BOOL m_bConnected;
|
||||
volatile EnServiceState m_enState;
|
||||
|
||||
PVOID m_pExtra;
|
||||
PVOID m_pReserved;
|
||||
|
||||
CBufferPtr m_rcBuffer;
|
||||
|
||||
protected:
|
||||
CStringA m_strHost;
|
||||
USHORT m_usPort;
|
||||
|
||||
CItemPool m_itPool;
|
||||
|
||||
private:
|
||||
CSpinGuard m_csState;
|
||||
|
||||
CCriSec m_csSend;
|
||||
TItemListExV m_lsSend;
|
||||
|
||||
CEvt m_evSend;
|
||||
CEvt m_evRecv;
|
||||
CEvt m_evStop;
|
||||
|
||||
volatile BOOL m_bPaused;
|
||||
|
||||
CThread<CTcpClient, VOID, UINT> m_thWorker;
|
||||
};
|
||||
24
TcpPackAgent.cpp
Normal file
24
TcpPackAgent.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPackAgent.h"
|
||||
221
TcpPackAgent.h
Normal file
221
TcpPackAgent.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpAgent.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPackAgentT : public IPackSocket, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::GetMaxConnectionCount;
|
||||
using __super::GetSocketBufferSize;
|
||||
using __super::GetFreeBufferObjPool;
|
||||
using __super::GetFreeBufferObjHold;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
using __super::SetLastError;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
int iNewCount = iCount + 1;
|
||||
unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
|
||||
|
||||
DWORD dwHeader;
|
||||
if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
|
||||
return FALSE;
|
||||
|
||||
return __super::SendPackets(dwConnID, buffers.get(), iNewCount);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireConnect(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool.PickFreeBuffer(pSocketObj->connID);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, TBufferPackInfo::Construct(pBuffer)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
ASSERT(pInfo);
|
||||
|
||||
TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
return ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_bfPool.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual BOOL BeforeUnpause(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
if(!TAgentSocketObj::IsValid(pSocketObj))
|
||||
return FALSE;
|
||||
|
||||
if(pSocketObj->IsPaused())
|
||||
return TRUE;
|
||||
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
ASSERT(pInfo);
|
||||
|
||||
TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
return (ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
|
||||
}
|
||||
|
||||
virtual BOOL CheckParams()
|
||||
{
|
||||
if ((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT) &&
|
||||
(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT) )
|
||||
return __super::CheckParams();
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
virtual void PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_bfPool.SetMaxCacheSize (GetMaxConnectionCount());
|
||||
m_bfPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_bfPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_bfPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_bfPool.SetBufferLockTime (GetFreeSocketObjLockTime());
|
||||
m_bfPool.SetBufferPoolSize (GetFreeSocketObjPool());
|
||||
m_bfPool.SetBufferPoolHold (GetFreeSocketObjHold());
|
||||
|
||||
m_bfPool.Prepare();
|
||||
}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_bfPool.ReleaseGCBuffer(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void SetMaxPackSize (DWORD dwMaxPackSize) {ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
|
||||
virtual void SetPackHeaderFlag (USHORT usPackHeaderFlag) {ENSURE_HAS_STOPPED(); m_usHeaderFlag = usPackHeaderFlag;}
|
||||
virtual DWORD GetMaxPackSize () {return m_dwMaxPackSize;}
|
||||
virtual USHORT GetPackHeaderFlag() {return m_usHeaderFlag;}
|
||||
|
||||
private:
|
||||
void ReleaseConnectionExtra(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
|
||||
if(pInfo != nullptr)
|
||||
{
|
||||
m_bfPool.PutFreeBuffer(pInfo->pBuffer);
|
||||
TBufferPackInfo::Destruct(pInfo);
|
||||
|
||||
ENSURE(SetConnectionReserved(pSocketObj, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
EnHandleResult DoFireSuperReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return __super::DoFireReceive(pSocketObj, pData, iLength);}
|
||||
|
||||
friend EnHandleResult ParsePack<>(CTcpPackAgentT* pThis, TBufferPackInfo* pInfo, TBuffer* pBuffer, TAgentSocketObj* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
|
||||
|
||||
public:
|
||||
CTcpPackAgentT(ITcpAgentListener* pListener)
|
||||
: T (pListener)
|
||||
, m_dwMaxPackSize (TCP_PACK_DEFAULT_MAX_SIZE)
|
||||
, m_usHeaderFlag (TCP_PACK_DEFAULT_HEADER_FLAG)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPackAgentT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwMaxPackSize;
|
||||
USHORT m_usHeaderFlag;
|
||||
|
||||
CBufferPool m_bfPool;
|
||||
};
|
||||
|
||||
typedef CTcpPackAgentT<CTcpAgent> CTcpPackAgent;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLAgent.h"
|
||||
typedef CTcpPackAgentT<CSSLAgent> CSSLPackAgent;
|
||||
|
||||
#endif
|
||||
24
TcpPackClient.cpp
Normal file
24
TcpPackClient.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPackClient.h"
|
||||
126
TcpPackClient.h
Normal file
126
TcpPackClient.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpClient.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPackClientT : public IPackClient, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::SetLastError;
|
||||
using __super::m_itPool;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
int iNewCount = iCount + 1;
|
||||
unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
|
||||
|
||||
DWORD dwHeader;
|
||||
if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
|
||||
return FALSE;
|
||||
|
||||
return __super::SendPackets(buffers.get(), iNewCount);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{
|
||||
return ParsePack(this, &m_pkInfo, &m_lsBuffer, (CTcpPackClientT*)pSender, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
|
||||
}
|
||||
|
||||
virtual BOOL BeforeUnpause()
|
||||
{
|
||||
return (ParsePack(this, &m_pkInfo, &m_lsBuffer, (CTcpPackClientT*)this, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
|
||||
}
|
||||
|
||||
virtual BOOL CheckParams()
|
||||
{
|
||||
if ((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT) &&
|
||||
(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT) )
|
||||
return __super::CheckParams();
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
virtual void Reset()
|
||||
{
|
||||
m_lsBuffer.Clear();
|
||||
m_pkInfo.Reset();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void SetMaxPackSize (DWORD dwMaxPackSize) {ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
|
||||
virtual void SetPackHeaderFlag (USHORT usPackHeaderFlag) {ENSURE_HAS_STOPPED(); m_usHeaderFlag = usPackHeaderFlag;}
|
||||
virtual DWORD GetMaxPackSize () {return m_dwMaxPackSize;}
|
||||
virtual USHORT GetPackHeaderFlag() {return m_usHeaderFlag;}
|
||||
|
||||
private:
|
||||
EnHandleResult DoFireSuperReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return __super::DoFireReceive(pSender, pData, iLength);}
|
||||
|
||||
friend EnHandleResult ParsePack<> (CTcpPackClientT* pThis, TPackInfo<TItemListEx>* pInfo, TItemListEx* pBuffer, CTcpPackClientT* pSocket,
|
||||
DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
|
||||
|
||||
public:
|
||||
CTcpPackClientT(ITcpClientListener* pListener)
|
||||
: T (pListener)
|
||||
, m_dwMaxPackSize (TCP_PACK_DEFAULT_MAX_SIZE)
|
||||
, m_usHeaderFlag (TCP_PACK_DEFAULT_HEADER_FLAG)
|
||||
, m_pkInfo (nullptr)
|
||||
, m_lsBuffer (m_itPool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPackClientT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwMaxPackSize;
|
||||
USHORT m_usHeaderFlag;
|
||||
|
||||
TPackInfo<TItemListEx> m_pkInfo;
|
||||
TItemListEx m_lsBuffer;
|
||||
};
|
||||
|
||||
typedef CTcpPackClientT<CTcpClient> CTcpPackClient;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLClient.h"
|
||||
typedef CTcpPackClientT<CSSLClient> CSSLPackClient;
|
||||
|
||||
#endif
|
||||
24
TcpPackServer.cpp
Normal file
24
TcpPackServer.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPackServer.h"
|
||||
221
TcpPackServer.h
Normal file
221
TcpPackServer.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpServer.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPackServerT : public IPackSocket, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::GetMaxConnectionCount;
|
||||
using __super::GetSocketBufferSize;
|
||||
using __super::GetFreeBufferObjPool;
|
||||
using __super::GetFreeBufferObjHold;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
using __super::SetLastError;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
int iNewCount = iCount + 1;
|
||||
unique_ptr<WSABUF[]> buffers(new WSABUF[iNewCount]);
|
||||
|
||||
DWORD dwHeader;
|
||||
if(!::AddPackHeader(pBuffers, iCount, buffers, m_dwMaxPackSize, m_usHeaderFlag, dwHeader))
|
||||
return FALSE;
|
||||
|
||||
return __super::SendPackets(dwConnID, buffers.get(), iNewCount);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireAccept(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool.PickFreeBuffer(pSocketObj->connID);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, TBufferPackInfo::Construct(pBuffer)));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
ASSERT(pInfo);
|
||||
|
||||
TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
return ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag, pData, iLength);
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_bfPool.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual BOOL BeforeUnpause(TSocketObj* pSocketObj)
|
||||
{
|
||||
if(!TSocketObj::IsValid(pSocketObj))
|
||||
return FALSE;
|
||||
|
||||
if(pSocketObj->IsPaused())
|
||||
return TRUE;
|
||||
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
ASSERT(pInfo);
|
||||
|
||||
TBuffer* pBuffer = (TBuffer*)pInfo->pBuffer;
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
return (ParsePack(this, pInfo, pBuffer, pSocketObj, m_dwMaxPackSize, m_usHeaderFlag) != HR_ERROR);
|
||||
}
|
||||
|
||||
virtual BOOL CheckParams()
|
||||
{
|
||||
if ((m_dwMaxPackSize > 0 && m_dwMaxPackSize <= TCP_PACK_MAX_SIZE_LIMIT) &&
|
||||
(m_usHeaderFlag >= 0 && m_usHeaderFlag <= TCP_PACK_HEADER_FLAG_LIMIT) )
|
||||
return __super::CheckParams();
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
virtual void PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_bfPool.SetMaxCacheSize (GetMaxConnectionCount());
|
||||
m_bfPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_bfPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_bfPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_bfPool.SetBufferLockTime (GetFreeSocketObjLockTime());
|
||||
m_bfPool.SetBufferPoolSize (GetFreeSocketObjPool());
|
||||
m_bfPool.SetBufferPoolHold (GetFreeSocketObjHold());
|
||||
|
||||
m_bfPool.Prepare();
|
||||
}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_bfPool.ReleaseGCBuffer(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
virtual void SetMaxPackSize (DWORD dwMaxPackSize) {ENSURE_HAS_STOPPED(); m_dwMaxPackSize = dwMaxPackSize;}
|
||||
virtual void SetPackHeaderFlag (USHORT usPackHeaderFlag) {ENSURE_HAS_STOPPED(); m_usHeaderFlag = usPackHeaderFlag;}
|
||||
virtual DWORD GetMaxPackSize () {return m_dwMaxPackSize;}
|
||||
virtual USHORT GetPackHeaderFlag() {return m_usHeaderFlag;}
|
||||
|
||||
private:
|
||||
void ReleaseConnectionExtra(TSocketObj* pSocketObj)
|
||||
{
|
||||
TBufferPackInfo* pInfo = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pInfo);
|
||||
|
||||
if(pInfo != nullptr)
|
||||
{
|
||||
m_bfPool.PutFreeBuffer(pInfo->pBuffer);
|
||||
TBufferPackInfo::Destruct(pInfo);
|
||||
|
||||
ENSURE(SetConnectionReserved(pSocketObj, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
EnHandleResult DoFireSuperReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return __super::DoFireReceive(pSocketObj, pData, iLength);}
|
||||
|
||||
friend EnHandleResult ParsePack<>(CTcpPackServerT* pThis, TBufferPackInfo* pInfo, TBuffer* pBuffer, TSocketObj* pSocket, DWORD dwMaxPackSize, USHORT usPackHeaderFlag);
|
||||
|
||||
public:
|
||||
CTcpPackServerT(ITcpServerListener* pListener)
|
||||
: T (pListener)
|
||||
, m_dwMaxPackSize (TCP_PACK_DEFAULT_MAX_SIZE)
|
||||
, m_usHeaderFlag (TCP_PACK_DEFAULT_HEADER_FLAG)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPackServerT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwMaxPackSize;
|
||||
USHORT m_usHeaderFlag;
|
||||
|
||||
CBufferPool m_bfPool;
|
||||
};
|
||||
|
||||
typedef CTcpPackServerT<CTcpServer> CTcpPackServer;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLServer.h"
|
||||
typedef CTcpPackServerT<CSSLServer> CSSLPackServer;
|
||||
|
||||
#endif
|
||||
24
TcpPullAgent.cpp
Normal file
24
TcpPullAgent.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPullAgent.h"
|
||||
173
TcpPullAgent.h
Normal file
173
TcpPullAgent.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpAgent.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPullAgentT : public IPullSocket, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::GetMaxConnectionCount;
|
||||
using __super::GetSocketBufferSize;
|
||||
using __super::GetFreeBufferObjPool;
|
||||
using __super::GetFreeBufferObjHold;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual EnFetchResult Fetch(CONNID dwConnID, BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool[dwConnID];
|
||||
return ::FetchBuffer(pBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
virtual EnFetchResult Peek(CONNID dwConnID, BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool[dwConnID];
|
||||
return ::PeekBuffer(pBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireConnect(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireConnect(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool.PutCacheBuffer(pSocketObj->connID);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, pBuffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireHandShake(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireReceive(TAgentSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
pBuffer->Cat(pData, iLength);
|
||||
|
||||
return __super::DoFireReceive(pSocketObj, pBuffer->Length());
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireClose(TAgentSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_bfPool.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_bfPool.SetMaxCacheSize (GetMaxConnectionCount());
|
||||
m_bfPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_bfPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_bfPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_bfPool.SetBufferLockTime (GetFreeSocketObjLockTime());
|
||||
m_bfPool.SetBufferPoolSize (GetFreeSocketObjPool());
|
||||
m_bfPool.SetBufferPoolHold (GetFreeSocketObjHold());
|
||||
|
||||
m_bfPool.Prepare();
|
||||
}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_bfPool.ReleaseGCBuffer(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void ReleaseConnectionExtra(TAgentSocketObj* pSocketObj)
|
||||
{
|
||||
TBuffer* pBuffer = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
|
||||
|
||||
if(pBuffer != nullptr)
|
||||
{
|
||||
m_bfPool.PutFreeBuffer(pBuffer);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CTcpPullAgentT(ITcpAgentListener* pListener)
|
||||
: T(pListener)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPullAgentT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
CBufferPool m_bfPool;
|
||||
};
|
||||
|
||||
typedef CTcpPullAgentT<CTcpAgent> CTcpPullAgent;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLAgent.h"
|
||||
typedef CTcpPullAgentT<CSSLAgent> CSSLPullAgent;
|
||||
|
||||
#endif
|
||||
24
TcpPullClient.cpp
Normal file
24
TcpPullClient.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPullClient.h"
|
||||
89
TcpPullClient.h
Normal file
89
TcpPullClient.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpClient.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPullClientT : public IPullClient, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::m_itPool;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual EnFetchResult Fetch(BYTE* pData, int iLength)
|
||||
{
|
||||
return ::FetchBuffer(&m_lsBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
virtual EnFetchResult Peek(BYTE* pData, int iLength)
|
||||
{
|
||||
return ::PeekBuffer(&m_lsBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireReceive(ITcpClient* pSender, const BYTE* pData, int iLength)
|
||||
{
|
||||
m_lsBuffer.Cat(pData, iLength);
|
||||
|
||||
return __super::DoFireReceive(pSender, m_lsBuffer.Length());
|
||||
}
|
||||
|
||||
virtual void Reset()
|
||||
{
|
||||
m_lsBuffer.Clear();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
public:
|
||||
CTcpPullClientT(ITcpClientListener* pListener)
|
||||
: T (pListener)
|
||||
, m_lsBuffer(m_itPool)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPullClientT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
TItemListEx m_lsBuffer;
|
||||
};
|
||||
|
||||
typedef CTcpPullClientT<CTcpClient> CTcpPullClient;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLClient.h"
|
||||
typedef CTcpPullClientT<CSSLClient> CSSLPullClient;
|
||||
|
||||
#endif
|
||||
24
TcpPullServer.cpp
Normal file
24
TcpPullServer.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "TcpPullServer.h"
|
||||
173
TcpPullServer.h
Normal file
173
TcpPullServer.h
Normal file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "TcpServer.h"
|
||||
#include "MiscHelper.h"
|
||||
|
||||
template<class T> class CTcpPullServerT : public IPullSocket, public T
|
||||
{
|
||||
using __super = T;
|
||||
using __super::SetConnectionReserved;
|
||||
using __super::GetConnectionReserved;
|
||||
using __super::GetMaxConnectionCount;
|
||||
using __super::GetSocketBufferSize;
|
||||
using __super::GetFreeBufferObjPool;
|
||||
using __super::GetFreeBufferObjHold;
|
||||
using __super::GetFreeSocketObjLockTime;
|
||||
using __super::GetFreeSocketObjPool;
|
||||
using __super::GetFreeSocketObjHold;
|
||||
|
||||
public:
|
||||
using __super::Stop;
|
||||
using __super::Wait;
|
||||
using __super::GetState;
|
||||
|
||||
public:
|
||||
virtual EnFetchResult Fetch(CONNID dwConnID, BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool[dwConnID];
|
||||
return ::FetchBuffer(pBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
virtual EnFetchResult Peek(CONNID dwConnID, BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool[dwConnID];
|
||||
return ::PeekBuffer(pBuffer, pData, iLength);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireAccept(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
{
|
||||
TBuffer* pBuffer = m_bfPool.PutCacheBuffer(pSocketObj->connID);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, pBuffer));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireHandShake(pSocketObj);
|
||||
|
||||
if(result == HR_ERROR)
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
TBuffer* pBuffer = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
|
||||
ASSERT(pBuffer && pBuffer->IsValid());
|
||||
|
||||
pBuffer->Cat(pData, iLength);
|
||||
|
||||
return __super::DoFireReceive(pSocketObj, pBuffer->Length());
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = __super::DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
ReleaseConnectionExtra(pSocketObj);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{
|
||||
EnHandleResult result = __super::DoFireShutdown();
|
||||
|
||||
m_bfPool.Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_bfPool.SetMaxCacheSize (GetMaxConnectionCount());
|
||||
m_bfPool.SetItemCapacity (GetSocketBufferSize());
|
||||
m_bfPool.SetItemPoolSize (GetFreeBufferObjPool());
|
||||
m_bfPool.SetItemPoolHold (GetFreeBufferObjHold());
|
||||
m_bfPool.SetBufferLockTime (GetFreeSocketObjLockTime());
|
||||
m_bfPool.SetBufferPoolSize (GetFreeSocketObjPool());
|
||||
m_bfPool.SetBufferPoolHold (GetFreeSocketObjHold());
|
||||
|
||||
m_bfPool.Prepare();
|
||||
}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_bfPool.ReleaseGCBuffer(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void ReleaseConnectionExtra(TSocketObj* pSocketObj)
|
||||
{
|
||||
TBuffer* pBuffer = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pBuffer);
|
||||
|
||||
if(pBuffer != nullptr)
|
||||
{
|
||||
m_bfPool.PutFreeBuffer(pBuffer);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CTcpPullServerT(ITcpServerListener* pListener)
|
||||
: T(pListener)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CTcpPullServerT()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
CBufferPool m_bfPool;
|
||||
};
|
||||
|
||||
typedef CTcpPullServerT<CTcpServer> CTcpPullServer;
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
|
||||
#include "SSLServer.h"
|
||||
typedef CTcpPullServerT<CSSLServer> CSSLPullServer;
|
||||
|
||||
#endif
|
||||
1187
TcpServer.cpp
Normal file
1187
TcpServer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
306
TcpServer.h
Normal file
306
TcpServer.h
Normal file
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
#include "./common/IODispatcher.h"
|
||||
|
||||
class CTcpServer : public ITcpServer, private CIOHandler
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszBindAddress, USHORT usPort);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendSmallFile (CONNID dwConnID, LPCTSTR lpszFileName, const LPWSABUF pHead = nullptr, const LPWSABUF pTail = nullptr);
|
||||
virtual BOOL SendPackets (CONNID dwConnID, const WSABUF pBuffers[], int iCount) {return DoSendPackets(dwConnID, pBuffers, iCount);}
|
||||
virtual BOOL PauseReceive (CONNID dwConnID, BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual BOOL Disconnect (CONNID dwConnID, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectLongConnections (DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL GetListenAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetLocalAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
|
||||
virtual BOOL IsConnected (CONNID dwConnID);
|
||||
virtual BOOL IsPauseReceive (CONNID dwConnID, BOOL& bPaused);
|
||||
virtual BOOL GetPendingDataLength (CONNID dwConnID, int& iPending);
|
||||
virtual DWORD GetConnectionCount ();
|
||||
virtual BOOL GetAllConnectionIDs (CONNID pIDs[], DWORD& dwCount);
|
||||
virtual BOOL GetConnectPeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual BOOL GetSilencePeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
#ifdef _SSL_SUPPORT
|
||||
virtual BOOL SetupSSLContext (int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr) {return FALSE;}
|
||||
virtual BOOL SetupSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr, Fn_SNI_ServerNameCallback fnServerNameCallback = nullptr) {return FALSE;}
|
||||
virtual int AddSSLContext (int iVerifyMode = SSL_VM_NONE, LPCTSTR lpszPemCertFile = nullptr, LPCTSTR lpszPemKeyFile = nullptr, LPCTSTR lpszKeyPassword = nullptr, LPCTSTR lpszCAPemCertFileOrPath = nullptr) {return FALSE;}
|
||||
virtual int AddSSLContextByMemory(int iVerifyMode = SSL_VM_NONE, LPCSTR lpszPemCert = nullptr, LPCSTR lpszPemKey = nullptr, LPCSTR lpszKeyPassword = nullptr, LPCSTR lpszCAPemCert = nullptr) {return FALSE;}
|
||||
virtual BOOL BindSSLServerName (LPCTSTR lpszServerName, int iContextIndex) {return FALSE;}
|
||||
virtual void CleanupSSLContext () {}
|
||||
|
||||
virtual BOOL StartSSLHandShake (CONNID dwConnID) {return FALSE;}
|
||||
virtual void SetSSLAutoHandShake(BOOL bAutoHandShake) {}
|
||||
virtual BOOL IsSSLAutoHandShake () {return FALSE;}
|
||||
virtual void SetSSLCipherList (LPCTSTR lpszCipherList){}
|
||||
virtual LPCTSTR GetSSLCipherList() {return nullptr;}
|
||||
virtual BOOL GetSSLSessionInfo(CONNID dwConnID, EnSSLSessionInfo enInfo, LPVOID* lppInfo) {return FALSE;}
|
||||
|
||||
protected:
|
||||
virtual BOOL StartSSLHandShake (TSocketObj* pSocketObj){return FALSE;}
|
||||
#endif
|
||||
|
||||
private:
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override;
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override;
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) override;
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) override;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
|
||||
virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
|
||||
virtual void SetSendPolicy (EnSendPolicy enSendPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
|
||||
virtual void SetOnSendSyncPolicy (EnOnSendSyncPolicy enOnSendSyncPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
|
||||
virtual void SetMaxConnectionCount (DWORD dwMaxConnectionCount) {ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount = dwMaxConnectionCount;}
|
||||
virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) {ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount = dwWorkerThreadCount;}
|
||||
virtual void SetSocketListenQueue (DWORD dwSocketListenQueue) {ENSURE_HAS_STOPPED(); m_dwSocketListenQueue = dwSocketListenQueue;}
|
||||
virtual void SetAcceptSocketCount (DWORD dwAcceptSocketCount) {ENSURE_HAS_STOPPED(); m_dwAcceptSocketCount = dwAcceptSocketCount;}
|
||||
virtual void SetSocketBufferSize (DWORD dwSocketBufferSize) {ENSURE_HAS_STOPPED(); m_dwSocketBufferSize = dwSocketBufferSize;}
|
||||
virtual void SetFreeSocketObjLockTime (DWORD dwFreeSocketObjLockTime) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime = dwFreeSocketObjLockTime;}
|
||||
virtual void SetFreeSocketObjPool (DWORD dwFreeSocketObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool = dwFreeSocketObjPool;}
|
||||
virtual void SetFreeBufferObjPool (DWORD dwFreeBufferObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool = dwFreeBufferObjPool;}
|
||||
virtual void SetFreeSocketObjHold (DWORD dwFreeSocketObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold = dwFreeSocketObjHold;}
|
||||
virtual void SetFreeBufferObjHold (DWORD dwFreeBufferObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold = dwFreeBufferObjHold;}
|
||||
virtual void SetKeepAliveTime (DWORD dwKeepAliveTime) {ENSURE_HAS_STOPPED(); m_dwKeepAliveTime = dwKeepAliveTime;}
|
||||
virtual void SetKeepAliveInterval (DWORD dwKeepAliveInterval) {ENSURE_HAS_STOPPED(); m_dwKeepAliveInterval = dwKeepAliveInterval;}
|
||||
virtual void SetMarkSilence (BOOL bMarkSilence) {ENSURE_HAS_STOPPED(); m_bMarkSilence = bMarkSilence;}
|
||||
virtual void SetNoDelay (BOOL bNoDelay) {ENSURE_HAS_STOPPED(); m_bNoDelay = bNoDelay;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual EnSendPolicy GetSendPolicy () {return m_enSendPolicy;}
|
||||
virtual EnOnSendSyncPolicy GetOnSendSyncPolicy () {return m_enOnSendSyncPolicy;}
|
||||
virtual DWORD GetMaxConnectionCount () {return m_dwMaxConnectionCount;}
|
||||
virtual DWORD GetWorkerThreadCount () {return m_dwWorkerThreadCount;}
|
||||
virtual DWORD GetSocketListenQueue () {return m_dwSocketListenQueue;}
|
||||
virtual DWORD GetAcceptSocketCount () {return m_dwAcceptSocketCount;}
|
||||
virtual DWORD GetSocketBufferSize () {return m_dwSocketBufferSize;}
|
||||
virtual DWORD GetFreeSocketObjLockTime () {return m_dwFreeSocketObjLockTime;}
|
||||
virtual DWORD GetFreeSocketObjPool () {return m_dwFreeSocketObjPool;}
|
||||
virtual DWORD GetFreeBufferObjPool () {return m_dwFreeBufferObjPool;}
|
||||
virtual DWORD GetFreeSocketObjHold () {return m_dwFreeSocketObjHold;}
|
||||
virtual DWORD GetFreeBufferObjHold () {return m_dwFreeBufferObjHold;}
|
||||
virtual DWORD GetKeepAliveTime () {return m_dwKeepAliveTime;}
|
||||
virtual DWORD GetKeepAliveInterval () {return m_dwKeepAliveInterval;}
|
||||
virtual BOOL IsMarkSilence () {return m_bMarkSilence;}
|
||||
virtual BOOL IsNoDelay () {return m_bNoDelay;}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareListen(SOCKET soListen)
|
||||
{return DoFirePrepareListen(soListen);}
|
||||
virtual EnHandleResult FireAccept(TSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult rs = DoFireAccept(pSocketObj);
|
||||
if(rs != HR_ERROR) rs = FireHandShake(pSocketObj);
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake(TSocketObj* pSocketObj)
|
||||
{return DoFireHandShake(pSocketObj);}
|
||||
virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireReceive(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(TSocketObj* pSocketObj, int iLength)
|
||||
{return DoFireReceive(pSocketObj, iLength);}
|
||||
virtual EnHandleResult FireSend(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireSend(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult FireShutdown()
|
||||
{return DoFireShutdown();}
|
||||
|
||||
virtual EnHandleResult DoFirePrepareListen(SOCKET soListen)
|
||||
{return m_pListener->OnPrepareListen(this, soListen);}
|
||||
virtual EnHandleResult DoFireAccept(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnAccept(this, pSocketObj->connID, pSocketObj->socket);}
|
||||
virtual EnHandleResult DoFireHandShake(TSocketObj* pSocketObj)
|
||||
{return m_pListener->OnHandShake(this, pSocketObj->connID);}
|
||||
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(TSocketObj* pSocketObj, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
|
||||
virtual EnHandleResult DoFireSend(TSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireClose(TSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{return m_pListener->OnShutdown(this);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual BOOL BeforeUnpause(TSocketObj* pSocketObj) {return TRUE;}
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
BOOL DoSendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
BOOL DoSendPackets(TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
|
||||
TSocketObj* FindSocketObj(CONNID dwConnID);
|
||||
|
||||
protected:
|
||||
BOOL SetConnectionExtra(TSocketObj* pSocketObj, PVOID pExtra);
|
||||
BOOL GetConnectionExtra(TSocketObj* pSocketObj, PVOID* ppExtra);
|
||||
BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved(TSocketObj* pSocketObj, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(TSocketObj* pSocketObj, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
|
||||
BOOL SetConnectionReserved2(TSocketObj* pSocketObj, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(TSocketObj* pSocketObj, PVOID* ppReserved2);
|
||||
|
||||
private:
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort);
|
||||
BOOL CreateWorkerThreads();
|
||||
BOOL StartAccept();
|
||||
|
||||
void CloseListenSocket();
|
||||
void DisconnectClientSocket();
|
||||
void WaitForClientSocketClose();
|
||||
void ReleaseClientSocket();
|
||||
void ReleaseFreeSocket();
|
||||
void WaitForWorkerThreadEnd();
|
||||
|
||||
TSocketObj* GetFreeSocketObj(CONNID dwConnID, SOCKET soClient);
|
||||
TSocketObj* CreateSocketObj();
|
||||
void AddFreeSocketObj (TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0);
|
||||
void DeleteSocketObj (TSocketObj* pSocketObj);
|
||||
BOOL InvalidSocketObj (TSocketObj* pSocketObj);
|
||||
void AddClientSocketObj (CONNID dwConnID, TSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr);
|
||||
void CloseClientSocketObj(TSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, int iShutdownFlag = SHUT_WR);
|
||||
|
||||
private:
|
||||
VOID HandleCmdSend (const TDispContext* pContext, CONNID dwConnID);
|
||||
VOID HandleCmdUnpause (const TDispContext* pContext, CONNID dwConnID);
|
||||
VOID HandleCmdDisconnect(const TDispContext* pContext, CONNID dwConnID, BOOL bForce);
|
||||
BOOL HandleAccept (const TDispContext* pContext, UINT events);
|
||||
BOOL HandleReceive (const TDispContext* pContext, TSocketObj* pSocketObj, int flag);
|
||||
BOOL HandleSend (const TDispContext* pContext, TSocketObj* pSocketObj, int flag);
|
||||
BOOL HandleClose (const TDispContext* pContext, TSocketObj* pSocketObj, EnSocketCloseFlag enFlag, UINT events);
|
||||
|
||||
int SendInternal (TSocketObj* pSocketObj, const WSABUF pBuffers[], int iCount);
|
||||
BOOL SendItem (TSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
|
||||
|
||||
public:
|
||||
CTcpServer(ITcpServerListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_fdGCTimer (INVALID_FD)
|
||||
, m_enReusePolicy (RAP_ADDR_AND_PORT)
|
||||
, m_enSendPolicy (SP_PACK)
|
||||
, m_enOnSendSyncPolicy (OSSP_RECEIVE)
|
||||
, m_dwMaxConnectionCount (DEFAULT_CONNECTION_COUNT)
|
||||
, m_dwWorkerThreadCount (DEFAULT_WORKER_THREAD_COUNT)
|
||||
, m_dwSocketListenQueue (DEFAULT_TCP_SERVER_SOCKET_LISTEN_QUEUE)
|
||||
, m_dwAcceptSocketCount (DEFAULT_WORKER_MAX_EVENT_COUNT)
|
||||
, m_dwSocketBufferSize (DEFAULT_TCP_SOCKET_BUFFER_SIZE)
|
||||
, m_dwFreeSocketObjLockTime (DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
|
||||
, m_dwFreeSocketObjPool (DEFAULT_FREE_SOCKETOBJ_POOL)
|
||||
, m_dwFreeBufferObjPool (DEFAULT_FREE_BUFFEROBJ_POOL)
|
||||
, m_dwFreeSocketObjHold (DEFAULT_FREE_SOCKETOBJ_HOLD)
|
||||
, m_dwFreeBufferObjHold (DEFAULT_FREE_BUFFEROBJ_HOLD)
|
||||
, m_dwKeepAliveTime (DEFALUT_TCP_KEEPALIVE_TIME)
|
||||
, m_dwKeepAliveInterval (DEFALUT_TCP_KEEPALIVE_INTERVAL)
|
||||
, m_bMarkSilence (TRUE)
|
||||
, m_bNoDelay (FALSE)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CTcpServer()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
EnSendPolicy m_enSendPolicy;
|
||||
EnOnSendSyncPolicy m_enOnSendSyncPolicy;
|
||||
DWORD m_dwMaxConnectionCount;
|
||||
DWORD m_dwWorkerThreadCount;
|
||||
DWORD m_dwSocketListenQueue;
|
||||
DWORD m_dwAcceptSocketCount;
|
||||
DWORD m_dwSocketBufferSize;
|
||||
DWORD m_dwFreeSocketObjLockTime;
|
||||
DWORD m_dwFreeSocketObjPool;
|
||||
DWORD m_dwFreeBufferObjPool;
|
||||
DWORD m_dwFreeSocketObjHold;
|
||||
DWORD m_dwFreeBufferObjHold;
|
||||
DWORD m_dwKeepAliveTime;
|
||||
DWORD m_dwKeepAliveInterval;
|
||||
BOOL m_bMarkSilence;
|
||||
BOOL m_bNoDelay;
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
ITcpServerListener* m_pListener;
|
||||
ListenSocketsPtr m_soListens;
|
||||
EnServiceState m_enState;
|
||||
EnSocketError m_enLastError;
|
||||
|
||||
CReceiveBuffersPtr m_rcBuffers;
|
||||
|
||||
CPrivateHeap m_phSocket;
|
||||
CBufferObjPool m_bfObjPool;
|
||||
|
||||
CSpinGuard m_csState;
|
||||
|
||||
FD m_fdGCTimer;
|
||||
|
||||
TSocketObjPtrPool m_bfActiveSockets;
|
||||
|
||||
TSocketObjPtrList m_lsFreeSocket;
|
||||
TSocketObjPtrQueue m_lsGCSocket;
|
||||
|
||||
CIODispatcher m_ioDispatcher;
|
||||
};
|
||||
186
UdpArqClient.cpp
Normal file
186
UdpArqClient.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "UdpArqClient.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
BOOL CUdpArqClient::CheckParams()
|
||||
{
|
||||
DWORD dwMaxDatagramSize = GetMaxDatagramSize();
|
||||
|
||||
if(m_dwMtu == 0)
|
||||
m_arqAttr.dwMtu = dwMaxDatagramSize;
|
||||
else
|
||||
{
|
||||
if(m_dwMtu > dwMaxDatagramSize)
|
||||
return FALSE;
|
||||
|
||||
m_arqAttr.dwMtu = m_dwMtu;
|
||||
}
|
||||
|
||||
return __super::CheckParams() && m_arqAttr.IsValid();
|
||||
}
|
||||
|
||||
void CUdpArqClient::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
}
|
||||
|
||||
void CUdpArqClient::Reset()
|
||||
{
|
||||
m_arqSession.Reset();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
void CUdpArqClient::OnWorkerThreadStart(THR_ID dwThreadID)
|
||||
{
|
||||
m_arqBuffer.Malloc(m_arqAttr.dwMaxMessageSize);
|
||||
m_arqTimer = ::CreateTimer(m_arqAttr.dwFlushInterval);
|
||||
}
|
||||
|
||||
void CUdpArqClient::OnWorkerThreadEnd(THR_ID dwThreadID)
|
||||
{
|
||||
if(IS_VALID_FD(m_arqTimer))
|
||||
{
|
||||
close(m_arqTimer);
|
||||
m_arqTimer = INVALID_FD;
|
||||
}
|
||||
|
||||
m_arqBuffer.Free();
|
||||
}
|
||||
|
||||
HANDLE CUdpArqClient::GetUserEvent()
|
||||
{
|
||||
return m_arqTimer;
|
||||
}
|
||||
|
||||
BOOL CUdpArqClient::OnUserEvent()
|
||||
{
|
||||
::ReadTimer(m_arqTimer);
|
||||
|
||||
return m_arqSession.Check();
|
||||
}
|
||||
|
||||
BOOL CUdpArqClient::Send(const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize)
|
||||
{
|
||||
if(IsConnected())
|
||||
{
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
result = m_arqSession.Send(pBuffer, iLength);
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL CUdpArqClient::SendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(!pBuffers || iCount <= 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if(iCount == 1)
|
||||
return Send((const BYTE*)pBuffers[0].buf, pBuffers[0].len);
|
||||
if(!IsConnected())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
int iLength = 0;
|
||||
int iMaxLen = (int)m_arqAttr.dwMaxMessageSize;
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
iLength += pBuffers[i].len;
|
||||
|
||||
if(iLength <= 0 || iLength > iMaxLen)
|
||||
return ERROR_INCORRECT_SIZE;
|
||||
|
||||
CBufferPtr sndBuffer(iLength);
|
||||
sndBuffer.SetSize(0);
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
sndBuffer.Cat(pBuffer, iBufLen);
|
||||
}
|
||||
}
|
||||
|
||||
int result = m_arqSession.Send(sndBuffer.Ptr(), (int)sndBuffer.Size());
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CUdpArqClient::ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv)
|
||||
{
|
||||
CUdpArqClient* pClient = (CUdpArqClient*)pv;
|
||||
|
||||
BOOL isOK = pClient->__super::Send((const BYTE*)pBuffer, iLength);
|
||||
|
||||
return isOK ? NO_ERROR : ::WSAGetLastError();
|
||||
}
|
||||
|
||||
EnHandleResult CUdpArqClient::FireConnect()
|
||||
{
|
||||
EnHandleResult result = DoFireConnect(this);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
m_arqSession.Renew(this, this, m_arqAttr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EnHandleResult CUdpArqClient::FireReceive(const BYTE* pData, int iLength)
|
||||
{
|
||||
return m_arqSession.Receive(pData, iLength, m_arqBuffer.Ptr(), (int)m_arqBuffer.Size());
|
||||
}
|
||||
|
||||
BOOL CUdpArqClient::GetWaitingSendMessageCount(int& iCount)
|
||||
{
|
||||
iCount = m_arqSession.GetWaitingSend();
|
||||
|
||||
return (iCount >= 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
114
UdpArqClient.h
Normal file
114
UdpArqClient.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UdpClient.h"
|
||||
#include "ArqHelper.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
class CUdpArqClient : public IArqClient, public CUdpClient
|
||||
{
|
||||
using __super = CUdpClient;
|
||||
|
||||
using CArqSession = CArqSessionT<CUdpArqClient, CUdpArqClient>;
|
||||
|
||||
friend typename CUdpArqClient::CArqSession;
|
||||
|
||||
public:
|
||||
virtual BOOL Send (const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendPackets(const WSABUF pBuffers[], int iCount);
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FireConnect();
|
||||
virtual EnHandleResult FireReceive(const BYTE* pData, int iLength);
|
||||
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID dwThreadID);
|
||||
virtual void OnWorkerThreadEnd(THR_ID dwThreadID);
|
||||
|
||||
virtual FD GetUserEvent();
|
||||
virtual BOOL OnUserEvent();
|
||||
|
||||
public:
|
||||
virtual void SetNoDelay (BOOL bNoDelay) {ENSURE_HAS_STOPPED(); m_arqAttr.bNoDelay = bNoDelay;}
|
||||
virtual void SetTurnoffCongestCtrl (BOOL bTurnOff) {ENSURE_HAS_STOPPED(); m_arqAttr.bTurnoffNc = bTurnOff;}
|
||||
virtual void SetFlushInterval (DWORD dwFlushInterval) {ENSURE_HAS_STOPPED(); m_arqAttr.dwFlushInterval = dwFlushInterval;}
|
||||
virtual void SetResendByAcks (DWORD dwResendByAcks) {ENSURE_HAS_STOPPED(); m_arqAttr.dwResendByAcks = dwResendByAcks;}
|
||||
virtual void SetSendWndSize (DWORD dwSendWndSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwSendWndSize = dwSendWndSize;}
|
||||
virtual void SetRecvWndSize (DWORD dwRecvWndSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwRecvWndSize = dwRecvWndSize;}
|
||||
virtual void SetMinRto (DWORD dwMinRto) {ENSURE_HAS_STOPPED(); m_arqAttr.dwMinRto = dwMinRto;}
|
||||
virtual void SetFastLimit (DWORD dwFastLimit) {ENSURE_HAS_STOPPED(); m_arqAttr.dwFastLimit = dwFastLimit;}
|
||||
virtual void SetMaxTransUnit (DWORD dwMaxTransUnit) {ENSURE_HAS_STOPPED(); m_dwMtu = dwMaxTransUnit;}
|
||||
virtual void SetMaxMessageSize (DWORD dwMaxMessageSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwMaxMessageSize = dwMaxMessageSize;}
|
||||
virtual void SetHandShakeTimeout (DWORD dwHandShakeTimeout) {ENSURE_HAS_STOPPED(); m_arqAttr.dwHandShakeTimeout = dwHandShakeTimeout;}
|
||||
|
||||
virtual BOOL IsNoDelay () {return m_arqAttr.bNoDelay;}
|
||||
virtual BOOL IsTurnoffCongestCtrl () {return m_arqAttr.bTurnoffNc;}
|
||||
virtual DWORD GetFlushInterval () {return m_arqAttr.dwFlushInterval;}
|
||||
virtual DWORD GetResendByAcks () {return m_arqAttr.dwResendByAcks;}
|
||||
virtual DWORD GetSendWndSize () {return m_arqAttr.dwSendWndSize;}
|
||||
virtual DWORD GetRecvWndSize () {return m_arqAttr.dwRecvWndSize;}
|
||||
virtual DWORD GetMinRto () {return m_arqAttr.dwMinRto;}
|
||||
virtual DWORD GetFastLimit () {return m_arqAttr.dwFastLimit;}
|
||||
virtual DWORD GetMaxTransUnit () {return m_dwMtu;}
|
||||
virtual DWORD GetMaxMessageSize () {return m_arqAttr.dwMaxMessageSize;}
|
||||
virtual DWORD GetHandShakeTimeout () {return m_arqAttr.dwHandShakeTimeout;}
|
||||
|
||||
virtual BOOL GetWaitingSendMessageCount (int& iCount);
|
||||
|
||||
public:
|
||||
const TArqAttr& GetArqAttribute () {return m_arqAttr;}
|
||||
Fn_ArqOutputProc GetArqOutputProc () {return ArqOutputProc;}
|
||||
|
||||
private:
|
||||
static int ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
|
||||
|
||||
public:
|
||||
CUdpArqClient(IUdpClientListener* pListener)
|
||||
: CUdpClient(pListener)
|
||||
, m_dwMtu (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CUdpArqClient()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwMtu;
|
||||
TArqAttr m_arqAttr;
|
||||
|
||||
CBufferPtr m_arqBuffer;
|
||||
FD m_arqTimer;
|
||||
|
||||
CArqSession m_arqSession;
|
||||
};
|
||||
|
||||
#endif
|
||||
253
UdpArqServer.cpp
Normal file
253
UdpArqServer.cpp
Normal file
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "UdpArqServer.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
BOOL CUdpArqServer::CheckParams()
|
||||
{
|
||||
DWORD dwMaxDatagramSize = GetMaxDatagramSize();
|
||||
|
||||
if(m_dwMtu == 0)
|
||||
m_arqAttr.dwMtu = dwMaxDatagramSize;
|
||||
else
|
||||
{
|
||||
if(m_dwMtu > dwMaxDatagramSize)
|
||||
return FALSE;
|
||||
|
||||
m_arqAttr.dwMtu = m_dwMtu;
|
||||
}
|
||||
|
||||
return __super::CheckParams() && m_arqAttr.IsValid();
|
||||
}
|
||||
|
||||
void CUdpArqServer::PrepareStart()
|
||||
{
|
||||
__super::PrepareStart();
|
||||
|
||||
m_ssPool.SetSessionLockTime(GetFreeSocketObjLockTime());
|
||||
m_ssPool.SetSessionPoolSize(GetFreeSocketObjPool());
|
||||
m_ssPool.SetSessionPoolHold(GetFreeSocketObjHold());
|
||||
|
||||
m_ssPool.Prepare();
|
||||
}
|
||||
|
||||
void CUdpArqServer::Reset()
|
||||
{
|
||||
::ClearPtrMap(m_rcBuffers);
|
||||
|
||||
m_ssPool.Clear();
|
||||
|
||||
__super::Reset();
|
||||
}
|
||||
|
||||
void CUdpArqServer::OnWorkerThreadStart(THR_ID dwThreadID)
|
||||
{
|
||||
{
|
||||
CCriSecLock locallock(m_csRcBuffers);
|
||||
m_rcBuffers[dwThreadID] = new CBufferPtr(m_arqAttr.dwMaxMessageSize);
|
||||
}
|
||||
|
||||
while((DWORD)m_rcBuffers.size() < GetWorkerThreadCount())
|
||||
::WaitFor(3);
|
||||
}
|
||||
|
||||
void CUdpArqServer::ReleaseGCSocketObj(BOOL bForce)
|
||||
{
|
||||
__super::ReleaseGCSocketObj(bForce);
|
||||
|
||||
#ifdef USE_EXTERNAL_GC
|
||||
m_ssPool.ReleaseGCSession(bForce);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL CUdpArqServer::Send(CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(pBuffer && iLength > 0 && iLength <= (int)m_arqAttr.dwMaxMessageSize)
|
||||
{
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(TUdpSocketObj::IsValid(pSocketObj))
|
||||
result = SendArq(pSocketObj, pBuffer, iLength);
|
||||
else
|
||||
result = ERROR_OBJECT_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL CUdpArqServer::SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(!pBuffers || iCount <= 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if(iCount == 1)
|
||||
return Send(dwConnID, (const BYTE*)pBuffers[0].buf, pBuffers[0].len);
|
||||
|
||||
TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TUdpSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int iLength = 0;
|
||||
int iMaxLen = (int)m_arqAttr.dwMaxMessageSize;
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
iLength += pBuffers[i].len;
|
||||
|
||||
if(iLength <= 0 || iLength > iMaxLen)
|
||||
return ERROR_INCORRECT_SIZE;
|
||||
|
||||
CBufferPtr sndBuffer(iLength);
|
||||
sndBuffer.SetSize(0);
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
sndBuffer.Cat(pBuffer, iBufLen);
|
||||
}
|
||||
}
|
||||
|
||||
int result = SendArq(pSocketObj, sndBuffer.Ptr(), (int)sndBuffer.Size());
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CUdpArqServer::SendArq(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength)
|
||||
{
|
||||
CArqSessionEx* pSession = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession == nullptr)
|
||||
return ERROR_OBJECT_NOT_FOUND;
|
||||
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
return pSession->Send(pBuffer, iLength);
|
||||
}
|
||||
|
||||
int CUdpArqServer::ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv)
|
||||
{
|
||||
TUdpSocketObj* pSocketObj = (TUdpSocketObj*)pv;
|
||||
|
||||
if(!TUdpSocketObj::IsValid(pSocketObj))
|
||||
return ERROR_OBJECT_NOT_FOUND;
|
||||
|
||||
CUdpArqServer* pServer = (CUdpArqServer*)IUdpArqServer::FromS((IUdpServer*)pSocketObj->pHolder);
|
||||
|
||||
TItemPtr itPtr(pServer->m_bfObjPool, pServer->m_bfObjPool.PickFreeItem());
|
||||
itPtr->Cat((const BYTE*)pBuffer, iLength);
|
||||
|
||||
return pServer->SendInternal(pSocketObj, itPtr);
|
||||
}
|
||||
|
||||
EnHandleResult CUdpArqServer::FireAccept(TUdpSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult result = DoFireAccept(pSocketObj);
|
||||
|
||||
if(result != HR_ERROR)
|
||||
{
|
||||
CArqSessionEx* pSession = m_ssPool.PickFreeSession(this, pSocketObj, m_arqAttr);
|
||||
ENSURE(SetConnectionReserved(pSocketObj, pSession));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
EnHandleResult CUdpArqServer::FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{
|
||||
CArqSessionEx* pSession = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
CBufferPtr& rcBuffer = *m_rcBuffers[SELF_THREAD_ID];
|
||||
return pSession->Receive(pData, iLength, rcBuffer.Ptr(), (int)rcBuffer.Size());
|
||||
}
|
||||
|
||||
EnHandleResult CUdpArqServer::FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
EnHandleResult result = DoFireClose(pSocketObj, enOperation, iErrorCode);
|
||||
|
||||
CArqSessionEx* pSession = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession != nullptr)
|
||||
m_ssPool.PutFreeSession(pSession);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL CUdpArqServer::GetWaitingSendMessageCount(CONNID dwConnID, int& iCount)
|
||||
{
|
||||
TUdpSocketObj* pSocketObj = FindSocketObj(dwConnID);
|
||||
|
||||
if(!TUdpSocketObj::IsValid(pSocketObj))
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CArqSessionEx* pSession = nullptr;
|
||||
GetConnectionReserved(pSocketObj, (PVOID*)&pSession);
|
||||
|
||||
if(pSession == nullptr)
|
||||
{
|
||||
::SetLastError(ERROR_OBJECT_NOT_FOUND);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
{
|
||||
CLocalSafeCounter localcounter(*pSession);
|
||||
|
||||
iCount = pSession->GetWaitingSend();
|
||||
}
|
||||
|
||||
return (iCount >= 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
120
UdpArqServer.h
Normal file
120
UdpArqServer.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UdpServer.h"
|
||||
#include "ArqHelper.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
#include "common/STLHelper.h"
|
||||
|
||||
class CUdpArqServer : public IArqSocket, public CUdpServer
|
||||
{
|
||||
using __super = CUdpServer;
|
||||
|
||||
using CArqSession = CArqSessionT<CUdpArqServer, TUdpSocketObj>;
|
||||
using CArqSessionEx = CArqSessionExT<CUdpArqServer, TUdpSocketObj>;
|
||||
using CArqSessionPool = CArqSessionPoolT<CUdpArqServer, TUdpSocketObj>;
|
||||
using CRecvBufferMap = unordered_map<THR_ID, CBufferPtr*>;
|
||||
|
||||
friend typename CUdpArqServer::CArqSession;
|
||||
|
||||
public:
|
||||
virtual BOOL Send (CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendPackets(CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FireAccept(TUdpSocketObj* pSocketObj);
|
||||
virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength);
|
||||
virtual EnHandleResult FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
virtual void OnWorkerThreadStart(THR_ID dwThreadID);
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
public:
|
||||
virtual void SetNoDelay (BOOL bNoDelay) {ENSURE_HAS_STOPPED(); m_arqAttr.bNoDelay = bNoDelay;}
|
||||
virtual void SetTurnoffCongestCtrl (BOOL bTurnOff) {ENSURE_HAS_STOPPED(); m_arqAttr.bTurnoffNc = bTurnOff;}
|
||||
virtual void SetFlushInterval (DWORD dwFlushInterval) {ENSURE_HAS_STOPPED(); m_arqAttr.dwFlushInterval = dwFlushInterval;}
|
||||
virtual void SetResendByAcks (DWORD dwResendByAcks) {ENSURE_HAS_STOPPED(); m_arqAttr.dwResendByAcks = dwResendByAcks;}
|
||||
virtual void SetSendWndSize (DWORD dwSendWndSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwSendWndSize = dwSendWndSize;}
|
||||
virtual void SetRecvWndSize (DWORD dwRecvWndSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwRecvWndSize = dwRecvWndSize;}
|
||||
virtual void SetMinRto (DWORD dwMinRto) {ENSURE_HAS_STOPPED(); m_arqAttr.dwMinRto = dwMinRto;}
|
||||
virtual void SetFastLimit (DWORD dwFastLimit) {ENSURE_HAS_STOPPED(); m_arqAttr.dwFastLimit = dwFastLimit;}
|
||||
virtual void SetMaxTransUnit (DWORD dwMaxTransUnit) {ENSURE_HAS_STOPPED(); m_dwMtu = dwMaxTransUnit;}
|
||||
virtual void SetMaxMessageSize (DWORD dwMaxMessageSize) {ENSURE_HAS_STOPPED(); m_arqAttr.dwMaxMessageSize = dwMaxMessageSize;}
|
||||
virtual void SetHandShakeTimeout (DWORD dwHandShakeTimeout) {ENSURE_HAS_STOPPED(); m_arqAttr.dwHandShakeTimeout = dwHandShakeTimeout;}
|
||||
|
||||
virtual BOOL IsNoDelay () {return m_arqAttr.bNoDelay;}
|
||||
virtual BOOL IsTurnoffCongestCtrl () {return m_arqAttr.bTurnoffNc;}
|
||||
virtual DWORD GetFlushInterval () {return m_arqAttr.dwFlushInterval;}
|
||||
virtual DWORD GetResendByAcks () {return m_arqAttr.dwResendByAcks;}
|
||||
virtual DWORD GetSendWndSize () {return m_arqAttr.dwSendWndSize;}
|
||||
virtual DWORD GetRecvWndSize () {return m_arqAttr.dwRecvWndSize;}
|
||||
virtual DWORD GetMinRto () {return m_arqAttr.dwMinRto;}
|
||||
virtual DWORD GetFastLimit () {return m_arqAttr.dwFastLimit;}
|
||||
virtual DWORD GetMaxTransUnit () {return m_dwMtu;}
|
||||
virtual DWORD GetMaxMessageSize () {return m_arqAttr.dwMaxMessageSize;}
|
||||
virtual DWORD GetHandShakeTimeout () {return m_arqAttr.dwHandShakeTimeout;}
|
||||
|
||||
virtual BOOL GetWaitingSendMessageCount (CONNID dwConnID, int& iCount);
|
||||
|
||||
public:
|
||||
const TArqAttr& GetArqAttribute () {return m_arqAttr;}
|
||||
Fn_ArqOutputProc GetArqOutputProc () {return ArqOutputProc;}
|
||||
|
||||
private:
|
||||
int SendArq(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength);
|
||||
|
||||
static int ArqOutputProc(const char* pBuffer, int iLength, IKCPCB* kcp, LPVOID pv);
|
||||
|
||||
public:
|
||||
CUdpArqServer(IUdpServerListener* pListener)
|
||||
: CUdpServer(pListener)
|
||||
, m_ssPool (this)
|
||||
, m_dwMtu (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~CUdpArqServer()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
DWORD m_dwMtu;
|
||||
TArqAttr m_arqAttr;
|
||||
|
||||
CCriSec m_csRcBuffers;
|
||||
CRecvBufferMap m_rcBuffers;
|
||||
|
||||
CArqSessionPool m_ssPool;
|
||||
};
|
||||
|
||||
#endif
|
||||
701
UdpCast.cpp
Normal file
701
UdpCast.cpp
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "UdpCast.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
BOOL CUdpCast::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
|
||||
{
|
||||
ASSERT(usLocalPort == 0);
|
||||
|
||||
if(!CheckParams() || !CheckStarting())
|
||||
return FALSE;
|
||||
|
||||
PrepareStart();
|
||||
m_ccContext.Reset();
|
||||
|
||||
BOOL isOK = FALSE;
|
||||
HP_SOCKADDR bindAddr(AF_UNSPEC, TRUE);
|
||||
|
||||
if(CreateClientSocket(lpszRemoteAddress, usPort, lpszBindAddress, bindAddr))
|
||||
{
|
||||
if(BindClientSocket(bindAddr))
|
||||
{
|
||||
if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
|
||||
{
|
||||
if(ConnectToGroup(bindAddr))
|
||||
{
|
||||
if(CreateWorkerThread())
|
||||
{
|
||||
isOK = TRUE;
|
||||
}
|
||||
else
|
||||
SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
|
||||
if(!isOK)
|
||||
{
|
||||
m_ccContext.Reset(FALSE);
|
||||
EXECUTE_RESTORE_ERROR(Stop());
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::CheckParams()
|
||||
{
|
||||
if (((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE) &&
|
||||
((int)m_dwFreeBufferPoolSize >= 0) &&
|
||||
((int)m_dwFreeBufferPoolHold >= 0) &&
|
||||
(m_enCastMode >= CM_MULTICAST && m_enCastMode <= CM_BROADCAST) &&
|
||||
(m_iMCTtl >= 0 && m_iMCTtl <= 255) &&
|
||||
(m_bMCLoop == TRUE || m_bMCLoop == FALSE) )
|
||||
return TRUE;
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CUdpCast::PrepareStart()
|
||||
{
|
||||
m_itPool.SetItemCapacity(m_dwMaxDatagramSize);
|
||||
m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
|
||||
m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
|
||||
|
||||
m_itPool.Prepare();
|
||||
}
|
||||
|
||||
BOOL CUdpCast::CheckStarting()
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(m_enState == SS_STOPPED)
|
||||
m_enState = SS_STARTING;
|
||||
else
|
||||
{
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::CheckStoping()
|
||||
{
|
||||
if(m_enState != SS_STOPPED)
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(HasStarted())
|
||||
{
|
||||
m_enState = SS_STOPPING;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& bindAddr)
|
||||
{
|
||||
HP_SCOPE_HOST host(lpszRemoteAddress);
|
||||
LPCTSTR lpszRealAddress = host.addr;
|
||||
|
||||
if(m_enCastMode == CM_BROADCAST && ::IsStrEmpty(lpszRealAddress))
|
||||
lpszRealAddress = DEFAULT_IPV4_BROAD_CAST_ADDRESS;
|
||||
|
||||
if(!::GetSockAddrByHostName(lpszRealAddress, usPort, m_castAddr))
|
||||
return FALSE;
|
||||
|
||||
if(::IsStrEmpty(lpszBindAddress))
|
||||
{
|
||||
bindAddr.family = m_castAddr.family;
|
||||
bindAddr.SetPort(usPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, bindAddr))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_enCastMode == CM_BROADCAST && bindAddr.IsIPv6())
|
||||
{
|
||||
::WSASetLastError(ERROR_PFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_castAddr.family != bindAddr.family)
|
||||
{
|
||||
::WSASetLastError(ERROR_AFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_soClient = socket(m_castAddr.family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if(m_soClient == INVALID_SOCKET)
|
||||
return FALSE;
|
||||
|
||||
VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
|
||||
VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
|
||||
|
||||
SetRemoteHost(host.name, usPort);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::BindClientSocket(HP_SOCKADDR& bindAddr)
|
||||
{
|
||||
HP_SOCKADDR anyAddr = HP_SOCKADDR::AnyAddr(m_castAddr.family);
|
||||
anyAddr.SetPort(m_castAddr.Port());
|
||||
|
||||
if(::bind(m_soClient, anyAddr.Addr(), anyAddr.AddrSize()) == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
|
||||
m_dwConnID = ::GenerateConnectionID();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::ConnectToGroup(const HP_SOCKADDR& bindAddr)
|
||||
{
|
||||
if(m_enCastMode == CM_MULTICAST)
|
||||
{
|
||||
if(!::SetMultiCastSocketOptions(m_soClient, bindAddr, m_castAddr, m_iMCTtl, m_bMCLoop))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(m_castAddr.IsIPv4());
|
||||
|
||||
UINT iSet = 1;
|
||||
VERIFY(::SSO_SetSocketOption(m_soClient, SOL_SOCKET, SO_BROADCAST, &iSet, sizeof(UINT)) != SOCKET_ERROR);
|
||||
}
|
||||
|
||||
SetConnected();
|
||||
|
||||
if(TRIGGER(FireConnect()) == HR_ERROR)
|
||||
{
|
||||
::WSASetLastError(ENSURE_ERROR_CANCELLED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::Stop()
|
||||
{
|
||||
if(!CheckStoping())
|
||||
return FALSE;
|
||||
|
||||
WaitForWorkerThreadEnd();
|
||||
|
||||
SetConnected(FALSE);
|
||||
|
||||
if(m_ccContext.bFireOnClose)
|
||||
FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
|
||||
|
||||
if(m_soClient != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(m_soClient, SHUT_WR);
|
||||
closesocket(m_soClient);
|
||||
|
||||
m_soClient = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CUdpCast::Reset()
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
m_evSend.Reset();
|
||||
m_evRecv.Reset();
|
||||
m_evStop.Reset();
|
||||
|
||||
m_lsSend.Clear();
|
||||
m_itPool.Clear();
|
||||
m_rcBuffer.Free();
|
||||
|
||||
m_castAddr.Reset();
|
||||
m_remoteAddr.Reset();
|
||||
|
||||
m_strHost.Empty();
|
||||
|
||||
m_usPort = 0;
|
||||
m_nEvents = 0;
|
||||
m_bPaused = FALSE;
|
||||
m_enState = SS_STOPPED;
|
||||
|
||||
m_evWait.SyncNotifyAll();
|
||||
}
|
||||
|
||||
void CUdpCast::WaitForWorkerThreadEnd()
|
||||
{
|
||||
if(!m_thWorker.IsRunning())
|
||||
return;
|
||||
|
||||
if(m_thWorker.IsInMyThread())
|
||||
m_thWorker.Detach();
|
||||
else
|
||||
{
|
||||
m_evStop.Set();
|
||||
m_thWorker.Join();
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CUdpCast::CreateWorkerThread()
|
||||
{
|
||||
return m_thWorker.Start(this, &CUdpCast::WorkerThreadProc);
|
||||
}
|
||||
|
||||
UINT WINAPI CUdpCast::WorkerThreadProc(LPVOID pv)
|
||||
{
|
||||
::SetCurrentWorkerThreadName();
|
||||
|
||||
TRACE("---------------> Cast Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
|
||||
|
||||
OnWorkerThreadStart(SELF_THREAD_ID);
|
||||
|
||||
BOOL bCallStop = TRUE;
|
||||
pollfd pfds[] = { {m_soClient, m_nEvents},
|
||||
{m_evSend.GetFD(), POLLIN},
|
||||
{m_evRecv.GetFD(), POLLIN},
|
||||
{m_evStop.GetFD(), POLLIN} };
|
||||
int size = ARRAY_SIZE(pfds);
|
||||
|
||||
m_rcBuffer.Malloc(m_dwMaxDatagramSize);
|
||||
|
||||
while(HasStarted())
|
||||
{
|
||||
int rs = (int)::PollForMultipleObjects(pfds, size);
|
||||
ASSERT(rs > TIMEOUT);
|
||||
|
||||
if(rs <= 0)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
|
||||
for(int i = 0; i < size; i++)
|
||||
{
|
||||
if((1 << i) & rs)
|
||||
{
|
||||
SHORT revents = pfds[i].revents;
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
if(!ProcessNetworkEvent(revents))
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 1)
|
||||
{
|
||||
m_evSend.Reset();
|
||||
|
||||
if(!SendData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 2)
|
||||
{
|
||||
m_evRecv.Reset();
|
||||
|
||||
if(!ReadData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 3)
|
||||
{
|
||||
m_evStop.Reset();
|
||||
|
||||
bCallStop = FALSE;
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else
|
||||
VERIFY(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
pfds[0].events = m_nEvents;
|
||||
}
|
||||
|
||||
EXIT_WORKER_THREAD:
|
||||
|
||||
OnWorkerThreadEnd(SELF_THREAD_ID);
|
||||
|
||||
if(bCallStop && HasStarted())
|
||||
Stop();
|
||||
|
||||
TRACE("---------------> Cast Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::ProcessNetworkEvent(SHORT events)
|
||||
{
|
||||
ASSERT(IsConnected());
|
||||
|
||||
BOOL bContinue = TRUE;
|
||||
|
||||
if(bContinue && events & POLLERR)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
if(bContinue && events & POLLIN)
|
||||
bContinue = HandleRead(events);
|
||||
|
||||
if(bContinue && events & POLLOUT)
|
||||
bContinue = HandleWrite(events);
|
||||
|
||||
if(bContinue && events & _POLL_HUNGUP_EVENTS)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
return bContinue;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::HandleClose(SHORT events)
|
||||
{
|
||||
EnSocketOperation enOperation = SO_CLOSE;
|
||||
|
||||
if(events & _POLL_HUNGUP_EVENTS)
|
||||
enOperation = SO_CLOSE;
|
||||
else if(events & POLLIN)
|
||||
enOperation = SO_RECEIVE;
|
||||
else if(events & POLLOUT)
|
||||
enOperation = SO_SEND;
|
||||
|
||||
m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::HandleRead(SHORT events)
|
||||
{
|
||||
return ReadData();
|
||||
}
|
||||
|
||||
BOOL CUdpCast::HandleWrite(SHORT events)
|
||||
{
|
||||
return SendData();
|
||||
}
|
||||
|
||||
BOOL CUdpCast::ReadData()
|
||||
{
|
||||
while(TRUE)
|
||||
{
|
||||
if(m_bPaused)
|
||||
break;
|
||||
|
||||
socklen_t addrLen = (socklen_t)m_remoteAddr.AddrSize();
|
||||
int rc = (int)recvfrom(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwMaxDatagramSize, MSG_TRUNC, m_remoteAddr.Addr(), &addrLen);
|
||||
|
||||
if(rc >= 0)
|
||||
{
|
||||
if(rc > (int)m_dwMaxDatagramSize)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, ERROR_BAD_LENGTH);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
|
||||
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::PauseReceive(BOOL bPause)
|
||||
{
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_bPaused == bPause)
|
||||
return TRUE;
|
||||
|
||||
m_bPaused = bPause;
|
||||
|
||||
if(!bPause)
|
||||
return m_evRecv.Set();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::SendData()
|
||||
{
|
||||
BOOL bBlocked = FALSE;
|
||||
|
||||
while(m_lsSend.Length() > 0)
|
||||
{
|
||||
TItemPtr itPtr(m_itPool);
|
||||
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
itPtr = m_lsSend.PopFront();
|
||||
}
|
||||
|
||||
if(!itPtr.IsValid())
|
||||
break;
|
||||
|
||||
if(!DoSendData(itPtr, bBlocked))
|
||||
return FALSE;
|
||||
|
||||
if(bBlocked)
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
m_lsSend.PushFront(itPtr.Detach());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::DoSendData(TItem* pItem, BOOL& bBlocked)
|
||||
{
|
||||
int rc = (int)sendto(m_soClient, (char*)pItem->Ptr(), pItem->Size(), 0, m_castAddr.Addr(), m_castAddr.AddrSize());
|
||||
|
||||
if(rc >= 0)
|
||||
{
|
||||
ASSERT(rc == pItem->Size());
|
||||
|
||||
if(rc == 0)
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
m_lsSend.ReduceLength(1);
|
||||
}
|
||||
|
||||
if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
bBlocked = TRUE;
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_SEND, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::Send(const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize)
|
||||
{
|
||||
if(IsConnected())
|
||||
{
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
|
||||
TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
|
||||
itPtr->Cat(pBuffer, iLength);
|
||||
|
||||
result = SendInternal(itPtr);
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL CUdpCast::SendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(!pBuffers || iCount <= 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if(!IsConnected())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
int result = NO_ERROR;
|
||||
int iLength = 0;
|
||||
int iMaxLen = (int)m_dwMaxDatagramSize;
|
||||
|
||||
TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
iLength += iBufLen;
|
||||
|
||||
if(iLength <= iMaxLen)
|
||||
itPtr->Cat(pBuffer, iBufLen);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(iLength >= 0 && iLength <= iMaxLen)
|
||||
result = SendInternal(itPtr);
|
||||
else
|
||||
result = ERROR_INCORRECT_SIZE;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CUdpCast::SendInternal(TItemPtr& itPtr)
|
||||
{
|
||||
int iPending;
|
||||
int iBufferSize;
|
||||
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
if(!IsConnected())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
iPending = m_lsSend.Length();
|
||||
iBufferSize = itPtr->Size();
|
||||
|
||||
m_lsSend.PushBack(itPtr.Detach());
|
||||
if(iBufferSize == 0) m_lsSend.IncreaseLength(1);
|
||||
|
||||
ASSERT(m_lsSend.Length() > 0);
|
||||
}
|
||||
|
||||
if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void CUdpCast::SetLastError(EnSocketError code, LPCSTR func, int ec)
|
||||
{
|
||||
TRACE("%s --> Error: %d, EC: %d", func, code, ec);
|
||||
|
||||
m_enLastError = code;
|
||||
::SetLastError(ec);
|
||||
}
|
||||
|
||||
BOOL CUdpCast::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ASSERT(lpszAddress != nullptr && iAddressLen > 0);
|
||||
|
||||
return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
void CUdpCast::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
|
||||
{
|
||||
m_strHost = lpszHost;
|
||||
m_usPort = usPort;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
if(m_strHost.IsEmpty())
|
||||
return isOK;
|
||||
|
||||
int iLen = m_strHost.GetLength() + 1;
|
||||
|
||||
if(iHostLen >= iLen)
|
||||
{
|
||||
memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
|
||||
usPort = m_usPort;
|
||||
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
iHostLen = iLen;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpCast::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
|
||||
{
|
||||
*lpszHost = m_strHost;
|
||||
|
||||
if(pusPort != nullptr)
|
||||
*pusPort = m_usPort;
|
||||
|
||||
return !m_strHost.IsEmpty();
|
||||
}
|
||||
|
||||
#endif
|
||||
219
UdpCast.h
Normal file
219
UdpCast.h
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
class CUdpCast : public IUdpCast
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendPackets (const WSABUF pBuffers[], int iCount);
|
||||
virtual BOOL PauseReceive (BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual CONNID GetConnectionID () {return m_dwConnID;}
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteHost (TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
|
||||
virtual BOOL GetPendingDataLength (int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
|
||||
virtual BOOL IsPauseReceive (BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
|
||||
virtual BOOL IsConnected () {return m_bConnected;}
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy = enReusePolicy;}
|
||||
virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) {ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize = dwMaxDatagramSize;}
|
||||
virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize = dwFreeBufferPoolSize;}
|
||||
virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold = dwFreeBufferPoolHold;}
|
||||
virtual void SetCastMode (EnCastMode enCastMode) {ENSURE_HAS_STOPPED(); m_enCastMode = enCastMode;}
|
||||
virtual void SetMultiCastTtl (int iMCTtl) {ENSURE_HAS_STOPPED(); m_iMCTtl = iMCTtl;}
|
||||
virtual void SetMultiCastLoop (BOOL bMCLoop) {ENSURE_HAS_STOPPED(); m_bMCLoop = bMCLoop;}
|
||||
virtual void SetExtra (PVOID pExtra) {m_pExtra = pExtra;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual DWORD GetMaxDatagramSize () {return m_dwMaxDatagramSize;}
|
||||
virtual DWORD GetFreeBufferPoolSize () {return m_dwFreeBufferPoolSize;}
|
||||
virtual DWORD GetFreeBufferPoolHold () {return m_dwFreeBufferPoolHold;}
|
||||
virtual EnCastMode GetCastMode () {return m_enCastMode;}
|
||||
virtual int GetMultiCastTtl () {return m_iMCTtl;}
|
||||
virtual BOOL IsMultiCastLoop () {return m_bMCLoop;}
|
||||
virtual PVOID GetExtra () {return m_pExtra;}
|
||||
|
||||
virtual BOOL GetRemoteAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ADDRESS_FAMILY usFamily;
|
||||
return ::sockaddr_IN_2_A(m_remoteAddr, usFamily, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareConnect(SOCKET socket)
|
||||
{return m_pListener->OnPrepareConnect(this, m_dwConnID, socket);}
|
||||
virtual EnHandleResult FireConnect()
|
||||
{
|
||||
EnHandleResult rs = m_pListener->OnConnect(this, m_dwConnID);
|
||||
if(rs != HR_ERROR) rs = FireHandShake();
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake()
|
||||
{return m_pListener->OnHandShake(this, m_dwConnID);}
|
||||
virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(this, m_dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(this, m_dwConnID, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(int iLength)
|
||||
{return m_pListener->OnReceive(this, m_dwConnID, iLength);}
|
||||
virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(this, m_dwConnID, enOperation, iErrorCode);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
protected:
|
||||
void SetReserved (PVOID pReserved) {m_pReserved = pReserved;}
|
||||
PVOID GetReserved () {return m_pReserved;}
|
||||
BOOL GetRemoteHost (LPCSTR* lpszHost, USHORT* pusPort = nullptr);
|
||||
|
||||
private:
|
||||
void SetRemoteHost (LPCTSTR lpszHost, USHORT usPort);
|
||||
void SetConnected (BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
|
||||
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& bindAddr);
|
||||
BOOL BindClientSocket(HP_SOCKADDR& bindAddr);
|
||||
BOOL ConnectToGroup(const HP_SOCKADDR& bindAddr);
|
||||
BOOL CreateWorkerThread();
|
||||
BOOL ProcessNetworkEvent(SHORT events);
|
||||
BOOL ReadData();
|
||||
BOOL SendData();
|
||||
BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
|
||||
int SendInternal(TItemPtr& itPtr);
|
||||
void WaitForWorkerThreadEnd();
|
||||
|
||||
BOOL HandleClose(SHORT events);
|
||||
BOOL HandleRead(SHORT events);
|
||||
BOOL HandleWrite(SHORT events);
|
||||
|
||||
UINT WINAPI WorkerThreadProc(LPVOID pv);
|
||||
|
||||
public:
|
||||
CUdpCast(IUdpCastListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_lsSend (m_itPool)
|
||||
, m_soClient (INVALID_SOCKET)
|
||||
, m_nEvents (0)
|
||||
, m_dwConnID (0)
|
||||
, m_usPort (0)
|
||||
, m_bPaused (FALSE)
|
||||
, m_bConnected (FALSE)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_pExtra (nullptr)
|
||||
, m_pReserved (nullptr)
|
||||
, m_enReusePolicy (RAP_ADDR_ONLY)
|
||||
, m_dwMaxDatagramSize (DEFAULT_UDP_MAX_DATAGRAM_SIZE)
|
||||
, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
|
||||
, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
|
||||
, m_iMCTtl (1)
|
||||
, m_bMCLoop (FALSE)
|
||||
, m_enCastMode (CM_MULTICAST)
|
||||
, m_castAddr (AF_UNSPEC, TRUE)
|
||||
, m_remoteAddr (AF_UNSPEC, TRUE)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CUdpCast()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
IUdpCastListener* m_pListener;
|
||||
TClientCloseContext m_ccContext;
|
||||
|
||||
SOCKET m_soClient;
|
||||
SHORT m_nEvents;
|
||||
CONNID m_dwConnID;
|
||||
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
DWORD m_dwMaxDatagramSize;
|
||||
DWORD m_dwFreeBufferPoolSize;
|
||||
DWORD m_dwFreeBufferPoolHold;
|
||||
|
||||
int m_iMCTtl;
|
||||
BOOL m_bMCLoop;
|
||||
EnCastMode m_enCastMode;
|
||||
|
||||
EnSocketError m_enLastError;
|
||||
volatile BOOL m_bConnected;
|
||||
volatile EnServiceState m_enState;
|
||||
|
||||
PVOID m_pExtra;
|
||||
PVOID m_pReserved;
|
||||
|
||||
HP_SOCKADDR m_castAddr;
|
||||
HP_SOCKADDR m_remoteAddr;
|
||||
|
||||
CBufferPtr m_rcBuffer;
|
||||
|
||||
protected:
|
||||
CStringA m_strHost;
|
||||
USHORT m_usPort;
|
||||
|
||||
CItemPool m_itPool;
|
||||
|
||||
private:
|
||||
CSpinGuard m_csState;
|
||||
|
||||
CCriSec m_csSend;
|
||||
TItemListExV m_lsSend;
|
||||
|
||||
CEvt m_evSend;
|
||||
CEvt m_evRecv;
|
||||
CEvt m_evStop;
|
||||
|
||||
volatile BOOL m_bPaused;
|
||||
|
||||
CThread<CUdpCast, VOID, UINT> m_thWorker;
|
||||
};
|
||||
|
||||
#endif
|
||||
830
UdpClient.cpp
Normal file
830
UdpClient.cpp
Normal file
@@ -0,0 +1,830 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "UdpClient.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
BOOL CUdpClient::Start(LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect, LPCTSTR lpszBindAddress, USHORT usLocalPort)
|
||||
{
|
||||
if(!CheckParams() || !CheckStarting())
|
||||
return FALSE;
|
||||
|
||||
PrepareStart();
|
||||
m_ccContext.Reset();
|
||||
|
||||
BOOL isOK = FALSE;
|
||||
HP_SOCKADDR addrRemote, addrBind;
|
||||
|
||||
if(CreateClientSocket(lpszRemoteAddress, addrRemote, usPort, lpszBindAddress, addrBind))
|
||||
{
|
||||
if(BindClientSocket(addrBind, addrRemote, usLocalPort))
|
||||
{
|
||||
if(TRIGGER(FirePrepareConnect(m_soClient)) != HR_ERROR)
|
||||
{
|
||||
if(ConnectToServer(addrRemote, bAsyncConnect))
|
||||
{
|
||||
if(CreateWorkerThread())
|
||||
isOK = TRUE;
|
||||
else
|
||||
SetLastError(SE_WORKER_THREAD_CREATE, __FUNCTION__, ERROR_CREATE_FAILED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
|
||||
}
|
||||
else
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
|
||||
if(!isOK)
|
||||
{
|
||||
m_ccContext.Reset(FALSE);
|
||||
EXECUTE_RESTORE_ERROR(Stop());
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CheckParams()
|
||||
{
|
||||
if (((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE) &&
|
||||
((int)m_dwFreeBufferPoolSize >= 0) &&
|
||||
((int)m_dwFreeBufferPoolHold >= 0) &&
|
||||
((int)m_dwDetectAttempts >= 0) &&
|
||||
((int)m_dwDetectInterval >= 1000 || m_dwDetectInterval == 0) )
|
||||
return TRUE;
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CUdpClient::PrepareStart()
|
||||
{
|
||||
m_itPool.SetItemCapacity(m_dwMaxDatagramSize);
|
||||
m_itPool.SetPoolSize(m_dwFreeBufferPoolSize);
|
||||
m_itPool.SetPoolHold(m_dwFreeBufferPoolHold);
|
||||
|
||||
m_itPool.Prepare();
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CheckStarting()
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(m_enState == SS_STOPPED)
|
||||
m_enState = SS_STARTING;
|
||||
else
|
||||
{
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CheckStoping()
|
||||
{
|
||||
if(m_enState != SS_STOPPED)
|
||||
{
|
||||
CSpinLock locallock(m_csState);
|
||||
|
||||
if(HasStarted())
|
||||
{
|
||||
m_enState = SS_STOPPING;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind)
|
||||
{
|
||||
HP_SCOPE_HOST host(lpszRemoteAddress);
|
||||
|
||||
if(!::GetSockAddrByHostName(host.addr, usPort, addrRemote))
|
||||
return FALSE;
|
||||
|
||||
if(::IsStrNotEmpty(lpszBindAddress))
|
||||
{
|
||||
if(!::sockaddr_A_2_IN(lpszBindAddress, 0, addrBind))
|
||||
return FALSE;
|
||||
|
||||
if(addrRemote.family != addrBind.family)
|
||||
{
|
||||
::WSASetLastError(ERROR_AFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
m_soClient = socket(addrRemote.family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if(m_soClient == INVALID_SOCKET)
|
||||
return FALSE;
|
||||
|
||||
VERIFY(::SSO_ReuseAddress(m_soClient, m_enReusePolicy) == NO_ERROR);
|
||||
|
||||
SetRemoteHost(host.name, usPort);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort)
|
||||
{
|
||||
if(addrBind.IsSpecified() && usLocalPort == 0)
|
||||
{
|
||||
if(::bind(m_soClient, addrBind.Addr(), addrBind.AddrSize()) == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
}
|
||||
else if(usLocalPort != 0)
|
||||
{
|
||||
HP_SOCKADDR realBindAddr = addrBind.IsSpecified() ? addrBind : HP_SOCKADDR::AnyAddr(addrRemote.family);
|
||||
|
||||
realBindAddr.SetPort(usLocalPort);
|
||||
|
||||
if(::bind(m_soClient, realBindAddr.Addr(), realBindAddr.AddrSize()) == SOCKET_ERROR)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_dwConnID = ::GenerateConnectionID();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
if(bAsyncConnect)
|
||||
{
|
||||
VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
|
||||
|
||||
int rc = ::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize());
|
||||
|
||||
if(IS_NO_ERROR(rc) || IS_IO_PENDING_ERROR())
|
||||
{
|
||||
m_nEvents = POLLOUT;
|
||||
isOK = TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(::connect(m_soClient, addrRemote.Addr(), addrRemote.AddrSize()) != SOCKET_ERROR)
|
||||
{
|
||||
VERIFY(::fcntl_SETFL(m_soClient, O_NOATIME | O_NONBLOCK | O_CLOEXEC));
|
||||
|
||||
SetConnected();
|
||||
|
||||
if(TRIGGER(FireConnect()) == HR_ERROR)
|
||||
::WSASetLastError(ENSURE_ERROR_CANCELLED);
|
||||
else
|
||||
{
|
||||
VERIFY(DetectConnection());
|
||||
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
isOK = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::Stop()
|
||||
{
|
||||
if(!CheckStoping())
|
||||
return FALSE;
|
||||
|
||||
WaitForWorkerThreadEnd();
|
||||
|
||||
CheckConnected();
|
||||
|
||||
if(m_ccContext.bFireOnClose)
|
||||
FireClose(m_ccContext.enOperation, m_ccContext.iErrorCode);
|
||||
|
||||
if(m_soClient != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(m_soClient, SHUT_WR);
|
||||
closesocket(m_soClient);
|
||||
|
||||
m_soClient = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CUdpClient::Reset()
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
m_evSend.Reset();
|
||||
m_evRecv.Reset();
|
||||
m_evStop.Reset();
|
||||
|
||||
m_lsSend.Clear();
|
||||
m_itPool.Clear();
|
||||
m_rcBuffer.Free();
|
||||
|
||||
m_strHost.Empty();
|
||||
|
||||
m_usPort = 0;
|
||||
m_nEvents = 0;
|
||||
m_dwDetectFails = 0;
|
||||
m_bPaused = FALSE;
|
||||
m_enState = SS_STOPPED;
|
||||
|
||||
m_evWait.SyncNotifyAll();
|
||||
}
|
||||
|
||||
void CUdpClient::WaitForWorkerThreadEnd()
|
||||
{
|
||||
if(!m_thWorker.IsRunning())
|
||||
return;
|
||||
|
||||
if(m_thWorker.IsInMyThread())
|
||||
m_thWorker.Detach();
|
||||
else
|
||||
{
|
||||
m_evStop.Set();
|
||||
m_thWorker.Join();
|
||||
}
|
||||
}
|
||||
|
||||
void CUdpClient::CheckConnected()
|
||||
{
|
||||
if(!IsConnected())
|
||||
return;
|
||||
|
||||
if(m_ccContext.bNotify)
|
||||
::SendUdpCloseNotify(m_soClient);
|
||||
|
||||
SetConnected(FALSE);
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CreateWorkerThread()
|
||||
{
|
||||
return m_thWorker.Start(this, &CUdpClient::WorkerThreadProc);
|
||||
}
|
||||
|
||||
UINT WINAPI CUdpClient::WorkerThreadProc(LPVOID pv)
|
||||
{
|
||||
::SetCurrentWorkerThreadName();
|
||||
|
||||
TRACE("---------------> Client Worker Thread 0x%08X started <---------------", SELF_THREAD_ID);
|
||||
|
||||
OnWorkerThreadStart(SELF_THREAD_ID);
|
||||
|
||||
BOOL bCallStop = TRUE;
|
||||
DWORD dwSize = 4;
|
||||
DWORD dwIndex = 0;
|
||||
BOOL bDetect = IsNeedDetect();
|
||||
FD fdUserEvt = GetUserEvent();
|
||||
|
||||
if(bDetect) ++dwSize;
|
||||
if(IS_VALID_FD(fdUserEvt)) ++dwSize;
|
||||
|
||||
pollfd* pfds = CreateLocalObjects(pollfd, dwSize);
|
||||
|
||||
pfds[dwIndex++] = {m_soClient, m_nEvents};
|
||||
pfds[dwIndex++] = {m_evSend.GetFD(), POLLIN};
|
||||
pfds[dwIndex++] = {m_evRecv.GetFD(), POLLIN};
|
||||
pfds[dwIndex++] = {m_evStop.GetFD(), POLLIN};
|
||||
|
||||
unique_ptr<CTimerEvent> evDetectPtr;
|
||||
|
||||
if(bDetect)
|
||||
{
|
||||
evDetectPtr.reset(new CTimerEvent());
|
||||
evDetectPtr->Set(m_dwDetectInterval);
|
||||
pfds[dwIndex++] = {evDetectPtr->GetFD(), POLLIN};
|
||||
}
|
||||
|
||||
if(IS_VALID_FD(fdUserEvt))
|
||||
pfds[dwIndex++] = {fdUserEvt, POLLIN};
|
||||
|
||||
m_rcBuffer.Malloc(m_dwMaxDatagramSize);
|
||||
|
||||
while(HasStarted())
|
||||
{
|
||||
int rs = (int)::PollForMultipleObjects(pfds, dwSize);
|
||||
ASSERT(rs > TIMEOUT);
|
||||
|
||||
if(rs <= 0)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_UNKNOWN, ::WSAGetLastError());
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
|
||||
for(DWORD i = 0; i < dwSize; i++)
|
||||
{
|
||||
if((1 << i) & rs)
|
||||
{
|
||||
SHORT revents = pfds[i].revents;
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
if(!ProcessNetworkEvent(revents))
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 1)
|
||||
{
|
||||
m_evSend.Reset();
|
||||
|
||||
if(!SendData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 2)
|
||||
{
|
||||
m_evRecv.Reset();
|
||||
|
||||
if(!ReadData())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 3)
|
||||
{
|
||||
m_evStop.Reset();
|
||||
|
||||
bCallStop = FALSE;
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else if(i == 4)
|
||||
{
|
||||
if(bDetect)
|
||||
{
|
||||
evDetectPtr->Reset();
|
||||
|
||||
if(!CheckConnection())
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!OnUserEvent())
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CLOSE, ENSURE_ERROR_CANCELLED);
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(i == 5)
|
||||
{
|
||||
if(!OnUserEvent())
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CLOSE, ENSURE_ERROR_CANCELLED);
|
||||
goto EXIT_WORKER_THREAD;
|
||||
}
|
||||
}
|
||||
else
|
||||
VERIFY(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
m_nEvents = (SHORT)((m_lsSend.IsEmpty() ? 0 : POLLOUT) | (m_bPaused ? 0 : POLLIN) | POLLRDHUP);
|
||||
pfds[0].events = m_nEvents;
|
||||
}
|
||||
|
||||
EXIT_WORKER_THREAD:
|
||||
|
||||
OnWorkerThreadEnd(SELF_THREAD_ID);
|
||||
|
||||
if(bCallStop && HasStarted())
|
||||
Stop();
|
||||
|
||||
TRACE("---------------> Client Worker Thread 0x%08X stoped <---------------", SELF_THREAD_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::CheckConnection()
|
||||
{
|
||||
if(m_dwDetectFails++ >= m_dwDetectAttempts)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CLOSE, NO_ERROR, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DetectConnection();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::DetectConnection()
|
||||
{
|
||||
int result = NO_ERROR;
|
||||
|
||||
if((int)send(m_soClient, nullptr, 0, 0) == SOCKET_ERROR)
|
||||
{
|
||||
result = ::WSAGetLastError();
|
||||
if(result == ERROR_WOULDBLOCK)
|
||||
result = NO_ERROR;
|
||||
}
|
||||
|
||||
BOOL isOK = (result == NO_ERROR);
|
||||
|
||||
if(isOK)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> send 0 bytes (detect package succ)", m_dwConnID);
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> send 0 bytes (detect package fail [%d])", m_dwConnID, result);
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::ProcessNetworkEvent(SHORT events)
|
||||
{
|
||||
BOOL bContinue = TRUE;
|
||||
|
||||
if(bContinue && events & POLLERR)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
if(bContinue && !IsConnected())
|
||||
bContinue = HandleConnect(events);
|
||||
|
||||
if(bContinue && events & POLLIN)
|
||||
bContinue = HandleRead(events);
|
||||
|
||||
if(bContinue && events & POLLOUT)
|
||||
bContinue = HandleWrite(events);
|
||||
|
||||
if(bContinue && events & _POLL_HUNGUP_EVENTS)
|
||||
bContinue = HandleClose(events);
|
||||
|
||||
return bContinue;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::HandleConnect(SHORT events)
|
||||
{
|
||||
ASSERT(events & POLLOUT);
|
||||
|
||||
int code = ::SSO_GetError(m_soClient);
|
||||
|
||||
if(!IS_NO_ERROR(code) || (events & _POLL_ERROR_EVENTS))
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CONNECT, code);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(events & _POLL_HUNGUP_EVENTS)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CONNECT, NO_ERROR);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SetConnected();
|
||||
|
||||
if(TRIGGER(FireConnect()) != HR_ERROR)
|
||||
VERIFY(DetectConnection());
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(FALSE, SO_CLOSE, ENSURE_ERROR_CANCELLED, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::HandleClose(SHORT events)
|
||||
{
|
||||
EnSocketOperation enOperation = SO_CLOSE;
|
||||
|
||||
if(events & _POLL_HUNGUP_EVENTS)
|
||||
enOperation = SO_CLOSE;
|
||||
else if(events & POLLIN)
|
||||
enOperation = SO_RECEIVE;
|
||||
else if(events & POLLOUT)
|
||||
enOperation = SO_SEND;
|
||||
|
||||
m_ccContext.Reset(TRUE, enOperation, ::SSO_GetError(m_soClient));
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::HandleRead(SHORT events)
|
||||
{
|
||||
return ReadData();
|
||||
}
|
||||
|
||||
BOOL CUdpClient::HandleWrite(SHORT events)
|
||||
{
|
||||
return SendData();
|
||||
}
|
||||
|
||||
BOOL CUdpClient::ReadData()
|
||||
{
|
||||
while(TRUE)
|
||||
{
|
||||
if(m_bPaused)
|
||||
break;
|
||||
|
||||
int rc = (int)recv(m_soClient, (char*)(BYTE*)m_rcBuffer, m_dwMaxDatagramSize, MSG_TRUNC);
|
||||
|
||||
if(rc > 0)
|
||||
{
|
||||
m_dwDetectFails = 0;
|
||||
|
||||
if(::IsUdpCloseNotify(m_rcBuffer, rc))
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_CLOSE, NO_ERROR, FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(rc > (int)m_dwMaxDatagramSize)
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, ERROR_BAD_LENGTH);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(TRIGGER(FireReceive(m_rcBuffer, rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnReceive() event return 'HR_ERROR', connection will be closed !", m_dwConnID);
|
||||
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, ENSURE_ERROR_CANCELLED);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
break;
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_RECEIVE, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(rc == 0)
|
||||
{
|
||||
m_dwDetectFails = 0;
|
||||
TRACE("<C-CNNID: %zu> recv 0 bytes (detect ack package)", m_dwConnID);
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::PauseReceive(BOOL bPause)
|
||||
{
|
||||
if(!IsConnected())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_bPaused == bPause)
|
||||
return TRUE;
|
||||
|
||||
m_bPaused = bPause;
|
||||
|
||||
if(!bPause)
|
||||
return m_evRecv.Set();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::SendData()
|
||||
{
|
||||
BOOL bBlocked = FALSE;
|
||||
|
||||
while(m_lsSend.Length() > 0)
|
||||
{
|
||||
TItemPtr itPtr(m_itPool);
|
||||
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
itPtr = m_lsSend.PopFront();
|
||||
}
|
||||
|
||||
if(!itPtr.IsValid())
|
||||
break;
|
||||
|
||||
ASSERT(!itPtr->IsEmpty());
|
||||
|
||||
if(!DoSendData(itPtr, bBlocked))
|
||||
return FALSE;
|
||||
|
||||
if(bBlocked)
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
m_lsSend.PushFront(itPtr.Detach());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::DoSendData(TItem* pItem, BOOL& bBlocked)
|
||||
{
|
||||
int rc = (int)send(m_soClient, (char*)pItem->Ptr(), pItem->Size(), 0);
|
||||
|
||||
if(rc > 0)
|
||||
{
|
||||
ASSERT(rc == pItem->Size());
|
||||
|
||||
if(TRIGGER(FireSend(pItem->Ptr(), rc)) == HR_ERROR)
|
||||
{
|
||||
TRACE("<C-CNNID: %zu> OnSend() event should not return 'HR_ERROR' !!", m_dwConnID);
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
bBlocked = TRUE;
|
||||
else
|
||||
{
|
||||
m_ccContext.Reset(TRUE, SO_SEND, code);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
ASSERT(FALSE);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::DoSend(const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(pBuffer && iLength > 0 && iLength <= (int)m_dwMaxDatagramSize)
|
||||
{
|
||||
if(IsConnected())
|
||||
{
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
|
||||
TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
|
||||
itPtr->Cat(pBuffer, iLength);
|
||||
|
||||
result = SendInternal(itPtr);
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL CUdpClient::SendPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(!pBuffers || iCount <= 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
if(!IsConnected())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
int result = NO_ERROR;
|
||||
int iLength = 0;
|
||||
int iMaxLen = (int)m_dwMaxDatagramSize;
|
||||
|
||||
TItemPtr itPtr(m_itPool, m_itPool.PickFreeItem());
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
iLength += iBufLen;
|
||||
|
||||
if(iLength <= iMaxLen)
|
||||
itPtr->Cat(pBuffer, iBufLen);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(iLength > 0 && iLength <= iMaxLen)
|
||||
result = SendInternal(itPtr);
|
||||
else
|
||||
result = ERROR_INCORRECT_SIZE;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CUdpClient::SendInternal(TItemPtr& itPtr)
|
||||
{
|
||||
int iPending;
|
||||
|
||||
{
|
||||
CCriSecLock locallock(m_csSend);
|
||||
|
||||
if(!IsConnected())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
iPending = m_lsSend.Length();
|
||||
|
||||
m_lsSend.PushBack(itPtr.Detach());
|
||||
ASSERT(m_lsSend.Length() > 0);
|
||||
}
|
||||
|
||||
if(iPending == 0 && m_lsSend.Length() > 0) m_evSend.Set();
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void CUdpClient::SetLastError(EnSocketError code, LPCSTR func, int ec)
|
||||
{
|
||||
TRACE("%s --> Error: %d, EC: %d", func, code, ec);
|
||||
|
||||
m_enLastError = code;
|
||||
::SetLastError(ec);
|
||||
}
|
||||
|
||||
BOOL CUdpClient::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ASSERT(lpszAddress != nullptr && iAddressLen > 0);
|
||||
|
||||
return ::GetSocketLocalAddress(m_soClient, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
void CUdpClient::SetRemoteHost(LPCTSTR lpszHost, USHORT usPort)
|
||||
{
|
||||
m_strHost = lpszHost;
|
||||
m_usPort = usPort;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::GetRemoteHost(TCHAR lpszHost[], int& iHostLen, USHORT& usPort)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
if(m_strHost.IsEmpty())
|
||||
return isOK;
|
||||
|
||||
int iLen = m_strHost.GetLength() + 1;
|
||||
|
||||
if(iHostLen >= iLen)
|
||||
{
|
||||
memcpy(lpszHost, CA2CT(m_strHost), iLen * sizeof(TCHAR));
|
||||
usPort = m_usPort;
|
||||
|
||||
isOK = TRUE;
|
||||
}
|
||||
|
||||
iHostLen = iLen;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CUdpClient::GetRemoteHost(LPCSTR* lpszHost, USHORT* pusPort)
|
||||
{
|
||||
*lpszHost = m_strHost;
|
||||
|
||||
if(pusPort != nullptr)
|
||||
*pusPort = m_usPort;
|
||||
|
||||
return !m_strHost.IsEmpty();
|
||||
}
|
||||
|
||||
#endif
|
||||
234
UdpClient.h
Normal file
234
UdpClient.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
class CUdpClient : public IUdpClient
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszRemoteAddress, USHORT usPort, BOOL bAsyncConnect = TRUE, LPCTSTR lpszBindAddress = nullptr, USHORT usLocalPort = 0);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (const BYTE* pBuffer, int iLength, int iOffset = 0) {return DoSend(pBuffer, iLength, iOffset);}
|
||||
virtual BOOL SendPackets (const WSABUF pBuffers[], int iCount);
|
||||
virtual BOOL PauseReceive (BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual CONNID GetConnectionID () {return m_dwConnID;}
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteHost (TCHAR lpszHost[], int& iHostLen, USHORT& usPort);
|
||||
virtual BOOL GetPendingDataLength (int& iPending) {iPending = m_lsSend.Length(); return HasStarted();}
|
||||
virtual BOOL IsPauseReceive (BOOL& bPaused) {bPaused = m_bPaused; return HasStarted();}
|
||||
virtual BOOL IsConnected () {return m_bConnected;}
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); m_enReusePolicy = enReusePolicy;}
|
||||
virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) {ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize = dwMaxDatagramSize;}
|
||||
virtual void SetDetectAttempts (DWORD dwDetectAttempts) {ENSURE_HAS_STOPPED(); m_dwDetectAttempts = dwDetectAttempts;}
|
||||
virtual void SetDetectInterval (DWORD dwDetectInterval) {ENSURE_HAS_STOPPED(); m_dwDetectInterval = dwDetectInterval;}
|
||||
virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize = dwFreeBufferPoolSize;}
|
||||
virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold = dwFreeBufferPoolHold;}
|
||||
virtual void SetExtra (PVOID pExtra) {m_pExtra = pExtra;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual DWORD GetMaxDatagramSize () {return m_dwMaxDatagramSize;}
|
||||
virtual DWORD GetDetectAttempts () {return m_dwDetectAttempts;}
|
||||
virtual DWORD GetDetectInterval () {return m_dwDetectInterval;}
|
||||
virtual DWORD GetFreeBufferPoolSize () {return m_dwFreeBufferPoolSize;}
|
||||
virtual DWORD GetFreeBufferPoolHold () {return m_dwFreeBufferPoolHold;}
|
||||
virtual PVOID GetExtra () {return m_pExtra;}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareConnect(SOCKET socket)
|
||||
{return DoFirePrepareConnect(this, socket);}
|
||||
virtual EnHandleResult FireConnect()
|
||||
{
|
||||
EnHandleResult rs = DoFireConnect(this);
|
||||
if(rs != HR_ERROR) rs = FireHandShake();
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake()
|
||||
{return DoFireHandShake(this);}
|
||||
virtual EnHandleResult FireSend(const BYTE* pData, int iLength)
|
||||
{return DoFireSend(this, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(const BYTE* pData, int iLength)
|
||||
{return DoFireReceive(this, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(int iLength)
|
||||
{return DoFireReceive(this, iLength);}
|
||||
virtual EnHandleResult FireClose(EnSocketOperation enOperation, int iErrorCode)
|
||||
{return DoFireClose(this, enOperation, iErrorCode);}
|
||||
|
||||
virtual EnHandleResult DoFirePrepareConnect(IUdpClient* pSender, SOCKET socket)
|
||||
{return m_pListener->OnPrepareConnect(pSender, pSender->GetConnectionID(), socket);}
|
||||
virtual EnHandleResult DoFireConnect(IUdpClient* pSender)
|
||||
{return m_pListener->OnConnect(pSender, pSender->GetConnectionID());}
|
||||
virtual EnHandleResult DoFireHandShake(IUdpClient* pSender)
|
||||
{return m_pListener->OnHandShake(pSender, pSender->GetConnectionID());}
|
||||
virtual EnHandleResult DoFireSend(IUdpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(IUdpClient* pSender, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(IUdpClient* pSender, int iLength)
|
||||
{return m_pListener->OnReceive(pSender, pSender->GetConnectionID(), iLength);}
|
||||
virtual EnHandleResult DoFireClose(IUdpClient* pSender, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(pSender, pSender->GetConnectionID(), enOperation, iErrorCode);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
virtual FD GetUserEvent() {return INVALID_FD;}
|
||||
virtual BOOL OnUserEvent() {return TRUE;}
|
||||
|
||||
static BOOL DoSend(CUdpClient* pClient, const BYTE* pBuffer, int iLength, int iOffset = 0)
|
||||
{return pClient->DoSend(pBuffer, iLength, iOffset);}
|
||||
|
||||
protected:
|
||||
void SetReserved (PVOID pReserved) {m_pReserved = pReserved;}
|
||||
PVOID GetReserved () {return m_pReserved;}
|
||||
BOOL GetRemoteHost (LPCSTR* lpszHost, USHORT* pusPort = nullptr);
|
||||
|
||||
private:
|
||||
BOOL DoSend (const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
|
||||
void SetRemoteHost (LPCTSTR lpszHost, USHORT usPort);
|
||||
void SetConnected (BOOL bConnected = TRUE) {m_bConnected = bConnected; if(bConnected) m_enState = SS_STARTED;}
|
||||
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL CreateClientSocket(LPCTSTR lpszRemoteAddress, HP_SOCKADDR& addrRemote, USHORT usPort, LPCTSTR lpszBindAddress, HP_SOCKADDR& addrBind);
|
||||
BOOL BindClientSocket(const HP_SOCKADDR& addrBind, const HP_SOCKADDR& addrRemote, USHORT usLocalPort);
|
||||
BOOL ConnectToServer(const HP_SOCKADDR& addrRemote, BOOL bAsyncConnect);
|
||||
BOOL CreateWorkerThread();
|
||||
BOOL ProcessNetworkEvent(SHORT events);
|
||||
BOOL ReadData();
|
||||
BOOL SendData();
|
||||
BOOL DoSendData(TItem* pItem, BOOL& bBlocked);
|
||||
int SendInternal(TItemPtr& itPtr);
|
||||
void WaitForWorkerThreadEnd();
|
||||
void CheckConnected();
|
||||
|
||||
BOOL HandleConnect (SHORT events);
|
||||
BOOL HandleClose (SHORT events);
|
||||
BOOL HandleRead (SHORT events);
|
||||
BOOL HandleWrite (SHORT events);
|
||||
|
||||
BOOL CheckConnection ();
|
||||
BOOL DetectConnection ();
|
||||
BOOL IsNeedDetect () {return m_dwDetectAttempts > 0 && m_dwDetectInterval > 0;}
|
||||
|
||||
UINT WINAPI WorkerThreadProc(LPVOID pv);
|
||||
|
||||
public:
|
||||
CUdpClient(IUdpClientListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_lsSend (m_itPool)
|
||||
, m_soClient (INVALID_SOCKET)
|
||||
, m_nEvents (0)
|
||||
, m_dwConnID (0)
|
||||
, m_usPort (0)
|
||||
, m_bPaused (FALSE)
|
||||
, m_bConnected (FALSE)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_dwDetectFails (0)
|
||||
, m_pExtra (nullptr)
|
||||
, m_pReserved (nullptr)
|
||||
, m_enReusePolicy (RAP_ADDR_ONLY)
|
||||
, m_dwMaxDatagramSize (DEFAULT_UDP_MAX_DATAGRAM_SIZE)
|
||||
, m_dwFreeBufferPoolSize(DEFAULT_CLIENT_FREE_BUFFER_POOL_SIZE)
|
||||
, m_dwFreeBufferPoolHold(DEFAULT_CLIENT_FREE_BUFFER_POOL_HOLD)
|
||||
, m_dwDetectAttempts (DEFAULT_UDP_DETECT_ATTEMPTS)
|
||||
, m_dwDetectInterval (DEFAULT_UDP_DETECT_INTERVAL)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CUdpClient()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
IUdpClientListener* m_pListener;
|
||||
TClientCloseContext m_ccContext;
|
||||
|
||||
SOCKET m_soClient;
|
||||
SHORT m_nEvents;
|
||||
CONNID m_dwConnID;
|
||||
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
DWORD m_dwMaxDatagramSize;
|
||||
DWORD m_dwFreeBufferPoolSize;
|
||||
DWORD m_dwFreeBufferPoolHold;
|
||||
DWORD m_dwDetectAttempts;
|
||||
DWORD m_dwDetectInterval;
|
||||
|
||||
EnSocketError m_enLastError;
|
||||
volatile BOOL m_bConnected;
|
||||
volatile EnServiceState m_enState;
|
||||
|
||||
PVOID m_pExtra;
|
||||
PVOID m_pReserved;
|
||||
|
||||
CBufferPtr m_rcBuffer;
|
||||
|
||||
protected:
|
||||
CStringA m_strHost;
|
||||
USHORT m_usPort;
|
||||
|
||||
CItemPool m_itPool;
|
||||
|
||||
private:
|
||||
CSpinGuard m_csState;
|
||||
|
||||
CCriSec m_csSend;
|
||||
TItemListExV m_lsSend;
|
||||
|
||||
CEvt m_evSend;
|
||||
CEvt m_evRecv;
|
||||
CEvt m_evStop;
|
||||
|
||||
DWORD m_dwDetectFails;
|
||||
volatile BOOL m_bPaused;
|
||||
|
||||
CThread<CUdpClient, VOID, UINT> m_thWorker;
|
||||
};
|
||||
|
||||
#endif
|
||||
757
UdpNode.cpp
Normal file
757
UdpNode.cpp
Normal file
@@ -0,0 +1,757 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 11
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Warray-bounds"
|
||||
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
||||
#endif
|
||||
|
||||
#include "UdpNode.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
BOOL CUdpNode::Start(LPCTSTR lpszBindAddress, USHORT usPort, EnCastMode enCastMode, LPCTSTR lpszCastAddress)
|
||||
{
|
||||
m_enCastMode = enCastMode;
|
||||
|
||||
if(!CheckParams() || !CheckStarting())
|
||||
return FALSE;
|
||||
|
||||
PrepareStart();
|
||||
|
||||
HP_SOCKADDR bindAddr(AF_UNSPEC, TRUE);
|
||||
|
||||
if(ParseBindAddr(lpszBindAddress, usPort, lpszCastAddress, bindAddr))
|
||||
if(CreateListenSocket(bindAddr))
|
||||
if(CreateWorkerThreads())
|
||||
if(StartAccept())
|
||||
{
|
||||
m_enState = SS_STARTED;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXECUTE_RESTORE_ERROR(Stop());
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::CheckParams()
|
||||
{
|
||||
if (((int)m_dwFreeBufferPoolSize >= 0) &&
|
||||
((int)m_dwFreeBufferPoolHold >= 0) &&
|
||||
((int)m_dwPostReceiveCount > 0) &&
|
||||
((int)m_dwWorkerThreadCount > 0 && m_dwWorkerThreadCount <= MAX_WORKER_THREAD_COUNT) &&
|
||||
(m_enCastMode >= CM_UNICAST && m_enCastMode <= CM_BROADCAST) &&
|
||||
(m_iMCTtl >= 0 && m_iMCTtl <= 255) &&
|
||||
(m_bMCLoop == TRUE || m_bMCLoop == FALSE) &&
|
||||
((int)m_dwMaxDatagramSize > 0 && m_dwMaxDatagramSize <= MAXIMUM_UDP_MAX_DATAGRAM_SIZE) )
|
||||
return TRUE;
|
||||
|
||||
SetLastError(SE_INVALID_PARAM, __FUNCTION__, ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::CheckStarting()
|
||||
{
|
||||
CReentrantWriteLock locallock(m_lcState);
|
||||
|
||||
if(m_enState == SS_STOPPED)
|
||||
m_enState = SS_STARTING;
|
||||
else
|
||||
{
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void CUdpNode::PrepareStart()
|
||||
{
|
||||
m_bfObjPool.SetItemCapacity(m_dwMaxDatagramSize);
|
||||
m_bfObjPool.SetPoolSize(m_dwFreeBufferPoolSize);
|
||||
m_bfObjPool.SetPoolHold(m_dwFreeBufferPoolHold);
|
||||
|
||||
m_bfObjPool.Prepare();
|
||||
|
||||
TNodeBufferObjList* pBufferObjList = (TNodeBufferObjList*)malloc(m_dwWorkerThreadCount * sizeof(TNodeBufferObjList));
|
||||
|
||||
for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
|
||||
new (pBufferObjList + i) TNodeBufferObjList(m_bfObjPool);
|
||||
|
||||
m_sndBuffs.reset(pBufferObjList);
|
||||
|
||||
m_csSends = make_unique<CCriSec[]>(m_dwWorkerThreadCount);
|
||||
|
||||
m_rcBuffers = make_unique<CBufferPtr[]>(m_dwWorkerThreadCount);
|
||||
for_each(m_rcBuffers.get(), m_rcBuffers.get() + m_dwWorkerThreadCount, [this](CBufferPtr& buff) {buff.Malloc(m_dwMaxDatagramSize);});
|
||||
|
||||
m_soListens = make_unique<SOCKET[]>(m_dwWorkerThreadCount);
|
||||
for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock) {sock = INVALID_FD;});
|
||||
}
|
||||
|
||||
BOOL CUdpNode::ParseBindAddr(LPCTSTR lpszBindAddress, USHORT usPort, LPCTSTR lpszCastAddress, HP_SOCKADDR& bindAddr)
|
||||
{
|
||||
if(::IsStrEmpty(lpszCastAddress))
|
||||
{
|
||||
if(m_enCastMode == CM_BROADCAST)
|
||||
lpszCastAddress = DEFAULT_IPV4_BROAD_CAST_ADDRESS;
|
||||
else if(m_enCastMode == CM_MULTICAST)
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_ADDRNOTAVAIL);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_enCastMode != CM_UNICAST && !::sockaddr_A_2_IN(lpszCastAddress, usPort, m_castAddr))
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(::IsStrEmpty(lpszBindAddress))
|
||||
{
|
||||
bindAddr.family = (m_enCastMode != CM_UNICAST) ? m_castAddr.family : AF_INET;
|
||||
bindAddr.SetPort(usPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!::sockaddr_A_2_IN(lpszBindAddress, usPort, bindAddr))
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(m_enCastMode == CM_BROADCAST && bindAddr.IsIPv6())
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_PFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(m_enCastMode != CM_UNICAST && m_castAddr.family != bindAddr.family)
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ERROR_AFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::CreateListenSocket(const HP_SOCKADDR& bindAddr)
|
||||
{
|
||||
for(DWORD i = 0; i < m_dwWorkerThreadCount; i++)
|
||||
{
|
||||
m_soListens[i] = socket(bindAddr.family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
SOCKET soListen = m_soListens[i];
|
||||
|
||||
if(IS_INVALID_FD(soListen))
|
||||
{
|
||||
SetLastError(SE_SOCKET_CREATE, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
::fcntl_SETFL(soListen, O_NOATIME | O_NONBLOCK | O_CLOEXEC);
|
||||
VERIFY(IS_NO_ERROR(::SSO_ReuseAddress(soListen, m_enReusePolicy)));
|
||||
|
||||
if(IS_HAS_ERROR(::bind(soListen, bindAddr.Addr(), bindAddr.AddrSize())))
|
||||
{
|
||||
SetLastError(SE_SOCKET_BIND, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
socklen_t dwAddrLen = (socklen_t)bindAddr.AddrSize();
|
||||
ENSURE(IS_NO_ERROR(::getsockname(soListen, m_localAddr.Addr(), &dwAddrLen)));
|
||||
}
|
||||
|
||||
if(m_enCastMode == CM_MULTICAST)
|
||||
{
|
||||
if(!::SetMultiCastSocketOptions(soListen, bindAddr, m_castAddr, m_iMCTtl, m_bMCLoop))
|
||||
{
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(m_enCastMode == CM_BROADCAST)
|
||||
{
|
||||
ASSERT(m_castAddr.IsIPv4());
|
||||
|
||||
BOOL bSet = TRUE;
|
||||
if(IS_HAS_ERROR(::SSO_SetSocketOption(soListen, SOL_SOCKET, SO_BROADCAST, &bSet, sizeof(BOOL))))
|
||||
{
|
||||
SetLastError(SE_CONNECT_SERVER, __FUNCTION__, ::WSAGetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if(TRIGGER(FirePrepareListen(soListen)) == HR_ERROR)
|
||||
{
|
||||
SetLastError(SE_SOCKET_PREPARE, __FUNCTION__, ENSURE_ERROR_CANCELLED);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::CreateWorkerThreads()
|
||||
{
|
||||
return m_ioDispatcher.Start(this, m_dwPostReceiveCount, m_dwWorkerThreadCount);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::StartAccept()
|
||||
{
|
||||
for(int i = 0; i < (int)m_dwWorkerThreadCount; i++)
|
||||
{
|
||||
SOCKET& soListen = m_soListens[i];
|
||||
|
||||
if(!m_ioDispatcher.AddFD(i, soListen, EPOLLIN | EPOLLOUT | EPOLLET, TO_PVOID(&soListen)))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::Stop()
|
||||
{
|
||||
if(!CheckStoping())
|
||||
return FALSE;
|
||||
|
||||
CloseListenSocket();
|
||||
|
||||
WaitForWorkerThreadEnd();
|
||||
|
||||
FireShutdown();
|
||||
|
||||
ReleaseFreeBuffer();
|
||||
|
||||
Reset();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::CheckStoping()
|
||||
{
|
||||
if(m_enState != SS_STOPPED)
|
||||
{
|
||||
CReentrantWriteLock locallock(m_lcState);
|
||||
|
||||
if(HasStarted())
|
||||
{
|
||||
m_enState = SS_STOPPING;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
SetLastError(SE_ILLEGAL_STATE, __FUNCTION__, ERROR_INVALID_STATE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void CUdpNode::CloseListenSocket()
|
||||
{
|
||||
if(m_soListens)
|
||||
{
|
||||
for_each(m_soListens.get(), m_soListens.get() + m_dwWorkerThreadCount, [](SOCKET& sock)
|
||||
{
|
||||
if(sock != INVALID_FD)
|
||||
{
|
||||
::ManualCloseSocket(sock);
|
||||
sock = INVALID_FD;
|
||||
}
|
||||
});
|
||||
|
||||
::WaitFor(100);
|
||||
}
|
||||
}
|
||||
|
||||
void CUdpNode::WaitForWorkerThreadEnd()
|
||||
{
|
||||
m_ioDispatcher.Stop();
|
||||
}
|
||||
|
||||
void CUdpNode::ReleaseFreeBuffer()
|
||||
{
|
||||
for_each(m_sndBuffs.get(), m_sndBuffs.get() + m_dwWorkerThreadCount, [](TNodeBufferObjList& sndBuff)
|
||||
{
|
||||
sndBuff.Clear();
|
||||
sndBuff.~TNodeBufferObjList();
|
||||
});
|
||||
|
||||
free(m_sndBuffs.release());
|
||||
|
||||
m_csSends = nullptr;
|
||||
|
||||
m_bfObjPool.Clear();
|
||||
}
|
||||
|
||||
void CUdpNode::Reset()
|
||||
{
|
||||
m_castAddr.Reset();
|
||||
m_localAddr.Reset();
|
||||
|
||||
m_soListens = nullptr;
|
||||
m_rcBuffers = nullptr;
|
||||
|
||||
m_iSending = 0;
|
||||
m_enState = SS_STOPPED;
|
||||
|
||||
m_evWait.SyncNotifyAll();
|
||||
}
|
||||
|
||||
int CUdpNode::GenerateBufferIndex(const HP_SOCKADDR& addrRemote)
|
||||
{
|
||||
return (int)(addrRemote.Hash() % m_dwWorkerThreadCount);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::Send(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
HP_SOCKADDR addrRemote;
|
||||
|
||||
if(!::GetSockAddrByHostName(lpszRemoteAddress, usRemotePort, addrRemote))
|
||||
return FALSE;
|
||||
|
||||
return DoSend(addrRemote, pBuffer, iLength, iOffset);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::SendPackets(LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
HP_SOCKADDR addrRemote;
|
||||
|
||||
if(!::GetSockAddrByHostName(lpszRemoteAddress, usRemotePort, addrRemote))
|
||||
return FALSE;
|
||||
|
||||
return DoSendPackets(addrRemote, pBuffers, iCount);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::SendCast(const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
if(m_enCastMode == CM_UNICAST)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return DoSend(m_castAddr, pBuffer, iLength, iOffset);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::SendCastPackets(const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
if(m_enCastMode == CM_UNICAST)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_OPERATION);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return DoSendPackets(m_castAddr, pBuffers, iCount);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::DoSend(const HP_SOCKADDR& addrRemote, const BYTE* pBuffer, int iLength, int iOffset)
|
||||
{
|
||||
ASSERT(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize);
|
||||
|
||||
int result = NO_ERROR;
|
||||
|
||||
if(IsValid())
|
||||
{
|
||||
if(addrRemote.family == m_localAddr.family)
|
||||
{
|
||||
if(pBuffer && iLength >= 0 && iLength <= (int)m_dwMaxDatagramSize)
|
||||
{
|
||||
if(iOffset != 0) pBuffer += iOffset;
|
||||
|
||||
TNodeBufferObjPtr bufPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
|
||||
bufPtr->Cat(pBuffer, iLength);
|
||||
|
||||
result = SendInternal(addrRemote, bufPtr);
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
else
|
||||
result = ERROR_AFNOSUPPORT;
|
||||
}
|
||||
else
|
||||
result = ERROR_INVALID_STATE;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::DoSendPackets(const HP_SOCKADDR& addrRemote, const WSABUF pBuffers[], int iCount)
|
||||
{
|
||||
ASSERT(pBuffers && iCount > 0);
|
||||
|
||||
if(!pBuffers || iCount <= 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if(!IsValid())
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(addrRemote.family != m_localAddr.family)
|
||||
{
|
||||
::SetLastError(ERROR_AFNOSUPPORT);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int result = NO_ERROR;
|
||||
int iLength = 0;
|
||||
int iMaxLen = (int)m_dwMaxDatagramSize;
|
||||
|
||||
TNodeBufferObjPtr bufPtr(m_bfObjPool, m_bfObjPool.PickFreeItem());
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
int iBufLen = pBuffers[i].len;
|
||||
|
||||
if(iBufLen > 0)
|
||||
{
|
||||
BYTE* pBuffer = (BYTE*)pBuffers[i].buf;
|
||||
ASSERT(pBuffer);
|
||||
|
||||
iLength += iBufLen;
|
||||
|
||||
if(iLength <= iMaxLen)
|
||||
bufPtr->Cat(pBuffer, iBufLen);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(iLength > 0 && iLength <= iMaxLen)
|
||||
result = SendInternal(addrRemote, bufPtr);
|
||||
else
|
||||
result = ERROR_INCORRECT_SIZE;
|
||||
|
||||
if(result != NO_ERROR)
|
||||
::SetLastError(result);
|
||||
|
||||
return (result == NO_ERROR);
|
||||
}
|
||||
|
||||
int CUdpNode::SendInternal(const HP_SOCKADDR& addrRemote, TNodeBufferObjPtr& bufPtr)
|
||||
{
|
||||
BOOL bPending;
|
||||
int iBufferSize = bufPtr->Size();
|
||||
int idx = GenerateBufferIndex(addrRemote);
|
||||
|
||||
addrRemote.Copy(bufPtr->remoteAddr);
|
||||
|
||||
{
|
||||
CReentrantReadLock locallock(m_lcState);
|
||||
|
||||
if(!IsValid())
|
||||
return ERROR_INVALID_STATE;
|
||||
|
||||
TNodeBufferObjList& sndBuff = m_sndBuffs[idx];
|
||||
|
||||
CCriSecLock locallock2(m_csSends[idx]);
|
||||
|
||||
bPending = IsPending(idx);
|
||||
sndBuff.PushBack(bufPtr.Detach());
|
||||
|
||||
if(iBufferSize == 0) sndBuff.IncreaseLength(1);
|
||||
|
||||
ASSERT(sndBuff.Length() > 0);
|
||||
}
|
||||
|
||||
if(!bPending && IsPending(idx))
|
||||
VERIFY(m_ioDispatcher.SendCommandByIndex(idx, DISP_CMD_SEND));
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
ASSERT(pv == &m_soListens[pContext->GetIndex()]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID CUdpNode::OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
VOID CUdpNode::OnCommand(const TDispContext* pContext, TDispCommand* pCmd)
|
||||
{
|
||||
int idx = pContext->GetIndex();
|
||||
int flag = (int)(pCmd->wParam);
|
||||
|
||||
switch(pCmd->type)
|
||||
{
|
||||
case DISP_CMD_SEND:
|
||||
HandleCmdSend(idx, flag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL CUdpNode::OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
return HandleReceive(pContext, RETRIVE_EVENT_FLAG_H(events));
|
||||
}
|
||||
|
||||
BOOL CUdpNode::OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
return HandleSend(pContext, RETRIVE_EVENT_FLAG_H(events), RETRIVE_EVENT_FLAG_R(events));
|
||||
}
|
||||
|
||||
BOOL CUdpNode::OnHungUp(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
return HandleClose(pContext->GetIndex(), nullptr, SO_CLOSE, 0);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::OnError(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
return HandleClose(pContext->GetIndex(), nullptr, SO_CLOSE, -1);
|
||||
}
|
||||
|
||||
VOID CUdpNode::OnDispatchThreadStart(THR_ID tid)
|
||||
{
|
||||
OnWorkerThreadStart(tid);
|
||||
}
|
||||
|
||||
VOID CUdpNode::OnDispatchThreadEnd(THR_ID tid)
|
||||
{
|
||||
OnWorkerThreadEnd(tid);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::HandleClose(int idx, TNodeBufferObj* pBufferObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
if(!HasStarted())
|
||||
return FALSE;
|
||||
|
||||
if(iErrorCode == -1)
|
||||
iErrorCode = ::SSO_GetError(m_soListens[idx]);
|
||||
|
||||
if(pBufferObj != nullptr)
|
||||
TRIGGER(FireError(&pBufferObj->remoteAddr, pBufferObj->Ptr(), pBufferObj->Size(), enOperation, iErrorCode));
|
||||
else
|
||||
TRIGGER(FireError(nullptr, nullptr, 0, enOperation, iErrorCode));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::HandleReceive(const TDispContext* pContext, int flag)
|
||||
{
|
||||
int idx = pContext->GetIndex();
|
||||
CBufferPtr& buffer = m_rcBuffers[idx];
|
||||
int iBufferLen = (int)buffer.Size();
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
HP_SOCKADDR addr;
|
||||
socklen_t dwAddrLen = (socklen_t)addr.AddrSize();
|
||||
|
||||
int rc = (int)recvfrom(m_soListens[idx], buffer.Ptr(), iBufferLen, MSG_TRUNC, addr.Addr(), &dwAddrLen);
|
||||
|
||||
if(rc >= 0)
|
||||
{
|
||||
if(rc > iBufferLen)
|
||||
{
|
||||
TRIGGER(FireError(&addr, buffer.Ptr(), iBufferLen, SO_RECEIVE, ERROR_BAD_LENGTH));
|
||||
continue;
|
||||
}
|
||||
|
||||
TRIGGER(FireReceive(&addr, buffer.Ptr(), rc));
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
break;
|
||||
else if(!HandleClose(idx, nullptr, SO_RECEIVE, code))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::HandleSend(const TDispContext* pContext, int flag, int rd)
|
||||
{
|
||||
HandleCmdSend(pContext->GetIndex(), flag);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
VOID CUdpNode::HandleCmdSend(int idx, int flag)
|
||||
{
|
||||
BOOL bBlocked = FALSE;
|
||||
TNodeBufferObjList& sndBuff = m_sndBuffs[idx];
|
||||
|
||||
TNodeBufferObjPtr bufPtr(m_bfObjPool);
|
||||
|
||||
while(IsPending(idx))
|
||||
{
|
||||
{
|
||||
CCriSecLock locallock(m_csSends[idx]);
|
||||
bufPtr = sndBuff.PopFront();
|
||||
}
|
||||
|
||||
if(!bufPtr.IsValid())
|
||||
break;
|
||||
|
||||
if(!SendItem(idx, sndBuff, bufPtr, bBlocked))
|
||||
return;
|
||||
|
||||
if(bBlocked)
|
||||
{
|
||||
{
|
||||
CCriSecLock locallock(m_csSends[idx]);
|
||||
sndBuff.PushFront(bufPtr.Detach());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!bBlocked && IsPending(idx))
|
||||
VERIFY(m_ioDispatcher.SendCommandByIndex(idx, DISP_CMD_SEND));
|
||||
}
|
||||
|
||||
BOOL CUdpNode::SendItem(int idx, TNodeBufferObjList& sndBuff, TNodeBufferObj* pBufferObj, BOOL& bBlocked)
|
||||
{
|
||||
int rc = (int)sendto(m_soListens[idx], pBufferObj->Ptr(), pBufferObj->Size(), 0, pBufferObj->remoteAddr.Addr(), pBufferObj->remoteAddr.AddrSize());
|
||||
|
||||
if(rc >= 0)
|
||||
{
|
||||
ASSERT(rc == pBufferObj->Size());
|
||||
|
||||
if(rc == 0)
|
||||
{
|
||||
CCriSecLock locallock(m_csSends[idx]);
|
||||
sndBuff.ReduceLength(1);
|
||||
}
|
||||
|
||||
TRIGGER(FireSend(pBufferObj));
|
||||
}
|
||||
else if(rc == SOCKET_ERROR)
|
||||
{
|
||||
int code = ::WSAGetLastError();
|
||||
|
||||
if(code == ERROR_WOULDBLOCK)
|
||||
bBlocked = TRUE;
|
||||
else if(!HandleClose(idx, pBufferObj, SO_SEND, code))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CUdpNode::GetLocalAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ADDRESS_FAMILY usFamily;
|
||||
return ::sockaddr_IN_2_A(m_localAddr, usFamily, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::GetCastAddress(TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort)
|
||||
{
|
||||
ADDRESS_FAMILY usFamily;
|
||||
return ::sockaddr_IN_2_A(m_castAddr, usFamily, lpszAddress, iAddressLen, usPort);
|
||||
}
|
||||
|
||||
void CUdpNode::SetLastError(EnSocketError code, LPCSTR func, int ec)
|
||||
{
|
||||
m_enLastError = code;
|
||||
::SetLastError(ec);
|
||||
}
|
||||
|
||||
BOOL CUdpNode::GetPendingDataLength(int& iPending)
|
||||
{
|
||||
iPending = 0;
|
||||
|
||||
{
|
||||
CReentrantReadLock locallock(m_lcState);
|
||||
|
||||
if(!IsValid())
|
||||
return FALSE;
|
||||
|
||||
for_each(m_sndBuffs.get(), m_sndBuffs.get() + m_dwWorkerThreadCount, [&iPending](TNodeBufferObjList& sndBuff) { iPending += sndBuff.Length(); });
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EnHandleResult CUdpNode::FireSend(TNodeBufferObj* pBufferObj)
|
||||
{
|
||||
TCHAR szAddress[60];
|
||||
int iAddressLen = ARRAY_SIZE(szAddress);
|
||||
ADDRESS_FAMILY usFamily;
|
||||
USHORT usPort;
|
||||
|
||||
::sockaddr_IN_2_A(pBufferObj->remoteAddr, usFamily, szAddress, iAddressLen, usPort);
|
||||
|
||||
return m_pListener->OnSend(this, szAddress, usPort, pBufferObj->Ptr(), pBufferObj->Size());
|
||||
}
|
||||
|
||||
EnHandleResult CUdpNode::FireReceive(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength)
|
||||
{
|
||||
TCHAR szAddress[60];
|
||||
int iAddressLen = ARRAY_SIZE(szAddress);
|
||||
ADDRESS_FAMILY usFamily;
|
||||
USHORT usPort;
|
||||
|
||||
::sockaddr_IN_2_A(*pRemoteAddr, usFamily, szAddress, iAddressLen, usPort);
|
||||
return m_pListener->OnReceive(this, szAddress, usPort, pData, iLength);
|
||||
}
|
||||
|
||||
EnHandleResult CUdpNode::FireError(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength, EnSocketOperation enOperation, int iErrorCode)
|
||||
{
|
||||
TCHAR szAddress[60];
|
||||
int iAddressLen = ARRAY_SIZE(szAddress);
|
||||
ADDRESS_FAMILY usFamily;
|
||||
USHORT usPort;
|
||||
|
||||
if(pRemoteAddr == nullptr)
|
||||
{
|
||||
::sockaddr_IN_2_A(m_localAddr, usFamily, szAddress, iAddressLen, usPort);
|
||||
return m_pListener->OnError(this, enOperation, iErrorCode, szAddress, usPort, nullptr, 0);
|
||||
}
|
||||
|
||||
::sockaddr_IN_2_A(*pRemoteAddr, usFamily, szAddress, iAddressLen, usPort);
|
||||
return m_pListener->OnError(this, enOperation, iErrorCode, szAddress, usPort, pData, iLength);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 11
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
198
UdpNode.h
Normal file
198
UdpNode.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
#include "./common/IODispatcher.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
class CUdpNode : public IUdpNode, private CIOHandler
|
||||
{
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszBindAddress = nullptr, USHORT usPort = 0, EnCastMode enCastMode = CM_UNICAST, LPCTSTR lpszCastAddress = nullptr);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendPackets (LPCTSTR lpszRemoteAddress, USHORT usRemotePort, const WSABUF pBuffers[], int iCount);
|
||||
virtual BOOL SendCast (const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendCastPackets(const WSABUF pBuffers[], int iCount);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual BOOL GetLocalAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetCastAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
virtual BOOL GetPendingDataLength (int& iPending);
|
||||
|
||||
private:
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override;
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override;
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) override;
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) override;
|
||||
|
||||
public:
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy){ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
|
||||
virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) {ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount = dwWorkerThreadCount;}
|
||||
virtual void SetFreeBufferPoolSize (DWORD dwFreeBufferPoolSize) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolSize = dwFreeBufferPoolSize;}
|
||||
virtual void SetFreeBufferPoolHold (DWORD dwFreeBufferPoolHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferPoolHold = dwFreeBufferPoolHold;}
|
||||
virtual void SetPostReceiveCount (DWORD dwPostReceiveCount) {ENSURE_HAS_STOPPED(); m_dwPostReceiveCount = dwPostReceiveCount;}
|
||||
virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) {ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize = dwMaxDatagramSize;}
|
||||
virtual void SetMultiCastTtl (int iMCTtl) {ENSURE_HAS_STOPPED(); m_iMCTtl = iMCTtl;}
|
||||
virtual void SetMultiCastLoop (BOOL bMCLoop) {ENSURE_HAS_STOPPED(); m_bMCLoop = bMCLoop;}
|
||||
virtual void SetExtra (PVOID pExtra) {m_pExtra = pExtra;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual DWORD GetWorkerThreadCount () {return m_dwWorkerThreadCount;}
|
||||
virtual DWORD GetFreeBufferPoolSize () {return m_dwFreeBufferPoolSize;}
|
||||
virtual DWORD GetFreeBufferPoolHold () {return m_dwFreeBufferPoolHold;}
|
||||
virtual DWORD GetPostReceiveCount () {return m_dwPostReceiveCount;}
|
||||
virtual DWORD GetMaxDatagramSize () {return m_dwMaxDatagramSize;}
|
||||
virtual EnCastMode GetCastMode () {return m_enCastMode;}
|
||||
virtual int GetMultiCastTtl () {return m_iMCTtl;}
|
||||
virtual BOOL IsMultiCastLoop () {return m_bMCLoop;}
|
||||
virtual PVOID GetExtra () {return m_pExtra;}
|
||||
|
||||
protected:
|
||||
EnHandleResult FirePrepareListen(SOCKET soListen)
|
||||
{return m_pListener->OnPrepareListen(this, soListen);}
|
||||
EnHandleResult FireShutdown()
|
||||
{return m_pListener->OnShutdown(this);}
|
||||
|
||||
EnHandleResult FireSend(TNodeBufferObj* pBufferObj);
|
||||
EnHandleResult FireReceive(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength);
|
||||
EnHandleResult FireError(const HP_SOCKADDR* pRemoteAddr, const BYTE* pData, int iLength, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID dwThreadID) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID dwThreadID) {}
|
||||
|
||||
BOOL DoSend(const HP_SOCKADDR& addrRemote, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
BOOL DoSendPackets(const HP_SOCKADDR& addrRemote, const WSABUF pBuffers[], int iCount);
|
||||
int SendInternal(const HP_SOCKADDR& addrRemote, TNodeBufferObjPtr& bufPtr);
|
||||
|
||||
private:
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL ParseBindAddr(LPCTSTR lpszBindAddress, USHORT usPort, LPCTSTR lpszCastAddress, HP_SOCKADDR& bindAddr);
|
||||
BOOL CreateListenSocket(const HP_SOCKADDR& bindAddr);
|
||||
BOOL CreateWorkerThreads();
|
||||
BOOL StartAccept();
|
||||
|
||||
void CloseListenSocket();
|
||||
void WaitForWorkerThreadEnd();
|
||||
void ReleaseFreeBuffer();
|
||||
|
||||
int GenerateBufferIndex(const HP_SOCKADDR& addrRemote);
|
||||
|
||||
private:
|
||||
BOOL HandleReceive(const TDispContext* pContext, int flag = 0);
|
||||
BOOL HandleSend(const TDispContext* pContext, int flag = 0, int rd = 0);
|
||||
BOOL HandleClose(int idx, TNodeBufferObj* pBufferObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
|
||||
VOID HandleCmdSend(int idx, int flag);
|
||||
|
||||
BOOL SendItem(int idx, TNodeBufferObjList& sndBuff, TNodeBufferObj* pBufferObj, BOOL& bBlocked);
|
||||
|
||||
private:
|
||||
BOOL IsValid () {return m_enState == SS_STARTED;}
|
||||
BOOL IsPending (int idx) {return m_sndBuffs[idx].Length() > 0;}
|
||||
|
||||
public:
|
||||
CUdpNode(IUdpNodeListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_iSending (0)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_enReusePolicy (RAP_ADDR_AND_PORT)
|
||||
, m_dwWorkerThreadCount (DEFAULT_WORKER_THREAD_COUNT)
|
||||
, m_dwFreeBufferPoolSize (DEFAULT_FREE_BUFFEROBJ_POOL)
|
||||
, m_dwFreeBufferPoolHold (DEFAULT_FREE_BUFFEROBJ_HOLD)
|
||||
, m_dwPostReceiveCount (DEFAULT_UDP_POST_RECEIVE_COUNT)
|
||||
, m_dwMaxDatagramSize (DEFAULT_UDP_MAX_DATAGRAM_SIZE)
|
||||
, m_pExtra (nullptr)
|
||||
, m_iMCTtl (1)
|
||||
, m_bMCLoop (FALSE)
|
||||
, m_enCastMode (CM_UNICAST)
|
||||
, m_castAddr (AF_UNSPEC, TRUE)
|
||||
, m_localAddr (AF_UNSPEC, TRUE)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CUdpNode()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
DWORD m_dwWorkerThreadCount;
|
||||
DWORD m_dwFreeBufferPoolSize;
|
||||
DWORD m_dwFreeBufferPoolHold;
|
||||
DWORD m_dwPostReceiveCount;
|
||||
DWORD m_dwMaxDatagramSize;
|
||||
PVOID m_pExtra;
|
||||
|
||||
int m_iMCTtl;
|
||||
BOOL m_bMCLoop;
|
||||
EnCastMode m_enCastMode;
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
HP_SOCKADDR m_castAddr;
|
||||
HP_SOCKADDR m_localAddr;
|
||||
|
||||
CNodeBufferObjPool m_bfObjPool;
|
||||
CNodeCriSecs m_csSends;
|
||||
TNodeBufferObjLists m_sndBuffs;
|
||||
|
||||
IUdpNodeListener* m_pListener;
|
||||
ListenSocketsPtr m_soListens;
|
||||
EnServiceState m_enState;
|
||||
EnSocketError m_enLastError;
|
||||
|
||||
CReceiveBuffersPtr m_rcBuffers;
|
||||
|
||||
CRWLock m_lcState;
|
||||
|
||||
volatile long m_iSending;
|
||||
|
||||
CIODispatcher m_ioDispatcher;
|
||||
};
|
||||
|
||||
#endif
|
||||
1231
UdpServer.cpp
Normal file
1231
UdpServer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
301
UdpServer.h
Normal file
301
UdpServer.h
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "SocketHelper.h"
|
||||
#include "./common/GeneralHelper.h"
|
||||
#include "./common/IODispatcher.h"
|
||||
|
||||
#ifdef _UDP_SUPPORT
|
||||
|
||||
class CUdpServer : public IUdpServer, private CIOHandler
|
||||
{
|
||||
using CWorkerThread = CThread<CUdpServer, VOID, UINT>;
|
||||
using CSendQueue = CCASSimpleQueue<CONNID>;
|
||||
using CSendQueuesPtr = unique_ptr<CSendQueue[]>;
|
||||
|
||||
public:
|
||||
virtual BOOL Start (LPCTSTR lpszBindAddress, USHORT usPort);
|
||||
virtual BOOL Stop ();
|
||||
virtual BOOL Send (CONNID dwConnID, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
virtual BOOL SendPackets (CONNID dwConnID, const WSABUF pBuffers[], int iCount);
|
||||
virtual BOOL PauseReceive (CONNID dwConnID, BOOL bPause = TRUE);
|
||||
virtual BOOL Wait (DWORD dwMilliseconds = INFINITE) {return m_evWait.WaitFor(dwMilliseconds, WAIT_FOR_STOP_PREDICATE);}
|
||||
virtual BOOL HasStarted () {return m_enState == SS_STARTED || m_enState == SS_STARTING;}
|
||||
virtual EnServiceState GetState () {return m_enState;}
|
||||
virtual BOOL Disconnect (CONNID dwConnID, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectLongConnections (DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL DisconnectSilenceConnections(DWORD dwPeriod, BOOL bForce = TRUE);
|
||||
virtual BOOL GetListenAddress (TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetLocalAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
virtual BOOL GetRemoteAddress (CONNID dwConnID, TCHAR lpszAddress[], int& iAddressLen, USHORT& usPort);
|
||||
|
||||
virtual BOOL IsConnected (CONNID dwConnID);
|
||||
virtual BOOL IsPauseReceive (CONNID dwConnID, BOOL& bPaused);
|
||||
virtual BOOL GetPendingDataLength (CONNID dwConnID, int& iPending);
|
||||
virtual DWORD GetConnectionCount ();
|
||||
virtual BOOL GetAllConnectionIDs (CONNID pIDs[], DWORD& dwCount);
|
||||
virtual BOOL GetConnectPeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual BOOL GetSilencePeriod (CONNID dwConnID, DWORD& dwPeriod);
|
||||
virtual EnSocketError GetLastError () {return m_enLastError;}
|
||||
virtual LPCTSTR GetLastErrorDesc () {return ::GetSocketErrorDesc(m_enLastError);}
|
||||
|
||||
private:
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override;
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override;
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override;
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) override;
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) override;
|
||||
|
||||
public:
|
||||
virtual BOOL IsSecure () {return FALSE;}
|
||||
|
||||
virtual BOOL SetConnectionExtra(CONNID dwConnID, PVOID pExtra);
|
||||
virtual BOOL GetConnectionExtra(CONNID dwConnID, PVOID* ppExtra);
|
||||
|
||||
virtual void SetReuseAddressPolicy (EnReuseAddressPolicy enReusePolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enReusePolicy == enReusePolicy);}
|
||||
virtual void SetSendPolicy (EnSendPolicy enSendPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enSendPolicy == enSendPolicy);}
|
||||
virtual void SetOnSendSyncPolicy (EnOnSendSyncPolicy enOnSendSyncPolicy) {ENSURE_HAS_STOPPED(); ASSERT(m_enOnSendSyncPolicy == enOnSendSyncPolicy);}
|
||||
virtual void SetMaxConnectionCount (DWORD dwMaxConnectionCount) {ENSURE_HAS_STOPPED(); m_dwMaxConnectionCount = dwMaxConnectionCount;}
|
||||
virtual void SetWorkerThreadCount (DWORD dwWorkerThreadCount) {ENSURE_HAS_STOPPED(); m_dwWorkerThreadCount = dwWorkerThreadCount;}
|
||||
virtual void SetFreeSocketObjLockTime (DWORD dwFreeSocketObjLockTime) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjLockTime = dwFreeSocketObjLockTime;}
|
||||
virtual void SetFreeSocketObjPool (DWORD dwFreeSocketObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjPool = dwFreeSocketObjPool;}
|
||||
virtual void SetFreeBufferObjPool (DWORD dwFreeBufferObjPool) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjPool = dwFreeBufferObjPool;}
|
||||
virtual void SetFreeSocketObjHold (DWORD dwFreeSocketObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeSocketObjHold = dwFreeSocketObjHold;}
|
||||
virtual void SetFreeBufferObjHold (DWORD dwFreeBufferObjHold) {ENSURE_HAS_STOPPED(); m_dwFreeBufferObjHold = dwFreeBufferObjHold;}
|
||||
virtual void SetMaxDatagramSize (DWORD dwMaxDatagramSize) {ENSURE_HAS_STOPPED(); m_dwMaxDatagramSize = dwMaxDatagramSize;}
|
||||
virtual void SetPostReceiveCount (DWORD dwPostReceiveCount) {ENSURE_HAS_STOPPED(); m_dwPostReceiveCount = dwPostReceiveCount;}
|
||||
virtual void SetDetectAttempts (DWORD dwDetectAttempts) {ENSURE_HAS_STOPPED(); m_dwDetectAttempts = dwDetectAttempts;}
|
||||
virtual void SetDetectInterval (DWORD dwDetectInterval) {ENSURE_HAS_STOPPED(); m_dwDetectInterval = dwDetectInterval;}
|
||||
virtual void SetMarkSilence (BOOL bMarkSilence) {ENSURE_HAS_STOPPED(); m_bMarkSilence = bMarkSilence;}
|
||||
|
||||
virtual EnReuseAddressPolicy GetReuseAddressPolicy () {return m_enReusePolicy;}
|
||||
virtual EnSendPolicy GetSendPolicy () {return m_enSendPolicy;}
|
||||
virtual EnOnSendSyncPolicy GetOnSendSyncPolicy () {return m_enOnSendSyncPolicy;}
|
||||
virtual DWORD GetMaxConnectionCount () {return m_dwMaxConnectionCount;}
|
||||
virtual DWORD GetWorkerThreadCount () {return m_dwWorkerThreadCount;}
|
||||
virtual DWORD GetFreeSocketObjLockTime () {return m_dwFreeSocketObjLockTime;}
|
||||
virtual DWORD GetFreeSocketObjPool () {return m_dwFreeSocketObjPool;}
|
||||
virtual DWORD GetFreeBufferObjPool () {return m_dwFreeBufferObjPool;}
|
||||
virtual DWORD GetFreeSocketObjHold () {return m_dwFreeSocketObjHold;}
|
||||
virtual DWORD GetFreeBufferObjHold () {return m_dwFreeBufferObjHold;}
|
||||
virtual DWORD GetMaxDatagramSize () {return m_dwMaxDatagramSize;}
|
||||
virtual DWORD GetPostReceiveCount () {return m_dwPostReceiveCount;}
|
||||
virtual DWORD GetDetectAttempts () {return m_dwDetectAttempts;}
|
||||
virtual DWORD GetDetectInterval () {return m_dwDetectInterval;}
|
||||
virtual BOOL IsMarkSilence () {return m_bMarkSilence;}
|
||||
|
||||
protected:
|
||||
virtual EnHandleResult FirePrepareListen(SOCKET soListen)
|
||||
{return DoFirePrepareListen(soListen);}
|
||||
virtual EnHandleResult FireAccept(TUdpSocketObj* pSocketObj)
|
||||
{
|
||||
EnHandleResult rs = DoFireAccept(pSocketObj);
|
||||
if(rs != HR_ERROR) rs = FireHandShake(pSocketObj);
|
||||
return rs;
|
||||
}
|
||||
virtual EnHandleResult FireHandShake(TUdpSocketObj* pSocketObj)
|
||||
{return DoFireHandShake(pSocketObj);}
|
||||
virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireReceive(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireReceive(TUdpSocketObj* pSocketObj, int iLength)
|
||||
{return DoFireReceive(pSocketObj, iLength);}
|
||||
virtual EnHandleResult FireSend(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return DoFireSend(pSocketObj, pData, iLength);}
|
||||
virtual EnHandleResult FireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return DoFireClose(pSocketObj, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult FireShutdown()
|
||||
{return DoFireShutdown();}
|
||||
|
||||
virtual EnHandleResult DoFirePrepareListen(SOCKET soListen)
|
||||
{return m_pListener->OnPrepareListen(this, soListen);}
|
||||
virtual EnHandleResult DoFireAccept(TUdpSocketObj* pSocketObj)
|
||||
{return m_pListener->OnAccept(this, pSocketObj->connID, (UINT_PTR)(&pSocketObj->remoteAddr));}
|
||||
virtual EnHandleResult DoFireHandShake(TUdpSocketObj* pSocketObj)
|
||||
{return m_pListener->OnHandShake(this, pSocketObj->connID);}
|
||||
virtual EnHandleResult DoFireReceive(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireReceive(TUdpSocketObj* pSocketObj, int iLength)
|
||||
{return m_pListener->OnReceive(this, pSocketObj->connID, iLength);}
|
||||
virtual EnHandleResult DoFireSend(TUdpSocketObj* pSocketObj, const BYTE* pData, int iLength)
|
||||
{return m_pListener->OnSend(this, pSocketObj->connID, pData, iLength);}
|
||||
virtual EnHandleResult DoFireClose(TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode)
|
||||
{return m_pListener->OnClose(this, pSocketObj->connID, enOperation, iErrorCode);}
|
||||
virtual EnHandleResult DoFireShutdown()
|
||||
{return m_pListener->OnShutdown(this);}
|
||||
|
||||
void SetLastError(EnSocketError code, LPCSTR func, int ec);
|
||||
virtual BOOL CheckParams();
|
||||
virtual void PrepareStart();
|
||||
virtual void Reset();
|
||||
|
||||
virtual void OnWorkerThreadStart(THR_ID tid) {}
|
||||
virtual void OnWorkerThreadEnd(THR_ID tid) {}
|
||||
|
||||
virtual void ReleaseGCSocketObj(BOOL bForce = FALSE);
|
||||
|
||||
TUdpSocketObj* FindSocketObj(CONNID dwConnID);
|
||||
int SendInternal(TUdpSocketObj* pSocketObj, TItemPtr& itPtr);
|
||||
|
||||
BOOL DoSend(TUdpSocketObj* pSocketObj, const BYTE* pBuffer, int iLength, int iOffset = 0);
|
||||
|
||||
protected:
|
||||
BOOL SetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID pExtra);
|
||||
BOOL GetConnectionExtra(TUdpSocketObj* pSocketObj, PVOID* ppExtra);
|
||||
BOOL SetConnectionReserved(CONNID dwConnID, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(CONNID dwConnID, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID pReserved);
|
||||
BOOL GetConnectionReserved(TUdpSocketObj* pSocketObj, PVOID* ppReserved);
|
||||
BOOL SetConnectionReserved2(CONNID dwConnID, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(CONNID dwConnID, PVOID* ppReserved2);
|
||||
BOOL SetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID pReserved2);
|
||||
BOOL GetConnectionReserved2(TUdpSocketObj* pSocketObj, PVOID* ppReserved2);
|
||||
|
||||
private:
|
||||
BOOL CheckStarting();
|
||||
BOOL CheckStoping();
|
||||
BOOL CreateListenSocket(LPCTSTR lpszBindAddress, USHORT usPort);
|
||||
BOOL CreateWorkerThreads();
|
||||
BOOL StartAccept();
|
||||
|
||||
void SendCloseNotify();
|
||||
void CloseListenSocket();
|
||||
void DisconnectClientSocket();
|
||||
void WaitForClientSocketClose();
|
||||
void ReleaseClientSocket();
|
||||
void ReleaseFreeSocket();
|
||||
void ClearSendQueues();
|
||||
void WaitForWorkerThreadEnd();
|
||||
|
||||
TUdpSocketObj* GetFreeSocketObj(CONNID dwConnID);
|
||||
TUdpSocketObj* CreateSocketObj();
|
||||
CONNID FindConnectionID(const HP_SOCKADDR* pAddr);
|
||||
void AddFreeSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, BOOL bNotify = TRUE);
|
||||
void DeleteSocketObj(TUdpSocketObj* pSocketObj);
|
||||
BOOL InvalidSocketObj(TUdpSocketObj* pSocketObj);
|
||||
void AddClientSocketObj(int idx, CONNID dwConnID, TUdpSocketObj* pSocketObj, const HP_SOCKADDR& remoteAddr);
|
||||
void CloseClientSocketObj(TUdpSocketObj* pSocketObj, EnSocketCloseFlag enFlag = SCF_NONE, EnSocketOperation enOperation = SO_UNKNOWN, int iErrorCode = 0, BOOL bNotify = TRUE);
|
||||
|
||||
EnHandleResult TriggerFireAccept(TUdpSocketObj* pSocketObj);
|
||||
|
||||
private:
|
||||
VOID HandleCmdSend (CONNID dwConnID, int flag);
|
||||
VOID HandleCmdDisconnect(CONNID dwConnID, BOOL bForce);
|
||||
VOID HandleCmdTimeout (CONNID dwConnID);
|
||||
|
||||
CONNID HandleAccept (const TDispContext* pContext, HP_SOCKADDR& addr);
|
||||
BOOL HandleReceive (const TDispContext* pContext, int flag = 0);
|
||||
BOOL HandleSend (const TDispContext* pContext, int flag = 0);
|
||||
BOOL HandleClose (TUdpSocketObj* pSocketObj, EnSocketOperation enOperation, int iErrorCode);
|
||||
void HandleZeroBytes (TUdpSocketObj* pSocketObj);
|
||||
|
||||
BOOL SendItem (TUdpSocketObj* pSocketObj, TItem* pItem, BOOL& bBlocked);
|
||||
|
||||
void DetectConnection (PVOID pv);
|
||||
BOOL IsNeedDetectConnection () const {return m_dwDetectAttempts > 0 && m_dwDetectInterval > 0;}
|
||||
|
||||
public:
|
||||
CUdpServer(IUdpServerListener* pListener)
|
||||
: m_pListener (pListener)
|
||||
, m_enLastError (SE_OK)
|
||||
, m_enState (SS_STOPPED)
|
||||
, m_fdGCTimer (INVALID_FD)
|
||||
, m_enSendPolicy (SP_PACK)
|
||||
, m_enOnSendSyncPolicy (OSSP_RECEIVE)
|
||||
, m_enReusePolicy (RAP_ADDR_AND_PORT)
|
||||
, m_dwMaxConnectionCount (DEFAULT_CONNECTION_COUNT)
|
||||
, m_dwWorkerThreadCount (DEFAULT_WORKER_THREAD_COUNT)
|
||||
, m_dwFreeSocketObjLockTime (DEFAULT_FREE_SOCKETOBJ_LOCK_TIME)
|
||||
, m_dwFreeSocketObjPool (DEFAULT_FREE_SOCKETOBJ_POOL)
|
||||
, m_dwFreeBufferObjPool (DEFAULT_FREE_BUFFEROBJ_POOL)
|
||||
, m_dwFreeSocketObjHold (DEFAULT_FREE_SOCKETOBJ_HOLD)
|
||||
, m_dwFreeBufferObjHold (DEFAULT_FREE_BUFFEROBJ_HOLD)
|
||||
, m_dwMaxDatagramSize (DEFAULT_UDP_MAX_DATAGRAM_SIZE)
|
||||
, m_dwPostReceiveCount (DEFAULT_UDP_POST_RECEIVE_COUNT)
|
||||
, m_dwDetectAttempts (DEFAULT_UDP_DETECT_ATTEMPTS)
|
||||
, m_dwDetectInterval (DEFAULT_UDP_DETECT_INTERVAL)
|
||||
, m_bMarkSilence (TRUE)
|
||||
{
|
||||
ASSERT(m_pListener);
|
||||
}
|
||||
|
||||
virtual ~CUdpServer()
|
||||
{
|
||||
ENSURE_STOP();
|
||||
}
|
||||
|
||||
private:
|
||||
EnReuseAddressPolicy m_enReusePolicy;
|
||||
EnSendPolicy m_enSendPolicy;
|
||||
EnOnSendSyncPolicy m_enOnSendSyncPolicy;
|
||||
DWORD m_dwMaxConnectionCount;
|
||||
DWORD m_dwWorkerThreadCount;
|
||||
DWORD m_dwFreeSocketObjLockTime;
|
||||
DWORD m_dwFreeSocketObjPool;
|
||||
DWORD m_dwFreeBufferObjPool;
|
||||
DWORD m_dwFreeSocketObjHold;
|
||||
DWORD m_dwFreeBufferObjHold;
|
||||
DWORD m_dwMaxDatagramSize;
|
||||
DWORD m_dwPostReceiveCount;
|
||||
DWORD m_dwDetectAttempts;
|
||||
DWORD m_dwDetectInterval;
|
||||
BOOL m_bMarkSilence;
|
||||
|
||||
protected:
|
||||
CBufferObjPool m_bfObjPool;
|
||||
|
||||
private:
|
||||
CSEM m_evWait;
|
||||
|
||||
IUdpServerListener* m_pListener;
|
||||
ListenSocketsPtr m_soListens;
|
||||
EnServiceState m_enState;
|
||||
EnSocketError m_enLastError;
|
||||
|
||||
CReceiveBuffersPtr m_rcBuffers;
|
||||
|
||||
CPrivateHeap m_phSocket;
|
||||
|
||||
CSpinGuard m_csState;
|
||||
|
||||
FD m_fdGCTimer;
|
||||
|
||||
TUdpSocketObjPtrPool m_bfActiveSockets;
|
||||
|
||||
CSimpleRWLock m_csClientSocket;
|
||||
TSockAddrMap m_mpClientAddr;
|
||||
|
||||
TUdpSocketObjPtrList m_lsFreeSocket;
|
||||
TUdpSocketObjPtrQueue m_lsGCSocket;
|
||||
|
||||
CSendQueuesPtr m_quSends;
|
||||
|
||||
CIODispatcher m_ioDispatcher;
|
||||
};
|
||||
|
||||
#endif
|
||||
409
brotli/decode.h
Normal file
409
brotli/decode.h
Normal file
@@ -0,0 +1,409 @@
|
||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for Brotli decompression.
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_DEC_DECODE_H_
|
||||
#define BROTLI_DEC_DECODE_H_
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/shared_dictionary.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Opaque structure that holds decoder state.
|
||||
*
|
||||
* Allocated and initialized with ::BrotliDecoderCreateInstance.
|
||||
* Cleaned up and deallocated with ::BrotliDecoderDestroyInstance.
|
||||
*/
|
||||
typedef struct BrotliDecoderStateStruct BrotliDecoderState;
|
||||
|
||||
/**
|
||||
* Result type for ::BrotliDecoderDecompress and
|
||||
* ::BrotliDecoderDecompressStream functions.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Decoding error, e.g. corrupted input or memory allocation problem. */
|
||||
BROTLI_DECODER_RESULT_ERROR = 0,
|
||||
/** Decoding successfully completed. */
|
||||
BROTLI_DECODER_RESULT_SUCCESS = 1,
|
||||
/** Partially done; should be called again with more input. */
|
||||
BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT = 2,
|
||||
/** Partially done; should be called again with more output. */
|
||||
BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT = 3
|
||||
} BrotliDecoderResult;
|
||||
|
||||
/**
|
||||
* Template that evaluates items of ::BrotliDecoderErrorCode.
|
||||
*
|
||||
* Example: @code {.cpp}
|
||||
* // Log Brotli error code.
|
||||
* switch (brotliDecoderErrorCode) {
|
||||
* #define CASE_(PREFIX, NAME, CODE) \
|
||||
* case BROTLI_DECODER ## PREFIX ## NAME: \
|
||||
* LOG(INFO) << "error code:" << #NAME; \
|
||||
* break;
|
||||
* #define NEWLINE_
|
||||
* BROTLI_DECODER_ERROR_CODES_LIST(CASE_, NEWLINE_)
|
||||
* #undef CASE_
|
||||
* #undef NEWLINE_
|
||||
* default: LOG(FATAL) << "unknown brotli error code";
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE, SEPARATOR) \
|
||||
BROTLI_ERROR_CODE(_, NO_ERROR, 0) SEPARATOR \
|
||||
/* Same as BrotliDecoderResult values */ \
|
||||
BROTLI_ERROR_CODE(_, SUCCESS, 1) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_INPUT, 2) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_, NEEDS_MORE_OUTPUT, 3) SEPARATOR \
|
||||
\
|
||||
/* Errors caused by invalid input */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_NIBBLE, -1) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, RESERVED, -2) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, EXUBERANT_META_NIBBLE, -3) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_ALPHABET, -4) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, SIMPLE_HUFFMAN_SAME, -5) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CL_SPACE, -6) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, HUFFMAN_SPACE, -7) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, CONTEXT_MAP_REPEAT, -8) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_1, -9) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, BLOCK_LENGTH_2, -10) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, TRANSFORM, -11) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DICTIONARY, -12) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, WINDOW_BITS, -13) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_1, -14) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, PADDING_2, -15) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_FORMAT_, DISTANCE, -16) SEPARATOR \
|
||||
\
|
||||
/* -17 code is reserved */ \
|
||||
\
|
||||
BROTLI_ERROR_CODE(_ERROR_, COMPOUND_DICTIONARY, -18) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_, DICTIONARY_NOT_SET, -19) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_, INVALID_ARGUMENTS, -20) SEPARATOR \
|
||||
\
|
||||
/* Memory allocation problems */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MODES, -21) SEPARATOR \
|
||||
/* Literal, insert and distance trees together */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, TREE_GROUPS, -22) SEPARATOR \
|
||||
/* -23..-24 codes are reserved for distinct tree groups */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, CONTEXT_MAP, -25) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_1, -26) SEPARATOR \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, RING_BUFFER_2, -27) SEPARATOR \
|
||||
/* -28..-29 codes are reserved for dynamic ring-buffer allocation */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_ALLOC_, BLOCK_TYPE_TREES, -30) SEPARATOR \
|
||||
\
|
||||
/* "Impossible" states */ \
|
||||
BROTLI_ERROR_CODE(_ERROR_, UNREACHABLE, -31)
|
||||
|
||||
/**
|
||||
* Error code for detailed logging / production debugging.
|
||||
*
|
||||
* See ::BrotliDecoderGetErrorCode and ::BROTLI_LAST_ERROR_CODE.
|
||||
*/
|
||||
typedef enum {
|
||||
#define BROTLI_COMMA_ ,
|
||||
#define BROTLI_ERROR_CODE_ENUM_ITEM_(PREFIX, NAME, CODE) \
|
||||
BROTLI_DECODER ## PREFIX ## NAME = CODE
|
||||
BROTLI_DECODER_ERROR_CODES_LIST(BROTLI_ERROR_CODE_ENUM_ITEM_, BROTLI_COMMA_)
|
||||
} BrotliDecoderErrorCode;
|
||||
#undef BROTLI_ERROR_CODE_ENUM_ITEM_
|
||||
#undef BROTLI_COMMA_
|
||||
|
||||
/**
|
||||
* The value of the last error code, negative integer.
|
||||
*
|
||||
* All other error code values are in the range from ::BROTLI_LAST_ERROR_CODE
|
||||
* to @c -1. There are also 4 other possible non-error codes @c 0 .. @c 3 in
|
||||
* ::BrotliDecoderErrorCode enumeration.
|
||||
*/
|
||||
#define BROTLI_LAST_ERROR_CODE BROTLI_DECODER_ERROR_UNREACHABLE
|
||||
|
||||
/** Options to be used with ::BrotliDecoderSetParameter. */
|
||||
typedef enum BrotliDecoderParameter {
|
||||
/**
|
||||
* Disable "canny" ring buffer allocation strategy.
|
||||
*
|
||||
* Ring buffer is allocated according to window size, despite the real size of
|
||||
* the content.
|
||||
*/
|
||||
BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION = 0,
|
||||
/**
|
||||
* Flag that determines if "Large Window Brotli" is used.
|
||||
*/
|
||||
BROTLI_DECODER_PARAM_LARGE_WINDOW = 1
|
||||
} BrotliDecoderParameter;
|
||||
|
||||
/**
|
||||
* Sets the specified parameter to the given decoder instance.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @param param parameter to set
|
||||
* @param value new parameter value
|
||||
* @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
|
||||
* @returns ::BROTLI_TRUE if value is accepted
|
||||
*/
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderSetParameter(
|
||||
BrotliDecoderState* state, BrotliDecoderParameter param, uint32_t value);
|
||||
|
||||
/**
|
||||
* Adds LZ77 prefix dictionary, adds or replaces built-in static dictionary and
|
||||
* transforms.
|
||||
*
|
||||
* Attached dictionary ownership is not transferred.
|
||||
* Data provided to this method should be kept accessible until
|
||||
* decoding is finished and decoder instance is destroyed.
|
||||
*
|
||||
* @note Dictionaries can NOT be attached after actual decoding is started.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @param type dictionary data format
|
||||
* @param data_size length of memory region pointed by @p data
|
||||
* @param data dictionary data in format corresponding to @p type
|
||||
* @returns ::BROTLI_FALSE if dictionary is corrupted,
|
||||
* or dictionary count limit is reached
|
||||
* @returns ::BROTLI_TRUE if dictionary is accepted / attached
|
||||
*/
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderAttachDictionary(
|
||||
BrotliDecoderState* state, BrotliSharedDictionaryType type,
|
||||
size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
|
||||
|
||||
/**
|
||||
* Creates an instance of ::BrotliDecoderState and initializes it.
|
||||
*
|
||||
* The instance can be used once for decoding and should then be destroyed with
|
||||
* ::BrotliDecoderDestroyInstance, it cannot be reused for a new decoding
|
||||
* session.
|
||||
*
|
||||
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||
* case they are both zero, default memory allocators are used. @p opaque is
|
||||
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||
* has to return without doing anything when asked to free a NULL pointer.
|
||||
*
|
||||
* @param alloc_func custom memory allocation function
|
||||
* @param free_func custom memory free function
|
||||
* @param opaque custom memory manager handle
|
||||
* @returns @c 0 if instance can not be allocated or initialized
|
||||
* @returns pointer to initialized ::BrotliDecoderState otherwise
|
||||
*/
|
||||
BROTLI_DEC_API BrotliDecoderState* BrotliDecoderCreateInstance(
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
|
||||
/**
|
||||
* Deinitializes and frees ::BrotliDecoderState instance.
|
||||
*
|
||||
* @param state decoder instance to be cleaned up and deallocated
|
||||
*/
|
||||
BROTLI_DEC_API void BrotliDecoderDestroyInstance(BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Performs one-shot memory-to-memory decompression.
|
||||
*
|
||||
* Decompresses the data in @p encoded_buffer into @p decoded_buffer, and sets
|
||||
* @p *decoded_size to the decompressed length.
|
||||
*
|
||||
* @param encoded_size size of @p encoded_buffer
|
||||
* @param encoded_buffer compressed data buffer with at least @p encoded_size
|
||||
* addressable bytes
|
||||
* @param[in, out] decoded_size @b in: size of @p decoded_buffer; \n
|
||||
* @b out: length of decompressed data written to
|
||||
* @p decoded_buffer
|
||||
* @param decoded_buffer decompressed data destination buffer
|
||||
* @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
|
||||
* allocation failed, or @p decoded_buffer is not large enough;
|
||||
* @returns ::BROTLI_DECODER_RESULT_SUCCESS otherwise
|
||||
*/
|
||||
BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompress(
|
||||
size_t encoded_size,
|
||||
const uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(encoded_size)],
|
||||
size_t* decoded_size,
|
||||
uint8_t decoded_buffer[BROTLI_ARRAY_PARAM(*decoded_size)]);
|
||||
|
||||
/**
|
||||
* Decompresses the input stream to the output stream.
|
||||
*
|
||||
* The values @p *available_in and @p *available_out must specify the number of
|
||||
* bytes addressable at @p *next_in and @p *next_out respectively.
|
||||
* When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
|
||||
*
|
||||
* After each call, @p *available_in will be decremented by the amount of input
|
||||
* bytes consumed, and the @p *next_in pointer will be incremented by that
|
||||
* amount. Similarly, @p *available_out will be decremented by the amount of
|
||||
* output bytes written, and the @p *next_out pointer will be incremented by
|
||||
* that amount.
|
||||
*
|
||||
* @p total_out, if it is not a null-pointer, will be set to the number
|
||||
* of bytes decompressed since the last @p state initialization.
|
||||
*
|
||||
* @note Input is never overconsumed, so @p next_in and @p available_in could be
|
||||
* passed to the next consumer after decoding is complete.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @param[in, out] available_in @b in: amount of available input; \n
|
||||
* @b out: amount of unused input
|
||||
* @param[in, out] next_in pointer to the next compressed byte
|
||||
* @param[in, out] available_out @b in: length of output buffer; \n
|
||||
* @b out: remaining size of output buffer
|
||||
* @param[in, out] next_out output buffer cursor;
|
||||
* can be @c NULL if @p available_out is @c 0
|
||||
* @param[out] total_out number of bytes decompressed so far; can be @c NULL
|
||||
* @returns ::BROTLI_DECODER_RESULT_ERROR if input is corrupted, memory
|
||||
* allocation failed, arguments were invalid, etc.;
|
||||
* use ::BrotliDecoderGetErrorCode to get detailed error code
|
||||
* @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT decoding is blocked until
|
||||
* more input data is provided
|
||||
* @returns ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT decoding is blocked until
|
||||
* more output space is provided
|
||||
* @returns ::BROTLI_DECODER_RESULT_SUCCESS decoding is finished, no more
|
||||
* input might be consumed and no more output will be produced
|
||||
*/
|
||||
BROTLI_DEC_API BrotliDecoderResult BrotliDecoderDecompressStream(
|
||||
BrotliDecoderState* state, size_t* available_in, const uint8_t** next_in,
|
||||
size_t* available_out, uint8_t** next_out, size_t* total_out);
|
||||
|
||||
/**
|
||||
* Checks if decoder has more output.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @returns ::BROTLI_TRUE, if decoder has some unconsumed output
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderHasMoreOutput(
|
||||
const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Acquires pointer to internal output buffer.
|
||||
*
|
||||
* This method is used to make language bindings easier and more efficient:
|
||||
* -# push data to ::BrotliDecoderDecompressStream,
|
||||
* until ::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT is reported
|
||||
* -# use ::BrotliDecoderTakeOutput to peek bytes and copy to language-specific
|
||||
* entity
|
||||
*
|
||||
* Also this could be useful if there is an output stream that is able to
|
||||
* consume all the provided data (e.g. when data is saved to file system).
|
||||
*
|
||||
* @attention After every call to ::BrotliDecoderTakeOutput @p *size bytes of
|
||||
* output are considered consumed for all consecutive calls to the
|
||||
* instance methods; returned pointer becomes invalidated as well.
|
||||
*
|
||||
* @note Decoder output is not guaranteed to be contiguous. This means that
|
||||
* after the size-unrestricted call to ::BrotliDecoderTakeOutput,
|
||||
* immediate next call to ::BrotliDecoderTakeOutput may return more data.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
|
||||
* any amount could be handled; \n
|
||||
* @b out: amount of data pointed by returned pointer and
|
||||
* considered consumed; \n
|
||||
* out value is never greater than in value, unless it is @c 0
|
||||
* @returns pointer to output data
|
||||
*/
|
||||
BROTLI_DEC_API const uint8_t* BrotliDecoderTakeOutput(
|
||||
BrotliDecoderState* state, size_t* size);
|
||||
|
||||
/**
|
||||
* Checks if instance has already consumed input.
|
||||
*
|
||||
* Instance that returns ::BROTLI_FALSE is considered "fresh" and could be
|
||||
* reused.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @returns ::BROTLI_TRUE if decoder has already used some input bytes
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsUsed(const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Checks if decoder instance reached the final state.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @returns ::BROTLI_TRUE if decoder is in a state where it reached the end of
|
||||
* the input and produced all of the output
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_DEC_API BROTLI_BOOL BrotliDecoderIsFinished(
|
||||
const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Acquires a detailed error code.
|
||||
*
|
||||
* Should be used only after ::BrotliDecoderDecompressStream returns
|
||||
* ::BROTLI_DECODER_RESULT_ERROR.
|
||||
*
|
||||
* See also ::BrotliDecoderErrorString
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @returns last saved error code
|
||||
*/
|
||||
BROTLI_DEC_API BrotliDecoderErrorCode BrotliDecoderGetErrorCode(
|
||||
const BrotliDecoderState* state);
|
||||
|
||||
/**
|
||||
* Converts error code to a c-string.
|
||||
*/
|
||||
BROTLI_DEC_API const char* BrotliDecoderErrorString(BrotliDecoderErrorCode c);
|
||||
|
||||
/**
|
||||
* Gets a decoder library version.
|
||||
*
|
||||
* Look at BROTLI_MAKE_HEX_VERSION for more information.
|
||||
*/
|
||||
BROTLI_DEC_API uint32_t BrotliDecoderVersion(void);
|
||||
|
||||
/**
|
||||
* Callback to fire on metadata block start.
|
||||
*
|
||||
* After this callback is fired, if @p size is not @c 0, it is followed by
|
||||
* ::brotli_decoder_metadata_chunk_func as more metadata block contents become
|
||||
* accessible.
|
||||
*
|
||||
* @param opaque callback handle
|
||||
* @param size size of metadata block
|
||||
*/
|
||||
typedef void (*brotli_decoder_metadata_start_func)(void* opaque, size_t size);
|
||||
|
||||
/**
|
||||
* Callback to fire on metadata block chunk becomes available.
|
||||
*
|
||||
* This function can be invoked multiple times per metadata block; block should
|
||||
* be considered finished when sum of @p size matches the announced metadata
|
||||
* block size. Chunks contents pointed by @p data are transient and shouln not
|
||||
* be accessed after leaving the callback.
|
||||
*
|
||||
* @param opaque callback handle
|
||||
* @param data pointer to metadata contents
|
||||
* @param size size of metadata block chunk, at least @c 1
|
||||
*/
|
||||
typedef void (*brotli_decoder_metadata_chunk_func)(void* opaque,
|
||||
const uint8_t* data,
|
||||
size_t size);
|
||||
|
||||
/**
|
||||
* Sets callback for receiving metadata blocks.
|
||||
*
|
||||
* @param state decoder instance
|
||||
* @param start_func callback on metadata block start
|
||||
* @param chunk_func callback on metadata block chunk
|
||||
* @param opaque callback handle
|
||||
*/
|
||||
BROTLI_DEC_API void BrotliDecoderSetMetadataCallbacks(
|
||||
BrotliDecoderState* state,
|
||||
brotli_decoder_metadata_start_func start_func,
|
||||
brotli_decoder_metadata_chunk_func chunk_func, void* opaque);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_DEC_DECODE_H_ */
|
||||
501
brotli/encode.h
Normal file
501
brotli/encode.h
Normal file
@@ -0,0 +1,501 @@
|
||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* API for Brotli compression.
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_ENC_ENCODE_H_
|
||||
#define BROTLI_ENC_ENCODE_H_
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/shared_dictionary.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Minimal value for ::BROTLI_PARAM_LGWIN parameter. */
|
||||
#define BROTLI_MIN_WINDOW_BITS 10
|
||||
/**
|
||||
* Maximal value for ::BROTLI_PARAM_LGWIN parameter.
|
||||
*
|
||||
* @note equal to @c BROTLI_MAX_DISTANCE_BITS constant.
|
||||
*/
|
||||
#define BROTLI_MAX_WINDOW_BITS 24
|
||||
/**
|
||||
* Maximal value for ::BROTLI_PARAM_LGWIN parameter
|
||||
* in "Large Window Brotli" (32-bit).
|
||||
*/
|
||||
#define BROTLI_LARGE_MAX_WINDOW_BITS 30
|
||||
/** Minimal value for ::BROTLI_PARAM_LGBLOCK parameter. */
|
||||
#define BROTLI_MIN_INPUT_BLOCK_BITS 16
|
||||
/** Maximal value for ::BROTLI_PARAM_LGBLOCK parameter. */
|
||||
#define BROTLI_MAX_INPUT_BLOCK_BITS 24
|
||||
/** Minimal value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||
#define BROTLI_MIN_QUALITY 0
|
||||
/** Maximal value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||
#define BROTLI_MAX_QUALITY 11
|
||||
|
||||
/** Options for ::BROTLI_PARAM_MODE parameter. */
|
||||
typedef enum BrotliEncoderMode {
|
||||
/**
|
||||
* Default compression mode.
|
||||
*
|
||||
* In this mode compressor does not know anything in advance about the
|
||||
* properties of the input.
|
||||
*/
|
||||
BROTLI_MODE_GENERIC = 0,
|
||||
/** Compression mode for UTF-8 formatted text input. */
|
||||
BROTLI_MODE_TEXT = 1,
|
||||
/** Compression mode used in WOFF 2.0. */
|
||||
BROTLI_MODE_FONT = 2
|
||||
} BrotliEncoderMode;
|
||||
|
||||
/** Default value for ::BROTLI_PARAM_QUALITY parameter. */
|
||||
#define BROTLI_DEFAULT_QUALITY 11
|
||||
/** Default value for ::BROTLI_PARAM_LGWIN parameter. */
|
||||
#define BROTLI_DEFAULT_WINDOW 22
|
||||
/** Default value for ::BROTLI_PARAM_MODE parameter. */
|
||||
#define BROTLI_DEFAULT_MODE BROTLI_MODE_GENERIC
|
||||
|
||||
/** Operations that can be performed by streaming encoder. */
|
||||
typedef enum BrotliEncoderOperation {
|
||||
/**
|
||||
* Process input.
|
||||
*
|
||||
* Encoder may postpone producing output, until it has processed enough input.
|
||||
*/
|
||||
BROTLI_OPERATION_PROCESS = 0,
|
||||
/**
|
||||
* Produce output for all processed input.
|
||||
*
|
||||
* Actual flush is performed when input stream is depleted and there is enough
|
||||
* space in output stream. This means that client should repeat
|
||||
* ::BROTLI_OPERATION_FLUSH operation until @p available_in becomes @c 0, and
|
||||
* ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
|
||||
* via ::BrotliEncoderTakeOutput, then operation should be repeated after
|
||||
* output buffer is drained.
|
||||
*
|
||||
* @warning Until flush is complete, client @b SHOULD @b NOT swap,
|
||||
* reduce or extend input stream.
|
||||
*
|
||||
* When flush is complete, output data will be sufficient for decoder to
|
||||
* reproduce all the given input.
|
||||
*/
|
||||
BROTLI_OPERATION_FLUSH = 1,
|
||||
/**
|
||||
* Finalize the stream.
|
||||
*
|
||||
* Actual finalization is performed when input stream is depleted and there is
|
||||
* enough space in output stream. This means that client should repeat
|
||||
* ::BROTLI_OPERATION_FINISH operation until @p available_in becomes @c 0, and
|
||||
* ::BrotliEncoderHasMoreOutput returns ::BROTLI_FALSE. If output is acquired
|
||||
* via ::BrotliEncoderTakeOutput, then operation should be repeated after
|
||||
* output buffer is drained.
|
||||
*
|
||||
* @warning Until finalization is complete, client @b SHOULD @b NOT swap,
|
||||
* reduce or extend input stream.
|
||||
*
|
||||
* Helper function ::BrotliEncoderIsFinished checks if stream is finalized and
|
||||
* output fully dumped.
|
||||
*
|
||||
* Adding more input data to finalized stream is impossible.
|
||||
*/
|
||||
BROTLI_OPERATION_FINISH = 2,
|
||||
/**
|
||||
* Emit metadata block to stream.
|
||||
*
|
||||
* Metadata is opaque to Brotli: neither encoder, nor decoder processes this
|
||||
* data or relies on it. It may be used to pass some extra information from
|
||||
* encoder client to decoder client without interfering with main data stream.
|
||||
*
|
||||
* @note Encoder may emit empty metadata blocks internally, to pad encoded
|
||||
* stream to byte boundary.
|
||||
*
|
||||
* @warning Until emitting metadata is complete client @b SHOULD @b NOT swap,
|
||||
* reduce or extend input stream.
|
||||
*
|
||||
* @warning The whole content of input buffer is considered to be the content
|
||||
* of metadata block. Do @b NOT @e append metadata to input stream,
|
||||
* before it is depleted with other operations.
|
||||
*
|
||||
* Stream is soft-flushed before metadata block is emitted. Metadata block
|
||||
* @b MUST be no longer than than 16MiB.
|
||||
*/
|
||||
BROTLI_OPERATION_EMIT_METADATA = 3
|
||||
} BrotliEncoderOperation;
|
||||
|
||||
/** Options to be used with ::BrotliEncoderSetParameter. */
|
||||
typedef enum BrotliEncoderParameter {
|
||||
/**
|
||||
* Tune encoder for specific input.
|
||||
*
|
||||
* ::BrotliEncoderMode enumerates all available values.
|
||||
*/
|
||||
BROTLI_PARAM_MODE = 0,
|
||||
/**
|
||||
* The main compression speed-density lever.
|
||||
*
|
||||
* The higher the quality, the slower the compression. Range is
|
||||
* from ::BROTLI_MIN_QUALITY to ::BROTLI_MAX_QUALITY.
|
||||
*/
|
||||
BROTLI_PARAM_QUALITY = 1,
|
||||
/**
|
||||
* Recommended sliding LZ77 window size.
|
||||
*
|
||||
* Encoder may reduce this value, e.g. if input is much smaller than
|
||||
* window size.
|
||||
*
|
||||
* Window size is `(1 << value) - 16`.
|
||||
*
|
||||
* Range is from ::BROTLI_MIN_WINDOW_BITS to ::BROTLI_MAX_WINDOW_BITS.
|
||||
*/
|
||||
BROTLI_PARAM_LGWIN = 2,
|
||||
/**
|
||||
* Recommended input block size.
|
||||
*
|
||||
* Encoder may reduce this value, e.g. if input is much smaller than input
|
||||
* block size.
|
||||
*
|
||||
* Range is from ::BROTLI_MIN_INPUT_BLOCK_BITS to
|
||||
* ::BROTLI_MAX_INPUT_BLOCK_BITS.
|
||||
*
|
||||
* @note Bigger input block size allows better compression, but consumes more
|
||||
* memory. \n The rough formula of memory used for temporary input
|
||||
* storage is `3 << lgBlock`.
|
||||
*/
|
||||
BROTLI_PARAM_LGBLOCK = 3,
|
||||
/**
|
||||
* Flag that affects usage of "literal context modeling" format feature.
|
||||
*
|
||||
* This flag is a "decoding-speed vs compression ratio" trade-off.
|
||||
*/
|
||||
BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING = 4,
|
||||
/**
|
||||
* Estimated total input size for all ::BrotliEncoderCompressStream calls.
|
||||
*
|
||||
* The default value is 0, which means that the total input size is unknown.
|
||||
*/
|
||||
BROTLI_PARAM_SIZE_HINT = 5,
|
||||
/**
|
||||
* Flag that determines if "Large Window Brotli" is used.
|
||||
*/
|
||||
BROTLI_PARAM_LARGE_WINDOW = 6,
|
||||
/**
|
||||
* Recommended number of postfix bits (NPOSTFIX).
|
||||
*
|
||||
* Encoder may change this value.
|
||||
*
|
||||
* Range is from 0 to ::BROTLI_MAX_NPOSTFIX.
|
||||
*/
|
||||
BROTLI_PARAM_NPOSTFIX = 7,
|
||||
/**
|
||||
* Recommended number of direct distance codes (NDIRECT).
|
||||
*
|
||||
* Encoder may change this value.
|
||||
*
|
||||
* Range is from 0 to (15 << NPOSTFIX) in steps of (1 << NPOSTFIX).
|
||||
*/
|
||||
BROTLI_PARAM_NDIRECT = 8,
|
||||
/**
|
||||
* Number of bytes of input stream already processed by a different instance.
|
||||
*
|
||||
* @note It is important to configure all the encoder instances with same
|
||||
* parameters (except this one) in order to allow all the encoded parts
|
||||
* obey the same restrictions implied by header.
|
||||
*
|
||||
* If offset is not 0, then stream header is omitted.
|
||||
* In any case output start is byte aligned, so for proper streams stitching
|
||||
* "predecessor" stream must be flushed.
|
||||
*
|
||||
* Range is not artificially limited, but all the values greater or equal to
|
||||
* maximal window size have the same effect. Values greater than 2**30 are not
|
||||
* allowed.
|
||||
*/
|
||||
BROTLI_PARAM_STREAM_OFFSET = 9
|
||||
} BrotliEncoderParameter;
|
||||
|
||||
/**
|
||||
* Opaque structure that holds encoder state.
|
||||
*
|
||||
* Allocated and initialized with ::BrotliEncoderCreateInstance.
|
||||
* Cleaned up and deallocated with ::BrotliEncoderDestroyInstance.
|
||||
*/
|
||||
typedef struct BrotliEncoderStateStruct BrotliEncoderState;
|
||||
|
||||
/**
|
||||
* Sets the specified parameter to the given encoder instance.
|
||||
*
|
||||
* @param state encoder instance
|
||||
* @param param parameter to set
|
||||
* @param value new parameter value
|
||||
* @returns ::BROTLI_FALSE if parameter is unrecognized, or value is invalid
|
||||
* @returns ::BROTLI_FALSE if value of parameter can not be changed at current
|
||||
* encoder state (e.g. when encoding is started, window size might be
|
||||
* already encoded and therefore it is impossible to change it)
|
||||
* @returns ::BROTLI_TRUE if value is accepted
|
||||
* @warning invalid values might be accepted in case they would not break
|
||||
* encoding process.
|
||||
*/
|
||||
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderSetParameter(
|
||||
BrotliEncoderState* state, BrotliEncoderParameter param, uint32_t value);
|
||||
|
||||
/**
|
||||
* Creates an instance of ::BrotliEncoderState and initializes it.
|
||||
*
|
||||
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||
* case they are both zero, default memory allocators are used. @p opaque is
|
||||
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||
* has to return without doing anything when asked to free a NULL pointer.
|
||||
*
|
||||
* @param alloc_func custom memory allocation function
|
||||
* @param free_func custom memory free function
|
||||
* @param opaque custom memory manager handle
|
||||
* @returns @c 0 if instance can not be allocated or initialized
|
||||
* @returns pointer to initialized ::BrotliEncoderState otherwise
|
||||
*/
|
||||
BROTLI_ENC_API BrotliEncoderState* BrotliEncoderCreateInstance(
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
|
||||
/**
|
||||
* Deinitializes and frees ::BrotliEncoderState instance.
|
||||
*
|
||||
* @param state decoder instance to be cleaned up and deallocated
|
||||
*/
|
||||
BROTLI_ENC_API void BrotliEncoderDestroyInstance(BrotliEncoderState* state);
|
||||
|
||||
/* Opaque type for pointer to different possible internal structures containing
|
||||
dictionary prepared for the encoder */
|
||||
typedef struct BrotliEncoderPreparedDictionaryStruct
|
||||
BrotliEncoderPreparedDictionary;
|
||||
|
||||
/**
|
||||
* Prepares a shared dictionary from the given file format for the encoder.
|
||||
*
|
||||
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||
* case they are both zero, default memory allocators are used. @p opaque is
|
||||
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||
* has to return without doing anything when asked to free a NULL pointer.
|
||||
*
|
||||
* @param type type of dictionary stored in data
|
||||
* @param data_size size of @p data buffer
|
||||
* @param data pointer to the dictionary data
|
||||
* @param quality the maximum Brotli quality to prepare the dictionary for,
|
||||
* use BROTLI_MAX_QUALITY by default
|
||||
* @param alloc_func custom memory allocation function
|
||||
* @param free_func custom memory free function
|
||||
* @param opaque custom memory manager handle
|
||||
*/
|
||||
BROTLI_ENC_API BrotliEncoderPreparedDictionary*
|
||||
BrotliEncoderPrepareDictionary(BrotliSharedDictionaryType type,
|
||||
size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)],
|
||||
int quality,
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
|
||||
BROTLI_ENC_API void BrotliEncoderDestroyPreparedDictionary(
|
||||
BrotliEncoderPreparedDictionary* dictionary);
|
||||
|
||||
/**
|
||||
* Attaches a prepared dictionary of any type to the encoder. Can be used
|
||||
* multiple times to attach multiple dictionaries. The dictionary type was
|
||||
* determined by BrotliEncoderPrepareDictionary. Multiple raw prefix
|
||||
* dictionaries and/or max 1 serialized dictionary with custom words can be
|
||||
* attached.
|
||||
*
|
||||
* @returns ::BROTLI_FALSE in case of error
|
||||
* @returns ::BROTLI_TRUE otherwise
|
||||
*/
|
||||
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderAttachPreparedDictionary(
|
||||
BrotliEncoderState* state,
|
||||
const BrotliEncoderPreparedDictionary* dictionary);
|
||||
|
||||
/**
|
||||
* Calculates the output size bound for the given @p input_size.
|
||||
*
|
||||
* @warning Result is only valid if quality is at least @c 2 and, in
|
||||
* case ::BrotliEncoderCompressStream was used, no flushes
|
||||
* (::BROTLI_OPERATION_FLUSH) were performed.
|
||||
*
|
||||
* @param input_size size of projected input
|
||||
* @returns @c 0 if result does not fit @c size_t
|
||||
*/
|
||||
BROTLI_ENC_API size_t BrotliEncoderMaxCompressedSize(size_t input_size);
|
||||
|
||||
/**
|
||||
* Performs one-shot memory-to-memory compression.
|
||||
*
|
||||
* Compresses the data in @p input_buffer into @p encoded_buffer, and sets
|
||||
* @p *encoded_size to the compressed length.
|
||||
*
|
||||
* @note If ::BrotliEncoderMaxCompressedSize(@p input_size) returns non-zero
|
||||
* value, then output is guaranteed to be no longer than that.
|
||||
*
|
||||
* @note If @p lgwin is greater than ::BROTLI_MAX_WINDOW_BITS then resulting
|
||||
* stream might be incompatible with RFC 7932; to decode such streams,
|
||||
* decoder should be configured with
|
||||
* ::BROTLI_DECODER_PARAM_LARGE_WINDOW = @c 1
|
||||
*
|
||||
* @param quality quality parameter value, e.g. ::BROTLI_DEFAULT_QUALITY
|
||||
* @param lgwin lgwin parameter value, e.g. ::BROTLI_DEFAULT_WINDOW
|
||||
* @param mode mode parameter value, e.g. ::BROTLI_DEFAULT_MODE
|
||||
* @param input_size size of @p input_buffer
|
||||
* @param input_buffer input data buffer with at least @p input_size
|
||||
* addressable bytes
|
||||
* @param[in, out] encoded_size @b in: size of @p encoded_buffer; \n
|
||||
* @b out: length of compressed data written to
|
||||
* @p encoded_buffer, or @c 0 if compression fails
|
||||
* @param encoded_buffer compressed data destination buffer
|
||||
* @returns ::BROTLI_FALSE in case of compression error
|
||||
* @returns ::BROTLI_FALSE if output buffer is too small
|
||||
* @returns ::BROTLI_TRUE otherwise
|
||||
*/
|
||||
BROTLI_ENC_API int BrotliEncoderCompress(
|
||||
int quality, int lgwin, BrotliEncoderMode mode, size_t input_size,
|
||||
const uint8_t input_buffer[BROTLI_ARRAY_PARAM(input_size)],
|
||||
size_t* encoded_size,
|
||||
uint8_t encoded_buffer[BROTLI_ARRAY_PARAM(*encoded_size)]);
|
||||
|
||||
/**
|
||||
* Compresses input stream to output stream.
|
||||
*
|
||||
* The values @p *available_in and @p *available_out must specify the number of
|
||||
* bytes addressable at @p *next_in and @p *next_out respectively.
|
||||
* When @p *available_out is @c 0, @p next_out is allowed to be @c NULL.
|
||||
*
|
||||
* After each call, @p *available_in will be decremented by the amount of input
|
||||
* bytes consumed, and the @p *next_in pointer will be incremented by that
|
||||
* amount. Similarly, @p *available_out will be decremented by the amount of
|
||||
* output bytes written, and the @p *next_out pointer will be incremented by
|
||||
* that amount.
|
||||
*
|
||||
* @p total_out, if it is not a null-pointer, will be set to the number
|
||||
* of bytes compressed since the last @p state initialization.
|
||||
*
|
||||
*
|
||||
*
|
||||
* Internally workflow consists of 3 tasks:
|
||||
* -# (optionally) copy input data to internal buffer
|
||||
* -# actually compress data and (optionally) store it to internal buffer
|
||||
* -# (optionally) copy compressed bytes from internal buffer to output stream
|
||||
*
|
||||
* Whenever all 3 tasks can't move forward anymore, or error occurs, this
|
||||
* method returns the control flow to caller.
|
||||
*
|
||||
* @p op is used to perform flush, finish the stream, or inject metadata block.
|
||||
* See ::BrotliEncoderOperation for more information.
|
||||
*
|
||||
* Flushing the stream means forcing encoding of all input passed to encoder and
|
||||
* completing the current output block, so it could be fully decoded by stream
|
||||
* decoder. To perform flush set @p op to ::BROTLI_OPERATION_FLUSH.
|
||||
* Under some circumstances (e.g. lack of output stream capacity) this operation
|
||||
* would require several calls to ::BrotliEncoderCompressStream. The method must
|
||||
* be called again until both input stream is depleted and encoder has no more
|
||||
* output (see ::BrotliEncoderHasMoreOutput) after the method is called.
|
||||
*
|
||||
* Finishing the stream means encoding of all input passed to encoder and
|
||||
* adding specific "final" marks, so stream decoder could determine that stream
|
||||
* is complete. To perform finish set @p op to ::BROTLI_OPERATION_FINISH.
|
||||
* Under some circumstances (e.g. lack of output stream capacity) this operation
|
||||
* would require several calls to ::BrotliEncoderCompressStream. The method must
|
||||
* be called again until both input stream is depleted and encoder has no more
|
||||
* output (see ::BrotliEncoderHasMoreOutput) after the method is called.
|
||||
*
|
||||
* @warning When flushing and finishing, @p op should not change until operation
|
||||
* is complete; input stream should not be swapped, reduced or
|
||||
* extended as well.
|
||||
*
|
||||
* @param state encoder instance
|
||||
* @param op requested operation
|
||||
* @param[in, out] available_in @b in: amount of available input; \n
|
||||
* @b out: amount of unused input
|
||||
* @param[in, out] next_in pointer to the next input byte
|
||||
* @param[in, out] available_out @b in: length of output buffer; \n
|
||||
* @b out: remaining size of output buffer
|
||||
* @param[in, out] next_out compressed output buffer cursor;
|
||||
* can be @c NULL if @p available_out is @c 0
|
||||
* @param[out] total_out number of bytes produced so far; can be @c NULL
|
||||
* @returns ::BROTLI_FALSE if there was an error
|
||||
* @returns ::BROTLI_TRUE otherwise
|
||||
*/
|
||||
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderCompressStream(
|
||||
BrotliEncoderState* state, BrotliEncoderOperation op, size_t* available_in,
|
||||
const uint8_t** next_in, size_t* available_out, uint8_t** next_out,
|
||||
size_t* total_out);
|
||||
|
||||
/**
|
||||
* Checks if encoder instance reached the final state.
|
||||
*
|
||||
* @param state encoder instance
|
||||
* @returns ::BROTLI_TRUE if encoder is in a state where it reached the end of
|
||||
* the input and produced all of the output
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderIsFinished(BrotliEncoderState* state);
|
||||
|
||||
/**
|
||||
* Checks if encoder has more output.
|
||||
*
|
||||
* @param state encoder instance
|
||||
* @returns ::BROTLI_TRUE, if encoder has some unconsumed output
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_ENC_API BROTLI_BOOL BrotliEncoderHasMoreOutput(
|
||||
BrotliEncoderState* state);
|
||||
|
||||
/**
|
||||
* Acquires pointer to internal output buffer.
|
||||
*
|
||||
* This method is used to make language bindings easier and more efficient:
|
||||
* -# push data to ::BrotliEncoderCompressStream,
|
||||
* until ::BrotliEncoderHasMoreOutput returns BROTLI_TRUE
|
||||
* -# use ::BrotliEncoderTakeOutput to peek bytes and copy to language-specific
|
||||
* entity
|
||||
*
|
||||
* Also this could be useful if there is an output stream that is able to
|
||||
* consume all the provided data (e.g. when data is saved to file system).
|
||||
*
|
||||
* @attention After every call to ::BrotliEncoderTakeOutput @p *size bytes of
|
||||
* output are considered consumed for all consecutive calls to the
|
||||
* instance methods; returned pointer becomes invalidated as well.
|
||||
*
|
||||
* @note Encoder output is not guaranteed to be contiguous. This means that
|
||||
* after the size-unrestricted call to ::BrotliEncoderTakeOutput,
|
||||
* immediate next call to ::BrotliEncoderTakeOutput may return more data.
|
||||
*
|
||||
* @param state encoder instance
|
||||
* @param[in, out] size @b in: number of bytes caller is ready to take, @c 0 if
|
||||
* any amount could be handled; \n
|
||||
* @b out: amount of data pointed by returned pointer and
|
||||
* considered consumed; \n
|
||||
* out value is never greater than in value, unless it is @c 0
|
||||
* @returns pointer to output data
|
||||
*/
|
||||
BROTLI_ENC_API const uint8_t* BrotliEncoderTakeOutput(
|
||||
BrotliEncoderState* state, size_t* size);
|
||||
|
||||
/* Returns the estimated peak memory usage (in bytes) of the BrotliCompress()
|
||||
function, not counting the memory needed for the input and output. */
|
||||
BROTLI_ENC_EXTRA_API size_t BrotliEncoderEstimatePeakMemoryUsage(
|
||||
int quality, int lgwin, size_t input_size);
|
||||
/* Returns 0 if dictionary is not valid; otherwise returns allocation size. */
|
||||
BROTLI_ENC_EXTRA_API size_t BrotliEncoderGetPreparedDictionarySize(
|
||||
const BrotliEncoderPreparedDictionary* dictionary);
|
||||
|
||||
/**
|
||||
* Gets an encoder library version.
|
||||
*
|
||||
* Look at BROTLI_MAKE_HEX_VERSION for more information.
|
||||
*/
|
||||
BROTLI_ENC_API uint32_t BrotliEncoderVersion(void);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_ENC_ENCODE_H_ */
|
||||
305
brotli/port.h
Normal file
305
brotli/port.h
Normal file
@@ -0,0 +1,305 @@
|
||||
/* Copyright 2016 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* Macros for compiler / platform specific API declarations. */
|
||||
|
||||
#ifndef BROTLI_COMMON_PORT_H_
|
||||
#define BROTLI_COMMON_PORT_H_
|
||||
|
||||
/* The following macros were borrowed from https://github.com/nemequ/hedley
|
||||
* with permission of original author - Evan Nemerson <evan@nemerson.com> */
|
||||
|
||||
/* >>> >>> >>> hedley macros */
|
||||
|
||||
#define BROTLI_MAKE_VERSION(major, minor, revision) \
|
||||
(((major) * 1000000) + ((minor) * 1000) + (revision))
|
||||
|
||||
#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
|
||||
#define BROTLI_GNUC_VERSION \
|
||||
BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
|
||||
#elif defined(__GNUC__)
|
||||
#define BROTLI_GNUC_VERSION BROTLI_MAKE_VERSION(__GNUC__, __GNUC_MINOR__, 0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_GNUC_VERSION)
|
||||
#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_GNUC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_GNUC_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)
|
||||
#define BROTLI_MSVC_VERSION \
|
||||
BROTLI_MAKE_VERSION((_MSC_FULL_VER / 10000000), \
|
||||
(_MSC_FULL_VER % 10000000) / 100000, \
|
||||
(_MSC_FULL_VER % 100000) / 100)
|
||||
#elif defined(_MSC_FULL_VER)
|
||||
#define BROTLI_MSVC_VERSION \
|
||||
BROTLI_MAKE_VERSION((_MSC_FULL_VER / 1000000), \
|
||||
(_MSC_FULL_VER % 1000000) / 10000, \
|
||||
(_MSC_FULL_VER % 10000) / 10)
|
||||
#elif defined(_MSC_VER)
|
||||
#define BROTLI_MSVC_VERSION \
|
||||
BROTLI_MAKE_VERSION(_MSC_VER / 100, _MSC_VER % 100, 0)
|
||||
#endif
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) (0)
|
||||
#elif defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||
(_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
|
||||
#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
|
||||
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||
(_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
|
||||
#else
|
||||
#define BROTLI_MSVC_VERSION_CHECK(major, minor, patch) \
|
||||
(_MSC_VER >= ((major * 100) + (minor)))
|
||||
#endif
|
||||
|
||||
#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)
|
||||
#define BROTLI_INTEL_VERSION \
|
||||
BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, \
|
||||
__INTEL_COMPILER % 100, \
|
||||
__INTEL_COMPILER_UPDATE)
|
||||
#elif defined(__INTEL_COMPILER)
|
||||
#define BROTLI_INTEL_VERSION \
|
||||
BROTLI_MAKE_VERSION(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_INTEL_VERSION)
|
||||
#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_INTEL_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_INTEL_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__PGI) && \
|
||||
defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
|
||||
#define BROTLI_PGI_VERSION \
|
||||
BROTLI_MAKE_VERSION(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_PGI_VERSION)
|
||||
#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_PGI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_PGI_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
|
||||
#define BROTLI_SUNPRO_VERSION \
|
||||
BROTLI_MAKE_VERSION( \
|
||||
(((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), \
|
||||
(((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), \
|
||||
(__SUNPRO_C & 0xf) * 10)
|
||||
#elif defined(__SUNPRO_C)
|
||||
#define BROTLI_SUNPRO_VERSION \
|
||||
BROTLI_MAKE_VERSION((__SUNPRO_C >> 8) & 0xf, \
|
||||
(__SUNPRO_C >> 4) & 0xf, \
|
||||
(__SUNPRO_C) & 0xf)
|
||||
#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
|
||||
#define BROTLI_SUNPRO_VERSION \
|
||||
BROTLI_MAKE_VERSION( \
|
||||
(((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), \
|
||||
(((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), \
|
||||
(__SUNPRO_CC & 0xf) * 10)
|
||||
#elif defined(__SUNPRO_CC)
|
||||
#define BROTLI_SUNPRO_VERSION \
|
||||
BROTLI_MAKE_VERSION((__SUNPRO_CC >> 8) & 0xf, \
|
||||
(__SUNPRO_CC >> 4) & 0xf, \
|
||||
(__SUNPRO_CC) & 0xf)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_SUNPRO_VERSION)
|
||||
#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_SUNPRO_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_SUNPRO_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
|
||||
#define BROTLI_ARM_VERSION \
|
||||
BROTLI_MAKE_VERSION((__ARMCOMPILER_VERSION / 1000000), \
|
||||
(__ARMCOMPILER_VERSION % 1000000) / 10000, \
|
||||
(__ARMCOMPILER_VERSION % 10000) / 100)
|
||||
#elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
|
||||
#define BROTLI_ARM_VERSION \
|
||||
BROTLI_MAKE_VERSION((__ARMCC_VERSION / 1000000), \
|
||||
(__ARMCC_VERSION % 1000000) / 10000, \
|
||||
(__ARMCC_VERSION % 10000) / 100)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_ARM_VERSION)
|
||||
#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_ARM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_ARM_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__ibmxl__)
|
||||
#define BROTLI_IBM_VERSION \
|
||||
BROTLI_MAKE_VERSION(__ibmxl_version__, \
|
||||
__ibmxl_release__, \
|
||||
__ibmxl_modification__)
|
||||
#elif defined(__xlC__) && defined(__xlC_ver__)
|
||||
#define BROTLI_IBM_VERSION \
|
||||
BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
|
||||
#elif defined(__xlC__)
|
||||
#define BROTLI_IBM_VERSION BROTLI_MAKE_VERSION(__xlC__ >> 8, __xlC__ & 0xff, 0)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_IBM_VERSION)
|
||||
#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_IBM_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_IBM_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__TI_COMPILER_VERSION__)
|
||||
#define BROTLI_TI_VERSION \
|
||||
BROTLI_MAKE_VERSION((__TI_COMPILER_VERSION__ / 1000000), \
|
||||
(__TI_COMPILER_VERSION__ % 1000000) / 1000, \
|
||||
(__TI_COMPILER_VERSION__ % 1000))
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_TI_VERSION)
|
||||
#define BROTLI_TI_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_TI_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_TI_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__IAR_SYSTEMS_ICC__)
|
||||
#if __VER__ > 1000
|
||||
#define BROTLI_IAR_VERSION \
|
||||
BROTLI_MAKE_VERSION((__VER__ / 1000000), \
|
||||
(__VER__ / 1000) % 1000, \
|
||||
(__VER__ % 1000))
|
||||
#else
|
||||
#define BROTLI_IAR_VERSION BROTLI_MAKE_VERSION(VER / 100, __VER__ % 100, 0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_IAR_VERSION)
|
||||
#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_IAR_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_IAR_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__TINYC__)
|
||||
#define BROTLI_TINYC_VERSION \
|
||||
BROTLI_MAKE_VERSION(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_TINYC_VERSION)
|
||||
#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) \
|
||||
(BROTLI_TINYC_VERSION >= BROTLI_MAKE_VERSION(major, minor, patch))
|
||||
#else
|
||||
#define BROTLI_TINYC_VERSION_CHECK(major, minor, patch) (0)
|
||||
#endif
|
||||
|
||||
#if defined(__has_attribute)
|
||||
#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
|
||||
__has_attribute(attribute)
|
||||
#else
|
||||
#define BROTLI_GNUC_HAS_ATTRIBUTE(attribute, major, minor, patch) \
|
||||
BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
|
||||
#endif
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
|
||||
__has_builtin(builtin)
|
||||
#else
|
||||
#define BROTLI_GNUC_HAS_BUILTIN(builtin, major, minor, patch) \
|
||||
BROTLI_GNUC_VERSION_CHECK(major, minor, patch)
|
||||
#endif
|
||||
|
||||
#if defined(__has_feature)
|
||||
#define BROTLI_HAS_FEATURE(feature) __has_feature(feature)
|
||||
#else
|
||||
#define BROTLI_HAS_FEATURE(feature) (0)
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define BROTLI_PUBLIC
|
||||
#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && \
|
||||
defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
|
||||
#define BROTLI_PUBLIC __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define BROTLI_PUBLIC
|
||||
#endif
|
||||
|
||||
/* BROTLI_INTERNAL could be defined to override visibility, e.g. for tests. */
|
||||
#if !defined(BROTLI_INTERNAL)
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define BROTLI_INTERNAL
|
||||
#elif BROTLI_GNUC_VERSION_CHECK(3, 3, 0) || \
|
||||
BROTLI_TI_VERSION_CHECK(8, 0, 0) || \
|
||||
BROTLI_INTEL_VERSION_CHECK(16, 0, 0) || \
|
||||
BROTLI_ARM_VERSION_CHECK(4, 1, 0) || \
|
||||
BROTLI_IBM_VERSION_CHECK(13, 1, 0) || \
|
||||
BROTLI_SUNPRO_VERSION_CHECK(5, 11, 0) || \
|
||||
(BROTLI_TI_VERSION_CHECK(7, 3, 0) && \
|
||||
defined(__TI_GNU_ATTRIBUTE_SUPPORT__) && defined(__TI_EABI__))
|
||||
#define BROTLI_INTERNAL __attribute__ ((visibility ("hidden")))
|
||||
#else
|
||||
#define BROTLI_INTERNAL
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
|
||||
!defined(__STDC_NO_VLA__) && !defined(__cplusplus) && \
|
||||
!defined(__PGI) && !defined(__PGIC__) && !defined(__TINYC__) && \
|
||||
!defined(__clang__)
|
||||
#define BROTLI_ARRAY_PARAM(name) (name)
|
||||
#else
|
||||
#define BROTLI_ARRAY_PARAM(name)
|
||||
#endif
|
||||
|
||||
/* <<< <<< <<< end of hedley macros. */
|
||||
|
||||
#if defined(BROTLI_SHARED_COMPILATION)
|
||||
#if defined(_WIN32)
|
||||
#if defined(BROTLICOMMON_SHARED_COMPILATION)
|
||||
#define BROTLI_COMMON_API __declspec(dllexport)
|
||||
#else
|
||||
#define BROTLI_COMMON_API __declspec(dllimport)
|
||||
#endif /* BROTLICOMMON_SHARED_COMPILATION */
|
||||
#if defined(BROTLIDEC_SHARED_COMPILATION)
|
||||
#define BROTLI_DEC_API __declspec(dllexport)
|
||||
#else
|
||||
#define BROTLI_DEC_API __declspec(dllimport)
|
||||
#endif /* BROTLIDEC_SHARED_COMPILATION */
|
||||
#if defined(BROTLIENC_SHARED_COMPILATION)
|
||||
#define BROTLI_ENC_API __declspec(dllexport)
|
||||
#else
|
||||
#define BROTLI_ENC_API __declspec(dllimport)
|
||||
#endif /* BROTLIENC_SHARED_COMPILATION */
|
||||
#else /* _WIN32 */
|
||||
#define BROTLI_COMMON_API BROTLI_PUBLIC
|
||||
#define BROTLI_DEC_API BROTLI_PUBLIC
|
||||
#define BROTLI_ENC_API BROTLI_PUBLIC
|
||||
#endif /* _WIN32 */
|
||||
#else /* BROTLI_SHARED_COMPILATION */
|
||||
#define BROTLI_COMMON_API
|
||||
#define BROTLI_DEC_API
|
||||
#define BROTLI_ENC_API
|
||||
#endif
|
||||
|
||||
#if defined(BROTLI_BUILD_ENC_EXTRA_API)
|
||||
#define BROTLI_ENC_EXTRA_API BROTLI_ENC_API
|
||||
#else
|
||||
#define BROTLI_ENC_EXTRA_API BROTLI_INTERNAL
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_COMMON_PORT_H_ */
|
||||
100
brotli/shared_dictionary.h
Normal file
100
brotli/shared_dictionary.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/* Copyright 2017 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/* (Opaque) Shared Dictionary definition and utilities. */
|
||||
|
||||
#ifndef BROTLI_COMMON_SHARED_DICTIONARY_H_
|
||||
#define BROTLI_COMMON_SHARED_DICTIONARY_H_
|
||||
|
||||
#include <brotli/port.h>
|
||||
#include <brotli/types.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SHARED_BROTLI_MIN_DICTIONARY_WORD_LENGTH 4
|
||||
#define SHARED_BROTLI_MAX_DICTIONARY_WORD_LENGTH 31
|
||||
#define SHARED_BROTLI_NUM_DICTIONARY_CONTEXTS 64
|
||||
#define SHARED_BROTLI_MAX_COMPOUND_DICTS 15
|
||||
|
||||
/**
|
||||
* Opaque structure that holds shared dictionary data.
|
||||
*
|
||||
* Allocated and initialized with ::BrotliSharedDictionaryCreateInstance.
|
||||
* Cleaned up and deallocated with ::BrotliSharedDictionaryDestroyInstance.
|
||||
*/
|
||||
typedef struct BrotliSharedDictionaryStruct BrotliSharedDictionary;
|
||||
|
||||
/**
|
||||
* Input data type for ::BrotliSharedDictionaryAttach.
|
||||
*/
|
||||
typedef enum BrotliSharedDictionaryType {
|
||||
/** Raw LZ77 prefix dictionary. */
|
||||
BROTLI_SHARED_DICTIONARY_RAW = 0,
|
||||
/** Serialized shared dictionary.
|
||||
*
|
||||
* DO NOT USE: methods accepting this value will fail.
|
||||
*/
|
||||
BROTLI_SHARED_DICTIONARY_SERIALIZED = 1
|
||||
} BrotliSharedDictionaryType;
|
||||
|
||||
/**
|
||||
* Creates an instance of ::BrotliSharedDictionary.
|
||||
*
|
||||
* Fresh instance has default word dictionary and transforms
|
||||
* and no LZ77 prefix dictionary.
|
||||
*
|
||||
* @p alloc_func and @p free_func @b MUST be both zero or both non-zero. In the
|
||||
* case they are both zero, default memory allocators are used. @p opaque is
|
||||
* passed to @p alloc_func and @p free_func when they are called. @p free_func
|
||||
* has to return without doing anything when asked to free a NULL pointer.
|
||||
*
|
||||
* @param alloc_func custom memory allocation function
|
||||
* @param free_func custom memory free function
|
||||
* @param opaque custom memory manager handle
|
||||
* @returns @c 0 if instance can not be allocated or initialized
|
||||
* @returns pointer to initialized ::BrotliSharedDictionary otherwise
|
||||
*/
|
||||
BROTLI_COMMON_API BrotliSharedDictionary* BrotliSharedDictionaryCreateInstance(
|
||||
brotli_alloc_func alloc_func, brotli_free_func free_func, void* opaque);
|
||||
|
||||
/**
|
||||
* Deinitializes and frees ::BrotliSharedDictionary instance.
|
||||
*
|
||||
* @param dict shared dictionary instance to be cleaned up and deallocated
|
||||
*/
|
||||
BROTLI_COMMON_API void BrotliSharedDictionaryDestroyInstance(
|
||||
BrotliSharedDictionary* dict);
|
||||
|
||||
/**
|
||||
* Attaches dictionary to a given instance of ::BrotliSharedDictionary.
|
||||
*
|
||||
* Dictionary to be attached is represented in a serialized format as a region
|
||||
* of memory.
|
||||
*
|
||||
* Provided data it partially referenced by a resulting (compound) dictionary,
|
||||
* and should be kept untouched, while at least one compound dictionary uses it.
|
||||
* This way memory overhead is kept minimal by the cost of additional resource
|
||||
* management.
|
||||
*
|
||||
* @param dict dictionary to extend
|
||||
* @param type type of dictionary to attach
|
||||
* @param data_size size of @p data
|
||||
* @param data serialized dictionary of type @p type, with at least @p data_size
|
||||
* addressable bytes
|
||||
* @returns ::BROTLI_TRUE if provided dictionary is successfully attached
|
||||
* @returns ::BROTLI_FALSE otherwise
|
||||
*/
|
||||
BROTLI_COMMON_API BROTLI_BOOL BrotliSharedDictionaryAttach(
|
||||
BrotliSharedDictionary* dict, BrotliSharedDictionaryType type,
|
||||
size_t data_size, const uint8_t data[BROTLI_ARRAY_PARAM(data_size)]);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* BROTLI_COMMON_SHARED_DICTIONARY_H_ */
|
||||
83
brotli/types.h
Normal file
83
brotli/types.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* Copyright 2013 Google Inc. All Rights Reserved.
|
||||
|
||||
Distributed under MIT license.
|
||||
See file LICENSE for detail or copy at https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Common types used in decoder and encoder API.
|
||||
*/
|
||||
|
||||
#ifndef BROTLI_COMMON_TYPES_H_
|
||||
#define BROTLI_COMMON_TYPES_H_
|
||||
|
||||
#include <stddef.h> /* for size_t */
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||
typedef __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
typedef __int64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER < 1600) */
|
||||
|
||||
/**
|
||||
* A portable @c bool replacement.
|
||||
*
|
||||
* ::BROTLI_BOOL is a "documentation" type: actually it is @c int, but in API it
|
||||
* denotes a type, whose only values are ::BROTLI_TRUE and ::BROTLI_FALSE.
|
||||
*
|
||||
* ::BROTLI_BOOL values passed to Brotli should either be ::BROTLI_TRUE or
|
||||
* ::BROTLI_FALSE, or be a result of ::TO_BROTLI_BOOL macros.
|
||||
*
|
||||
* ::BROTLI_BOOL values returned by Brotli should not be tested for equality
|
||||
* with @c true, @c false, ::BROTLI_TRUE, ::BROTLI_FALSE, but rather should be
|
||||
* evaluated, for example: @code{.cpp}
|
||||
* if (SomeBrotliFunction(encoder, BROTLI_TRUE) &&
|
||||
* !OtherBrotliFunction(decoder, BROTLI_FALSE)) {
|
||||
* bool x = !!YetAnotherBrotliFunction(encoder, TO_BROLTI_BOOL(2 * 2 == 4));
|
||||
* DoSomething(x);
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
#define BROTLI_BOOL int
|
||||
/** Portable @c true replacement. */
|
||||
#define BROTLI_TRUE 1
|
||||
/** Portable @c false replacement. */
|
||||
#define BROTLI_FALSE 0
|
||||
/** @c bool to ::BROTLI_BOOL conversion macros. */
|
||||
#define TO_BROTLI_BOOL(X) (!!(X) ? BROTLI_TRUE : BROTLI_FALSE)
|
||||
|
||||
#define BROTLI_MAKE_UINT64_T(high, low) ((((uint64_t)(high)) << 32) | low)
|
||||
|
||||
#define BROTLI_UINT32_MAX (~((uint32_t)0))
|
||||
#define BROTLI_SIZE_MAX (~((size_t)0))
|
||||
|
||||
/**
|
||||
* Allocating function pointer type.
|
||||
*
|
||||
* @param opaque custom memory manager handle provided by client
|
||||
* @param size requested memory region size; can not be @c 0
|
||||
* @returns @c 0 in the case of failure
|
||||
* @returns a valid pointer to a memory region of at least @p size bytes
|
||||
* long otherwise
|
||||
*/
|
||||
typedef void* (*brotli_alloc_func)(void* opaque, size_t size);
|
||||
|
||||
/**
|
||||
* Deallocating function pointer type.
|
||||
*
|
||||
* This function @b SHOULD do nothing if @p address is @c 0.
|
||||
*
|
||||
* @param opaque custom memory manager handle provided by client
|
||||
* @param address memory region pointer returned by ::brotli_alloc_func, or @c 0
|
||||
*/
|
||||
typedef void (*brotli_free_func)(void* opaque, void* address);
|
||||
|
||||
#endif /* BROTLI_COMMON_TYPES_H_ */
|
||||
298
common/BufferPool.cpp
Normal file
298
common/BufferPool.cpp
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "BufferPool.h"
|
||||
#include "FuncHelper.h"
|
||||
|
||||
const DWORD TItem::DEFAULT_ITEM_CAPACITY = DEFAULT_BUFFER_CACHE_CAPACITY;
|
||||
const DWORD CBufferPool::DEFAULT_MAX_CACHE_SIZE = 0;
|
||||
const DWORD CBufferPool::DEFAULT_ITEM_CAPACITY = CItemPool::DEFAULT_ITEM_CAPACITY;
|
||||
const DWORD CBufferPool::DEFAULT_ITEM_POOL_SIZE = CItemPool::DEFAULT_POOL_SIZE;
|
||||
const DWORD CBufferPool::DEFAULT_ITEM_POOL_HOLD = CItemPool::DEFAULT_POOL_HOLD;
|
||||
const DWORD CBufferPool::DEFAULT_BUFFER_LOCK_TIME = DEFAULT_OBJECT_CACHE_LOCK_TIME;
|
||||
const DWORD CBufferPool::DEFAULT_BUFFER_POOL_SIZE = DEFAULT_OBJECT_CACHE_POOL_SIZE;
|
||||
const DWORD CBufferPool::DEFAULT_BUFFER_POOL_HOLD = DEFAULT_OBJECT_CACHE_POOL_HOLD;
|
||||
|
||||
int TItem::Cat(const BYTE* pData, int length)
|
||||
{
|
||||
ASSERT(pData != nullptr && length >= 0);
|
||||
|
||||
int cat = MIN(Remain(), length);
|
||||
|
||||
if(cat > 0)
|
||||
{
|
||||
memcpy(end, pData, cat);
|
||||
end += cat;
|
||||
}
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
int TItem::Cat(const TItem& other)
|
||||
{
|
||||
ASSERT(this != &other);
|
||||
return Cat(other.Ptr(), other.Size());
|
||||
}
|
||||
|
||||
int TItem::Fetch(BYTE* pData, int length)
|
||||
{
|
||||
ASSERT(pData != nullptr && length > 0);
|
||||
|
||||
int fetch = MIN(Size(), length);
|
||||
memcpy(pData, begin, fetch);
|
||||
begin += fetch;
|
||||
|
||||
return fetch;
|
||||
}
|
||||
|
||||
int TItem::Peek(BYTE* pData, int length)
|
||||
{
|
||||
ASSERT(pData != nullptr && length > 0);
|
||||
|
||||
int peek = MIN(Size(), length);
|
||||
memcpy(pData, begin, peek);
|
||||
|
||||
return peek;
|
||||
}
|
||||
|
||||
int TItem::Increase(int length)
|
||||
{
|
||||
ASSERT(length >= 0);
|
||||
|
||||
int increase = MIN(Remain(), length);
|
||||
end += increase;
|
||||
|
||||
return increase;
|
||||
}
|
||||
|
||||
int TItem::Reduce(int length)
|
||||
{
|
||||
ASSERT(length >= 0);
|
||||
|
||||
int reduce = MIN(Size(), length);
|
||||
begin += reduce;
|
||||
|
||||
return reduce;
|
||||
}
|
||||
|
||||
void TItem::Reset(int first, int last)
|
||||
{
|
||||
ASSERT(first >= -1 && first <= capacity);
|
||||
ASSERT(last >= -1 && last <= capacity);
|
||||
|
||||
if(first >= 0) begin = head + MIN(first, capacity);
|
||||
if(last >= 0) end = head + MIN(last, capacity);
|
||||
}
|
||||
|
||||
TBuffer* TBuffer::Construct(CBufferPool& pool, ULONG_PTR dwID)
|
||||
{
|
||||
ASSERT(dwID != 0);
|
||||
|
||||
CPrivateHeap& heap = pool.GetPrivateHeap();
|
||||
TBuffer* pBuffer = (TBuffer*)heap.Alloc(sizeof(TBuffer));
|
||||
|
||||
return ::ConstructObject(pBuffer, heap, pool.GetItemPool(), dwID);
|
||||
}
|
||||
|
||||
void TBuffer::Destruct(TBuffer* pBuffer)
|
||||
{
|
||||
ASSERT(pBuffer != nullptr);
|
||||
|
||||
CPrivateHeap& heap = pBuffer->heap;
|
||||
::DestructObject(pBuffer);
|
||||
heap.Free(pBuffer);
|
||||
}
|
||||
|
||||
void TBuffer::Reset()
|
||||
{
|
||||
id = 0;
|
||||
length = 0;
|
||||
freeTime = ::TimeGetTime();
|
||||
}
|
||||
|
||||
int TBuffer::Cat(const BYTE* pData, int len)
|
||||
{
|
||||
items.Cat(pData, len);
|
||||
return IncreaseLength(len);
|
||||
}
|
||||
|
||||
int TBuffer::Cat(const TItem* pItem)
|
||||
{
|
||||
items.Cat(pItem);
|
||||
return IncreaseLength(pItem->Size());
|
||||
}
|
||||
|
||||
int TBuffer::Cat(const TItemList& other)
|
||||
{
|
||||
ASSERT(&items != &other);
|
||||
|
||||
for(TItem* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
|
||||
Cat(pItem);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int TBuffer::Fetch(BYTE* pData, int len)
|
||||
{
|
||||
int fetch = items.Fetch(pData, len);
|
||||
DecreaseLength(fetch);
|
||||
|
||||
return fetch;
|
||||
}
|
||||
|
||||
int TBuffer::Peek(BYTE* pData, int len)
|
||||
{
|
||||
return items.Peek(pData, len);
|
||||
}
|
||||
|
||||
int TBuffer::Reduce(int len)
|
||||
{
|
||||
int reduce = items.Reduce(len);
|
||||
DecreaseLength(reduce);
|
||||
|
||||
return reduce;
|
||||
}
|
||||
|
||||
void CBufferPool::PutFreeBuffer(ULONG_PTR dwID)
|
||||
{
|
||||
ASSERT(dwID != 0);
|
||||
|
||||
TBuffer* pBuffer = FindCacheBuffer(dwID);
|
||||
|
||||
if(pBuffer != nullptr)
|
||||
PutFreeBuffer(pBuffer);
|
||||
}
|
||||
|
||||
void CBufferPool::PutFreeBuffer(TBuffer* pBuffer)
|
||||
{
|
||||
ASSERT(pBuffer != nullptr);
|
||||
|
||||
if(!pBuffer->IsValid())
|
||||
return;
|
||||
|
||||
m_bfCache.RemoveEx(pBuffer->ID());
|
||||
|
||||
BOOL bOK = FALSE;
|
||||
|
||||
{
|
||||
CCriSecLock locallock(pBuffer->cs);
|
||||
|
||||
if(pBuffer->IsValid())
|
||||
{
|
||||
pBuffer->Reset();
|
||||
bOK = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(bOK)
|
||||
{
|
||||
m_itPool.PutFreeItem(pBuffer->items);
|
||||
|
||||
#ifndef USE_EXTERNAL_GC
|
||||
ReleaseGCBuffer();
|
||||
#endif
|
||||
if(!m_lsFreeBuffer.TryPut(pBuffer))
|
||||
m_lsGCBuffer.PushBack(pBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void CBufferPool::ReleaseGCBuffer(BOOL bForce)
|
||||
{
|
||||
::ReleaseGCObj(m_lsGCBuffer, m_dwBufferLockTime, bForce);
|
||||
}
|
||||
|
||||
TBuffer* CBufferPool::PutCacheBuffer(ULONG_PTR dwID)
|
||||
{
|
||||
ASSERT(dwID != 0);
|
||||
|
||||
TBuffer* pBuffer = PickFreeBuffer(dwID);
|
||||
m_bfCache.SetEx(dwID, pBuffer);
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
TBuffer* CBufferPool::PickFreeBuffer(ULONG_PTR dwID)
|
||||
{
|
||||
ASSERT( dwID != 0);
|
||||
|
||||
DWORD dwIndex;
|
||||
TBuffer* pBuffer = nullptr;
|
||||
|
||||
if(m_lsFreeBuffer.TryLock(&pBuffer, dwIndex))
|
||||
{
|
||||
if(::GetTimeGap32(pBuffer->freeTime) >= m_dwBufferLockTime)
|
||||
VERIFY(m_lsFreeBuffer.ReleaseLock(nullptr, dwIndex));
|
||||
else
|
||||
{
|
||||
VERIFY(m_lsFreeBuffer.ReleaseLock(pBuffer, dwIndex));
|
||||
pBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(pBuffer) pBuffer->id = dwID;
|
||||
else pBuffer = TBuffer::Construct(*this, dwID);
|
||||
|
||||
ASSERT(pBuffer);
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
TBuffer* CBufferPool::FindCacheBuffer(ULONG_PTR dwID)
|
||||
{
|
||||
ASSERT(dwID != 0);
|
||||
|
||||
TBuffer* pBuffer = nullptr;
|
||||
|
||||
if(m_bfCache.GetEx(dwID, &pBuffer) != TBufferCache::GR_VALID)
|
||||
pBuffer = nullptr;
|
||||
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
void CBufferPool::Prepare()
|
||||
{
|
||||
m_itPool.Prepare();
|
||||
|
||||
m_bfCache.Reset(m_dwMaxCacheSize);
|
||||
m_lsFreeBuffer.Reset(m_dwBufferPoolSize);
|
||||
}
|
||||
|
||||
void CBufferPool::Clear()
|
||||
{
|
||||
TBufferCache::IndexSet& indexes = m_bfCache.Indexes();
|
||||
|
||||
for(auto it = indexes.begin(), end = indexes.end(); it != end; ++it)
|
||||
{
|
||||
TBuffer* pBuffer = FindCacheBuffer(*it);
|
||||
if(pBuffer) TBuffer::Destruct(pBuffer);
|
||||
}
|
||||
|
||||
m_bfCache.Reset();
|
||||
|
||||
m_lsFreeBuffer.Clear();
|
||||
|
||||
ReleaseGCBuffer(TRUE);
|
||||
VERIFY(m_lsGCBuffer.IsEmpty());
|
||||
|
||||
m_itPool.Clear();
|
||||
m_heap.Reset();
|
||||
}
|
||||
869
common/BufferPool.h
Normal file
869
common/BufferPool.h
Normal file
@@ -0,0 +1,869 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Singleton.h"
|
||||
#include "STLHelper.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "PrivateHeap.h"
|
||||
#include "CriSec.h"
|
||||
|
||||
template<class T> T* ConstructItemT(T*, CPrivateHeap& heap, int capacity, BYTE* pData, int length)
|
||||
{
|
||||
ASSERT(capacity > 0);
|
||||
|
||||
int item_size = sizeof(T);
|
||||
T* pItem = (T*)heap.Alloc(item_size + capacity);
|
||||
BYTE* pHead = (BYTE*)pItem + item_size;
|
||||
|
||||
return ::ConstructObject(pItem, heap, pHead, capacity, pData, length);
|
||||
}
|
||||
|
||||
template<class T> void DestructItemT(T* pItem)
|
||||
{
|
||||
ASSERT(pItem != nullptr);
|
||||
|
||||
CPrivateHeap& heap = pItem->GetPrivateHeap();
|
||||
|
||||
::DestructObject(pItem);
|
||||
heap.Free(pItem);
|
||||
}
|
||||
|
||||
struct TItem
|
||||
{
|
||||
template<typename T> friend struct TSimpleList;
|
||||
template<typename T> friend class CNodePoolT;
|
||||
template<typename T> friend struct TItemListT;
|
||||
|
||||
friend struct TBuffer;
|
||||
|
||||
public:
|
||||
int Cat (const BYTE* pData, int length);
|
||||
int Cat (const TItem& other);
|
||||
int Fetch (BYTE* pData, int length);
|
||||
int Peek (BYTE* pData, int length);
|
||||
int Increase(int length);
|
||||
int Reduce (int length);
|
||||
void Reset (int first = 0, int last = 0);
|
||||
|
||||
BYTE* Ptr () {return begin;}
|
||||
const BYTE* Ptr () const {return begin;}
|
||||
int Size () const {return (int)(end - begin);}
|
||||
int Remain () const {return capacity - (int)(end - head);}
|
||||
int Capacity() const {return capacity;}
|
||||
bool IsEmpty () const {return Size() == 0;}
|
||||
bool IsFull () const {return Remain() == 0;}
|
||||
CPrivateHeap& GetPrivateHeap() {return heap;}
|
||||
|
||||
operator BYTE* () {return Ptr();}
|
||||
operator const BYTE* () const {return Ptr();}
|
||||
|
||||
public:
|
||||
static TItem* Construct(CPrivateHeap& heap,
|
||||
int capacity = DEFAULT_ITEM_CAPACITY,
|
||||
BYTE* pData = nullptr,
|
||||
int length = 0)
|
||||
{
|
||||
return ::ConstructItemT((TItem*)(nullptr), heap, capacity, pData, length);
|
||||
}
|
||||
|
||||
static void Destruct(TItem* pItem)
|
||||
{
|
||||
::DestructItemT(pItem);
|
||||
}
|
||||
|
||||
TItem(CPrivateHeap& hp, BYTE* pHead, int cap = DEFAULT_ITEM_CAPACITY, BYTE* pData = nullptr, int length = 0)
|
||||
: heap(hp), head(pHead), begin(pHead), end(pHead), capacity(cap), next(nullptr), last(nullptr)
|
||||
{
|
||||
if(pData != nullptr && length != 0)
|
||||
Cat(pData, length);
|
||||
}
|
||||
|
||||
~TItem() {}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TItem)
|
||||
|
||||
public:
|
||||
static const DWORD DEFAULT_ITEM_CAPACITY;
|
||||
|
||||
private:
|
||||
CPrivateHeap& heap;
|
||||
|
||||
private:
|
||||
TItem* next;
|
||||
TItem* last;
|
||||
|
||||
int capacity;
|
||||
BYTE* head;
|
||||
BYTE* begin;
|
||||
BYTE* end;
|
||||
};
|
||||
|
||||
template<class T> struct TSimpleList
|
||||
{
|
||||
public:
|
||||
T* PushFront(T* pItem)
|
||||
{
|
||||
if(pFront != nullptr)
|
||||
{
|
||||
pFront->last = pItem;
|
||||
pItem->next = pFront;
|
||||
}
|
||||
else
|
||||
{
|
||||
pItem->last = nullptr;
|
||||
pItem->next = nullptr;
|
||||
pBack = pItem;
|
||||
}
|
||||
|
||||
pFront = pItem;
|
||||
++size;
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
T* PushBack(T* pItem)
|
||||
{
|
||||
if(pBack != nullptr)
|
||||
{
|
||||
pBack->next = pItem;
|
||||
pItem->last = pBack;
|
||||
}
|
||||
else
|
||||
{
|
||||
pItem->last = nullptr;
|
||||
pItem->next = nullptr;
|
||||
pFront = pItem;
|
||||
}
|
||||
|
||||
pBack = pItem;
|
||||
++size;
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
T* PopFront()
|
||||
{
|
||||
T* pItem = pFront;
|
||||
|
||||
if(pFront != pBack)
|
||||
{
|
||||
pFront = (T*)pFront->next;
|
||||
pFront->last = nullptr;
|
||||
}
|
||||
else if(pFront != nullptr)
|
||||
{
|
||||
pFront = nullptr;
|
||||
pBack = nullptr;
|
||||
}
|
||||
|
||||
if(pItem != nullptr)
|
||||
{
|
||||
pItem->next = nullptr;
|
||||
pItem->last = nullptr;
|
||||
|
||||
--size;
|
||||
}
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
T* PopBack()
|
||||
{
|
||||
T* pItem = pBack;
|
||||
|
||||
if(pFront != pBack)
|
||||
{
|
||||
pBack = (T*)pBack->last;
|
||||
pBack->next = nullptr;
|
||||
}
|
||||
else if(pBack != nullptr)
|
||||
{
|
||||
pFront = nullptr;
|
||||
pBack = nullptr;
|
||||
}
|
||||
|
||||
if(pItem != nullptr)
|
||||
{
|
||||
pItem->next = nullptr;
|
||||
pItem->last = nullptr;
|
||||
|
||||
--size;
|
||||
}
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
TSimpleList<T>& Shift(TSimpleList<T>& other)
|
||||
{
|
||||
if(&other != this && other.size > 0)
|
||||
{
|
||||
if(size > 0)
|
||||
{
|
||||
pBack->next = other.pFront;
|
||||
other.pFront->last = pBack;
|
||||
}
|
||||
else
|
||||
{
|
||||
pFront = other.pFront;
|
||||
}
|
||||
|
||||
pBack = other.pBack;
|
||||
size += other.size;
|
||||
|
||||
other.Reset();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
if(size > 0)
|
||||
{
|
||||
T* pItem;
|
||||
while((pItem = PopFront()) != nullptr)
|
||||
T::Destruct(pItem);
|
||||
}
|
||||
}
|
||||
|
||||
T* Front () const {return pFront;}
|
||||
T* Back () const {return pBack;}
|
||||
int Size () const {return size;}
|
||||
bool IsEmpty () const {return size == 0;}
|
||||
|
||||
public:
|
||||
TSimpleList() {Reset();}
|
||||
~TSimpleList() {Clear();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TSimpleList<T>)
|
||||
|
||||
private:
|
||||
void Reset()
|
||||
{
|
||||
pFront = nullptr;
|
||||
pBack = nullptr;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int size;
|
||||
T* pFront;
|
||||
T* pBack;
|
||||
};
|
||||
|
||||
template<class T> class CNodePoolT
|
||||
{
|
||||
public:
|
||||
void PutFreeItem(T* pItem)
|
||||
{
|
||||
ASSERT(pItem != nullptr);
|
||||
|
||||
if(!m_lsFreeItem.TryPut(pItem))
|
||||
T::Destruct(pItem);
|
||||
}
|
||||
|
||||
void PutFreeItem(TSimpleList<T>& lsItem)
|
||||
{
|
||||
if(lsItem.IsEmpty())
|
||||
return;
|
||||
|
||||
T* pItem;
|
||||
while((pItem = lsItem.PopFront()) != nullptr)
|
||||
PutFreeItem(pItem);
|
||||
}
|
||||
|
||||
T* PickFreeItem()
|
||||
{
|
||||
T* pItem = nullptr;
|
||||
|
||||
if(!m_lsFreeItem.TryGet(&pItem))
|
||||
pItem = T::Construct(m_heap, m_dwItemCapacity);
|
||||
|
||||
ASSERT(pItem);
|
||||
pItem->Reset();
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
void Prepare()
|
||||
{
|
||||
m_lsFreeItem.Reset(m_dwPoolSize);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
m_lsFreeItem.Clear();
|
||||
|
||||
m_heap.Reset();
|
||||
}
|
||||
|
||||
public:
|
||||
void SetItemCapacity(DWORD dwItemCapacity) {m_dwItemCapacity = dwItemCapacity;}
|
||||
void SetPoolSize (DWORD dwPoolSize) {m_dwPoolSize = dwPoolSize;}
|
||||
void SetPoolHold (DWORD dwPoolHold) {m_dwPoolHold = dwPoolHold;}
|
||||
DWORD GetItemCapacity () {return m_dwItemCapacity;}
|
||||
DWORD GetPoolSize () {return m_dwPoolSize;}
|
||||
DWORD GetPoolHold () {return m_dwPoolHold;}
|
||||
|
||||
CPrivateHeap& GetPrivateHeap() {return m_heap;}
|
||||
|
||||
public:
|
||||
CNodePoolT( DWORD dwPoolSize = DEFAULT_POOL_SIZE,
|
||||
DWORD dwPoolHold = DEFAULT_POOL_HOLD,
|
||||
DWORD dwItemCapacity = DEFAULT_ITEM_CAPACITY)
|
||||
: m_dwPoolSize(dwPoolSize)
|
||||
, m_dwPoolHold(dwPoolHold)
|
||||
, m_dwItemCapacity(dwItemCapacity)
|
||||
{
|
||||
}
|
||||
|
||||
~CNodePoolT() {Clear();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CNodePoolT)
|
||||
|
||||
public:
|
||||
static const DWORD DEFAULT_ITEM_CAPACITY;
|
||||
static const DWORD DEFAULT_POOL_SIZE;
|
||||
static const DWORD DEFAULT_POOL_HOLD;
|
||||
|
||||
private:
|
||||
CPrivateHeap m_heap;
|
||||
|
||||
DWORD m_dwItemCapacity;
|
||||
DWORD m_dwPoolSize;
|
||||
DWORD m_dwPoolHold;
|
||||
|
||||
CRingPool<T> m_lsFreeItem;
|
||||
};
|
||||
|
||||
template<class T> const DWORD CNodePoolT<T>::DEFAULT_ITEM_CAPACITY = TItem::DEFAULT_ITEM_CAPACITY;
|
||||
template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_SIZE = DEFAULT_BUFFER_CACHE_POOL_SIZE;
|
||||
template<class T> const DWORD CNodePoolT<T>::DEFAULT_POOL_HOLD = DEFAULT_BUFFER_CACHE_POOL_HOLD;
|
||||
|
||||
using CItemPool = CNodePoolT<TItem>;
|
||||
|
||||
template<class T> struct TItemListT : public TSimpleList<T>
|
||||
{
|
||||
using __super = TSimpleList<T>;
|
||||
|
||||
public:
|
||||
int PushTail(const BYTE* pData, int length)
|
||||
{
|
||||
ASSERT(length <= (int)itPool.GetItemCapacity());
|
||||
|
||||
if(length > (int)itPool.GetItemCapacity())
|
||||
return 0;
|
||||
|
||||
T* pItem = __super::PushBack(itPool.PickFreeItem());
|
||||
return pItem->Cat(pData, length);
|
||||
}
|
||||
|
||||
int Cat(const BYTE* pData, int length)
|
||||
{
|
||||
int remain = length;
|
||||
|
||||
while(remain > 0)
|
||||
{
|
||||
T* pItem = __super::Back();
|
||||
|
||||
if(pItem == nullptr || pItem->IsFull())
|
||||
pItem = __super::PushBack(itPool.PickFreeItem());
|
||||
|
||||
int cat = pItem->Cat(pData, remain);
|
||||
|
||||
pData += cat;
|
||||
remain -= cat;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int Cat(const T* pItem)
|
||||
{
|
||||
return Cat(pItem->Ptr(), pItem->Size());
|
||||
}
|
||||
|
||||
int Cat(const TItemListT<T>& other)
|
||||
{
|
||||
ASSERT(this != &other);
|
||||
|
||||
int length = 0;
|
||||
|
||||
for(T* pItem = other.Front(); pItem != nullptr; pItem = pItem->next)
|
||||
length += Cat(pItem);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int Fetch(BYTE* pData, int length)
|
||||
{
|
||||
int remain = length;
|
||||
|
||||
while(remain > 0 && __super::Size() > 0)
|
||||
{
|
||||
T* pItem = __super::Front();
|
||||
int fetch = pItem->Fetch(pData, remain);
|
||||
|
||||
pData += fetch;
|
||||
remain -= fetch;
|
||||
|
||||
if(pItem->IsEmpty())
|
||||
itPool.PutFreeItem(__super::PopFront());
|
||||
}
|
||||
|
||||
return length - remain;
|
||||
}
|
||||
|
||||
int Peek(BYTE* pData, int length)
|
||||
{
|
||||
int remain = length;
|
||||
T* pItem = __super::Front();
|
||||
|
||||
while(remain > 0 && pItem != nullptr)
|
||||
{
|
||||
int peek = pItem->Peek(pData, remain);
|
||||
|
||||
pData += peek;
|
||||
remain -= peek;
|
||||
pItem = pItem->next;
|
||||
}
|
||||
|
||||
return length - remain;
|
||||
}
|
||||
|
||||
int Increase(int length)
|
||||
{
|
||||
int remain = length;
|
||||
|
||||
while(remain > 0)
|
||||
{
|
||||
T* pItem = __super::Back();
|
||||
|
||||
if(pItem == nullptr || pItem->IsFull())
|
||||
{
|
||||
pItem = itPool.PickFreeItem();
|
||||
__super::PushBack(pItem);
|
||||
}
|
||||
|
||||
remain -= pItem->Increase(remain);
|
||||
}
|
||||
|
||||
return length - remain;
|
||||
}
|
||||
|
||||
int Reduce(int length)
|
||||
{
|
||||
int remain = length;
|
||||
|
||||
while(remain > 0 && __super::Size() > 0)
|
||||
{
|
||||
T* pItem = __super::Front();
|
||||
remain -= pItem->Reduce(remain);
|
||||
|
||||
if(pItem->IsEmpty())
|
||||
itPool.PutFreeItem(__super::PopFront());
|
||||
}
|
||||
|
||||
return length - remain;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
itPool.PutFreeItem(*this);
|
||||
}
|
||||
|
||||
CNodePoolT<T>& GetItemPool() {return itPool;}
|
||||
|
||||
public:
|
||||
TItemListT(CNodePoolT<T>& pool) : itPool(pool)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
CNodePoolT<T>& itPool;
|
||||
};
|
||||
|
||||
using TItemList = TItemListT<TItem>;
|
||||
|
||||
template<class T, class length_t = int, typename = enable_if_t<is_integral<typename decay<length_t>::type>::value>>
|
||||
struct TItemListExT : public TItemListT<T>
|
||||
{
|
||||
using __super = TItemListT<T>;
|
||||
|
||||
public:
|
||||
T* PushFront(T* pItem)
|
||||
{
|
||||
length += pItem->Size();
|
||||
return __super::PushFront(pItem);
|
||||
}
|
||||
|
||||
T* PushBack(T* pItem)
|
||||
{
|
||||
length += pItem->Size();
|
||||
return __super::PushBack(pItem);
|
||||
}
|
||||
|
||||
T* PopFront()
|
||||
{
|
||||
T* pItem = __super::PopFront();
|
||||
|
||||
if(pItem != nullptr)
|
||||
length -= pItem->Size();
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
T* PopBack()
|
||||
{
|
||||
T* pItem = __super::PopBack();
|
||||
|
||||
if(pItem != nullptr)
|
||||
length -= pItem->Size();
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
TItemListExT& Shift(TItemListExT<T>& other)
|
||||
{
|
||||
if(&other != this && other.length > 0)
|
||||
{
|
||||
length += other.length;
|
||||
other.length = 0;
|
||||
|
||||
__super::Shift(other);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
__super::Clear();
|
||||
length = 0;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
__super::Release();
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public:
|
||||
int PushTail(const BYTE* pData, int length)
|
||||
{
|
||||
int cat = __super::PushTail(pData, length);
|
||||
this->length += cat;
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
int Cat(const BYTE* pData, int length)
|
||||
{
|
||||
int cat = __super::Cat(pData, length);
|
||||
this->length += cat;
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
int Cat(const T* pItem)
|
||||
{
|
||||
int cat = __super::Cat(pItem->Ptr(), pItem->Size());
|
||||
this->length += cat;
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
int Cat(const TItemListT<T>& other)
|
||||
{
|
||||
int cat = __super::Cat(other);
|
||||
this->length += cat;
|
||||
|
||||
return cat;
|
||||
}
|
||||
|
||||
int Fetch(BYTE* pData, int length)
|
||||
{
|
||||
int fetch = __super::Fetch(pData, length);
|
||||
this->length -= fetch;
|
||||
|
||||
return fetch;
|
||||
}
|
||||
|
||||
int Increase(int length)
|
||||
{
|
||||
int increase = __super::Increase(length);
|
||||
this->length += increase;
|
||||
|
||||
return increase;
|
||||
}
|
||||
|
||||
int Reduce(int length)
|
||||
{
|
||||
int reduce = __super::Reduce(length);
|
||||
this->length -= reduce;
|
||||
|
||||
return reduce;
|
||||
}
|
||||
|
||||
typename decay<length_t>::type Length() const {return length;}
|
||||
|
||||
int IncreaseLength (int length) {return (this->length += length);}
|
||||
int ReduceLength (int length) {return (this->length -= length);}
|
||||
|
||||
public:
|
||||
TItemListExT(CNodePoolT<T>& pool) : TItemListT<T>(pool), length(0)
|
||||
{
|
||||
}
|
||||
|
||||
~TItemListExT()
|
||||
{
|
||||
ASSERT(length >= 0);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TItemListExT)
|
||||
|
||||
private:
|
||||
length_t length;
|
||||
};
|
||||
|
||||
using TItemListEx = TItemListExT<TItem>;
|
||||
using TItemListExV = TItemListExT<TItem, volatile int>;
|
||||
|
||||
template<class T> struct TItemPtrT
|
||||
{
|
||||
public:
|
||||
T* Reset(T* pItem = nullptr)
|
||||
{
|
||||
if(m_pItem != nullptr)
|
||||
itPool.PutFreeItem(m_pItem);
|
||||
|
||||
m_pItem = pItem;
|
||||
|
||||
return m_pItem;
|
||||
}
|
||||
|
||||
T* Attach(T* pItem)
|
||||
{
|
||||
return Reset(pItem);
|
||||
}
|
||||
|
||||
T* Detach()
|
||||
{
|
||||
T* pItem = m_pItem;
|
||||
m_pItem = nullptr;
|
||||
|
||||
return pItem;
|
||||
}
|
||||
|
||||
T* New()
|
||||
{
|
||||
return Attach(itPool.PickFreeItem());
|
||||
}
|
||||
|
||||
bool IsValid () {return m_pItem != nullptr;}
|
||||
T* operator -> () {return m_pItem;}
|
||||
T* operator = (T* pItem) {return Reset(pItem);}
|
||||
operator T* () {return m_pItem;}
|
||||
T*& PtrRef () {return m_pItem;}
|
||||
T* Ptr () {return m_pItem;}
|
||||
const T* Ptr () const {return m_pItem;}
|
||||
operator const T* () const {return m_pItem;}
|
||||
|
||||
public:
|
||||
TItemPtrT(CNodePoolT<T>& pool, T* pItem = nullptr)
|
||||
: itPool(pool), m_pItem(pItem)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
TItemPtrT(TItemListT<T>& ls, T* pItem = nullptr)
|
||||
: itPool(ls.GetItemPool()), m_pItem(pItem)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~TItemPtrT()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TItemPtrT)
|
||||
|
||||
private:
|
||||
CNodePoolT<T>& itPool;
|
||||
T* m_pItem;
|
||||
};
|
||||
|
||||
using TItemPtr = TItemPtrT<TItem>;
|
||||
|
||||
class CBufferPool;
|
||||
|
||||
struct TBuffer
|
||||
{
|
||||
template<typename T> friend struct TSimpleList;
|
||||
friend class CBufferPool;
|
||||
|
||||
public:
|
||||
static TBuffer* Construct(CBufferPool& pool, ULONG_PTR dwID);
|
||||
static void Destruct(TBuffer* pBuffer);
|
||||
|
||||
public:
|
||||
int Cat (const BYTE* pData, int len);
|
||||
int Cat (const TItem* pItem);
|
||||
int Cat (const TItemList& other);
|
||||
int Fetch (BYTE* pData, int length);
|
||||
int Peek (BYTE* pData, int length);
|
||||
int Reduce (int len);
|
||||
|
||||
public:
|
||||
CCriSec& CriSec () {return cs;}
|
||||
TItemList& ItemList() {return items;}
|
||||
|
||||
ULONG_PTR ID () const {return id;}
|
||||
int Length () const {return length;}
|
||||
bool IsValid () const {return id != 0;}
|
||||
|
||||
DWORD GetFreeTime () const {return freeTime;}
|
||||
int GetCount () const {return 0;}
|
||||
|
||||
private:
|
||||
int IncreaseLength (int len) {return (length += len);}
|
||||
int DecreaseLength (int len) {return (length -= len);}
|
||||
|
||||
void Reset ();
|
||||
|
||||
private:
|
||||
friend TBuffer* ConstructObject<>(TBuffer*, CPrivateHeap&, CItemPool&, ULONG_PTR&);
|
||||
friend void DestructObject<>(TBuffer*);
|
||||
|
||||
TBuffer(CPrivateHeap& hp, CItemPool& itPool, ULONG_PTR dwID = 0)
|
||||
: heap(hp), items(itPool), id(dwID), length(0)
|
||||
{
|
||||
}
|
||||
|
||||
~TBuffer() {}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TBuffer)
|
||||
|
||||
private:
|
||||
CPrivateHeap& heap;
|
||||
|
||||
private:
|
||||
ULONG_PTR id;
|
||||
int length;
|
||||
DWORD freeTime;
|
||||
|
||||
private:
|
||||
TBuffer* next;
|
||||
TBuffer* last;
|
||||
|
||||
CCriSec cs;
|
||||
TItemList items;
|
||||
};
|
||||
|
||||
class CBufferPool
|
||||
{
|
||||
using TBufferList = CRingPool<TBuffer>;
|
||||
using TBufferQueue = CCASQueue<TBuffer>;
|
||||
using TBufferCache = CRingCache<TBuffer, ULONG_PTR, true>;
|
||||
|
||||
public:
|
||||
void PutFreeBuffer (ULONG_PTR dwID);
|
||||
TBuffer* PutCacheBuffer (ULONG_PTR dwID);
|
||||
TBuffer* FindCacheBuffer (ULONG_PTR dwID);
|
||||
TBuffer* PickFreeBuffer (ULONG_PTR dwID);
|
||||
void PutFreeBuffer (TBuffer* pBuffer);
|
||||
|
||||
void Prepare ();
|
||||
void Clear ();
|
||||
|
||||
void ReleaseGCBuffer (BOOL bForce = FALSE);
|
||||
|
||||
public:
|
||||
void SetItemCapacity (DWORD dwItemCapacity) {m_itPool.SetItemCapacity(dwItemCapacity);}
|
||||
void SetItemPoolSize (DWORD dwItemPoolSize) {m_itPool.SetPoolSize(dwItemPoolSize);}
|
||||
void SetItemPoolHold (DWORD dwItemPoolHold) {m_itPool.SetPoolHold(dwItemPoolHold);}
|
||||
|
||||
void SetMaxCacheSize (DWORD dwMaxCacheSize) {m_dwMaxCacheSize = dwMaxCacheSize;}
|
||||
void SetBufferLockTime (DWORD dwBufferLockTime) {m_dwBufferLockTime = dwBufferLockTime;}
|
||||
void SetBufferPoolSize (DWORD dwBufferPoolSize) {m_dwBufferPoolSize = dwBufferPoolSize;}
|
||||
void SetBufferPoolHold (DWORD dwBufferPoolHold) {m_dwBufferPoolHold = dwBufferPoolHold;}
|
||||
|
||||
DWORD GetItemCapacity () {return m_itPool.GetItemCapacity();}
|
||||
DWORD GetItemPoolSize () {return m_itPool.GetPoolSize();}
|
||||
DWORD GetItemPoolHold () {return m_itPool.GetPoolHold();}
|
||||
|
||||
DWORD GetMaxCacheSize () {return m_dwMaxCacheSize;}
|
||||
DWORD GetBufferLockTime () {return m_dwBufferLockTime;}
|
||||
DWORD GetBufferPoolSize () {return m_dwBufferPoolSize;}
|
||||
DWORD GetBufferPoolHold () {return m_dwBufferPoolHold;}
|
||||
|
||||
TBuffer* operator [] (ULONG_PTR dwID) {return FindCacheBuffer(dwID);}
|
||||
|
||||
public:
|
||||
CBufferPool(DWORD dwPoolSize = DEFAULT_BUFFER_POOL_SIZE,
|
||||
DWORD dwPoolHold = DEFAULT_BUFFER_POOL_HOLD,
|
||||
DWORD dwLockTime = DEFAULT_BUFFER_LOCK_TIME,
|
||||
DWORD dwMaxCacheSize = DEFAULT_MAX_CACHE_SIZE)
|
||||
: m_dwBufferPoolSize(dwPoolSize)
|
||||
, m_dwBufferPoolHold(dwPoolHold)
|
||||
, m_dwBufferLockTime(dwLockTime)
|
||||
, m_dwMaxCacheSize(dwMaxCacheSize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CBufferPool() {Clear();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CBufferPool)
|
||||
|
||||
public:
|
||||
CPrivateHeap& GetPrivateHeap() {return m_heap;}
|
||||
CItemPool& GetItemPool() {return m_itPool;}
|
||||
|
||||
public:
|
||||
static const DWORD DEFAULT_MAX_CACHE_SIZE;
|
||||
static const DWORD DEFAULT_ITEM_CAPACITY;
|
||||
static const DWORD DEFAULT_ITEM_POOL_SIZE;
|
||||
static const DWORD DEFAULT_ITEM_POOL_HOLD;
|
||||
static const DWORD DEFAULT_BUFFER_LOCK_TIME;
|
||||
static const DWORD DEFAULT_BUFFER_POOL_SIZE;
|
||||
static const DWORD DEFAULT_BUFFER_POOL_HOLD;
|
||||
|
||||
private:
|
||||
DWORD m_dwMaxCacheSize;
|
||||
DWORD m_dwBufferLockTime;
|
||||
DWORD m_dwBufferPoolSize;
|
||||
DWORD m_dwBufferPoolHold;
|
||||
|
||||
CPrivateHeap m_heap;
|
||||
CItemPool m_itPool;
|
||||
|
||||
TBufferCache m_bfCache;
|
||||
|
||||
TBufferList m_lsFreeBuffer;
|
||||
TBufferQueue m_lsGCBuffer;
|
||||
};
|
||||
198
common/BufferPtr.h
Normal file
198
common/BufferPtr.h
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
|
||||
#include <memory.h>
|
||||
#include <malloc.h>
|
||||
|
||||
template<class T, size_t MAX_CACHE_SIZE = 0>
|
||||
class CBufferPtrT
|
||||
{
|
||||
public:
|
||||
explicit CBufferPtrT(size_t size = 0, bool zero = false) {Reset(); Malloc(size, zero);}
|
||||
explicit CBufferPtrT(const T* pch, size_t size) {Reset(); Copy(pch, size);}
|
||||
CBufferPtrT(const CBufferPtrT& other) {Reset(); Copy(other);}
|
||||
template<size_t S> CBufferPtrT(const CBufferPtrT<T, S>& other) {Reset(); Copy(other);}
|
||||
|
||||
~CBufferPtrT() {Free();}
|
||||
|
||||
T* Malloc(size_t size = 1, bool zero = false)
|
||||
{
|
||||
Free();
|
||||
return Alloc(size, zero, false);
|
||||
}
|
||||
|
||||
T* Realloc(size_t size, bool zero = false)
|
||||
{
|
||||
return Alloc(size, zero, true);
|
||||
}
|
||||
|
||||
void Free()
|
||||
{
|
||||
if(m_pch)
|
||||
{
|
||||
free(m_pch);
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t S> CBufferPtrT& Copy(const CBufferPtrT<T, S>& other)
|
||||
{
|
||||
if((void*)&other != (void*)this)
|
||||
Copy(other.Ptr(), other.Size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBufferPtrT& Copy(const T* pch, size_t size)
|
||||
{
|
||||
Malloc(size);
|
||||
|
||||
if(m_pch)
|
||||
memcpy(m_pch, pch, size * sizeof(T));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t S> CBufferPtrT& Cat(const CBufferPtrT<T, S>& other)
|
||||
{
|
||||
if((void*)&other != (void*)this)
|
||||
Cat(other.Ptr(), other.Size());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CBufferPtrT& Cat(const T* pch, size_t size = 1)
|
||||
{
|
||||
size_t pre_size = m_size;
|
||||
Realloc(m_size + size);
|
||||
|
||||
if(m_pch)
|
||||
memcpy(m_pch + pre_size, pch, size * sizeof(T));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<size_t S> bool Equal(const CBufferPtrT<T, S>& other) const
|
||||
{
|
||||
if((void*)&other == (void*)this)
|
||||
return true;
|
||||
else if(m_size != other.Size())
|
||||
return false;
|
||||
else if(m_size == 0)
|
||||
return true;
|
||||
else
|
||||
return (memcmp(m_pch, other.Ptr(), m_size * sizeof(T)) == 0);
|
||||
}
|
||||
|
||||
bool Equal(T* pch) const
|
||||
{
|
||||
if(m_pch == pch)
|
||||
return true;
|
||||
else if(!m_pch || !pch)
|
||||
return false;
|
||||
else
|
||||
return (memcmp(m_pch, pch, m_size * sizeof(T)) == 0);
|
||||
}
|
||||
|
||||
size_t SetSize(size_t size)
|
||||
{
|
||||
if(size < 0 || size > m_capacity)
|
||||
size = m_capacity;
|
||||
|
||||
return (m_size = size);
|
||||
}
|
||||
|
||||
T* Ptr() {return m_pch;}
|
||||
const T* Ptr() const {return m_pch;}
|
||||
T& Get(int i) {return *(m_pch + i);}
|
||||
const T& Get(int i) const {return *(m_pch + i);}
|
||||
size_t Size() const {return m_size;}
|
||||
size_t Capacity() const {return m_capacity;}
|
||||
bool IsValid() const {return m_pch != 0;}
|
||||
|
||||
operator T* () {return Ptr();}
|
||||
operator const T* () const {return Ptr();}
|
||||
T& operator [] (int i) {return Get(i);}
|
||||
const T& operator [] (int i) const {return Get(i);}
|
||||
bool operator == (T* pv) const {return Equal(pv);}
|
||||
template<size_t S> bool operator == (const CBufferPtrT<T, S>& other) {return Equal(other);}
|
||||
CBufferPtrT& operator = (const CBufferPtrT& other) {return Copy(other);}
|
||||
template<size_t S> CBufferPtrT& operator = (const CBufferPtrT<T, S>& other) {return Copy(other);}
|
||||
|
||||
private:
|
||||
void Reset() {m_pch = 0; m_size = 0; m_capacity = 0;}
|
||||
size_t GetAllocSize(size_t size) {return MAX(size, MIN(size * 2, m_size + MAX_CACHE_SIZE));}
|
||||
|
||||
T* Alloc(size_t size, bool zero = false, bool is_realloc = false)
|
||||
{
|
||||
if(size != m_size)
|
||||
{
|
||||
size_t rsize = GetAllocSize(size);
|
||||
if(size > m_capacity || rsize < m_size)
|
||||
{
|
||||
T* pch = is_realloc ?
|
||||
(T*)realloc(m_pch, rsize * sizeof(T)) :
|
||||
(T*)malloc(rsize * sizeof(T)) ;
|
||||
|
||||
if(pch || rsize == 0)
|
||||
{
|
||||
m_pch = pch;
|
||||
m_size = size;
|
||||
m_capacity = rsize;
|
||||
}
|
||||
else
|
||||
{
|
||||
Free();
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
}
|
||||
else
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
if(zero && m_pch)
|
||||
memset(m_pch, 0, m_size * sizeof(T));
|
||||
|
||||
return m_pch;
|
||||
}
|
||||
|
||||
private:
|
||||
T* m_pch;
|
||||
size_t m_size;
|
||||
size_t m_capacity;
|
||||
};
|
||||
|
||||
typedef CBufferPtrT<char> CCharBufferPtr;
|
||||
typedef CBufferPtrT<wchar_t> CWCharBufferPtr;
|
||||
typedef CBufferPtrT<unsigned char> CByteBufferPtr;
|
||||
typedef CByteBufferPtr CBufferPtr;
|
||||
|
||||
#ifdef _UNICODE
|
||||
typedef CWCharBufferPtr CTCharBufferPtr;
|
||||
#else
|
||||
typedef CCharBufferPtr CTCharBufferPtr;
|
||||
#endif
|
||||
291
common/CriSec.h
Normal file
291
common/CriSec.h
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Singleton.h"
|
||||
#include "FuncHelper.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CSpinGuard
|
||||
{
|
||||
public:
|
||||
CSpinGuard() : m_atFlag(FALSE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CSpinGuard()
|
||||
{
|
||||
ASSERT(!m_atFlag);
|
||||
}
|
||||
|
||||
void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
|
||||
{
|
||||
for(UINT i = 0; !TryLock(bWeek, m); ++i)
|
||||
YieldThread(i);
|
||||
}
|
||||
|
||||
BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
|
||||
{
|
||||
BOOL bExpect = FALSE;
|
||||
|
||||
return bWeek
|
||||
? m_atFlag.compare_exchange_weak(bExpect, TRUE, m)
|
||||
: m_atFlag.compare_exchange_strong(bExpect, TRUE, m);
|
||||
}
|
||||
|
||||
void Unlock(memory_order m = memory_order_release)
|
||||
{
|
||||
ASSERT(m_atFlag);
|
||||
m_atFlag.store(FALSE, m);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSpinGuard)
|
||||
|
||||
private:
|
||||
atomic<BOOL> m_atFlag;
|
||||
};
|
||||
|
||||
class CReentrantSpinGuard
|
||||
{
|
||||
public:
|
||||
CReentrantSpinGuard()
|
||||
: m_atThreadID (0)
|
||||
, m_iCount (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CReentrantSpinGuard()
|
||||
{
|
||||
ASSERT(m_atThreadID == 0);
|
||||
ASSERT(m_iCount == 0);
|
||||
}
|
||||
|
||||
void Lock(BOOL bWeek = TRUE, memory_order m = memory_order_acquire)
|
||||
{
|
||||
for(UINT i = 0; !_TryLock(i == 0, bWeek, m); ++i)
|
||||
YieldThread(i);
|
||||
}
|
||||
|
||||
BOOL TryLock(BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
|
||||
{
|
||||
return _TryLock(TRUE, bWeek, m);
|
||||
}
|
||||
|
||||
void Unlock(memory_order m = memory_order_release)
|
||||
{
|
||||
ASSERT(::IsSelfThread(m_atThreadID));
|
||||
|
||||
if((--m_iCount) == 0)
|
||||
m_atThreadID.store(0, m);
|
||||
}
|
||||
|
||||
private:
|
||||
BOOL _TryLock(BOOL bFirst, BOOL bWeek = FALSE, memory_order m = memory_order_acquire)
|
||||
{
|
||||
THR_ID dwCurrentThreadID = SELF_THREAD_ID;
|
||||
|
||||
if(bFirst && ::IsSameThread(m_atThreadID, dwCurrentThreadID))
|
||||
{
|
||||
++m_iCount;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
THR_ID ulExpect = 0;
|
||||
|
||||
BOOL isOK = bWeek
|
||||
? m_atThreadID.compare_exchange_weak(ulExpect, dwCurrentThreadID, m)
|
||||
: m_atThreadID.compare_exchange_strong(ulExpect, dwCurrentThreadID, m);
|
||||
|
||||
|
||||
if(isOK)
|
||||
{
|
||||
ASSERT(m_iCount == 0);
|
||||
|
||||
m_iCount = 1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CReentrantSpinGuard)
|
||||
|
||||
private:
|
||||
atomic_tid m_atThreadID;
|
||||
int m_iCount;
|
||||
};
|
||||
|
||||
class CFakeGuard
|
||||
{
|
||||
public:
|
||||
void Lock() {}
|
||||
void Unlock() {}
|
||||
BOOL TryLock() {return TRUE;}
|
||||
};
|
||||
|
||||
template<class CLockObj> class CLocalLock
|
||||
{
|
||||
public:
|
||||
CLocalLock(CLockObj& obj) : m_lock(obj) {m_lock.Lock();}
|
||||
~CLocalLock() {m_lock.Unlock();}
|
||||
private:
|
||||
CLockObj& m_lock;
|
||||
};
|
||||
|
||||
template<class CLockObj> class CLocalTryLock
|
||||
{
|
||||
public:
|
||||
CLocalTryLock(CLockObj& obj) : m_lock(obj) {m_bValid = m_lock.TryLock();}
|
||||
~CLocalTryLock() {if(m_bValid) m_lock.Unlock();}
|
||||
|
||||
BOOL IsValid() {return m_bValid;}
|
||||
|
||||
private:
|
||||
CLockObj& m_lock;
|
||||
BOOL m_bValid;
|
||||
};
|
||||
|
||||
template<class CMTXObj> class CMTXTryLock
|
||||
{
|
||||
public:
|
||||
CMTXTryLock(CMTXObj& obj) : m_lock(obj) {m_bValid = m_lock.try_lock();}
|
||||
~CMTXTryLock() {if(m_bValid) m_lock.unlock();}
|
||||
|
||||
BOOL IsValid() {return m_bValid;}
|
||||
|
||||
private:
|
||||
CMTXObj& m_lock;
|
||||
BOOL m_bValid;
|
||||
};
|
||||
|
||||
using CSpinLock = CLocalLock<CSpinGuard>;
|
||||
using CReentrantSpinLock = CLocalLock<CReentrantSpinGuard>;
|
||||
using CFakeLock = CLocalLock<CFakeGuard>;
|
||||
|
||||
using CCriSec = mutex;
|
||||
using CCriSecLock = lock_guard<mutex>;
|
||||
using CCriSecLock2 = unique_lock<mutex>;
|
||||
using CCriSecTryLock = CMTXTryLock<mutex>;
|
||||
|
||||
using CMTX = CCriSec;
|
||||
using CMutexLock = CCriSecLock;
|
||||
using CMutexLock2 = CCriSecLock2;
|
||||
using CMutexTryLock = CCriSecTryLock;
|
||||
|
||||
using CReentrantCriSec = recursive_mutex;
|
||||
using CReentrantCriSecLock = lock_guard<recursive_mutex>;
|
||||
using CReentrantCriSecLock2 = unique_lock<recursive_mutex>;
|
||||
using CReentrantCriSecTryLock = CMTXTryLock<recursive_mutex>;
|
||||
|
||||
using CReentrantMTX = CReentrantCriSec;
|
||||
using CReentrantMutexLock = CReentrantCriSecLock;
|
||||
using CReentrantMutexLock2 = CReentrantCriSecLock2;
|
||||
using CReentrantMutexTryLock = CReentrantCriSecTryLock;
|
||||
|
||||
template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CSafeCounterT
|
||||
{
|
||||
public:
|
||||
T Increment() {return ::InterlockedIncrement(&m_iCount);}
|
||||
T Decrement() {return ::InterlockedDecrement(&m_iCount);}
|
||||
T AddFetch(T iCount) {return ::InterlockedAdd(&m_iCount, iCount);}
|
||||
T SubFetch(T iCount) {return ::InterlockedSub(&m_iCount, iCount);}
|
||||
T FetchAdd(T iCount) {return ::InterlockedExchangeAdd(&m_iCount, iCount);}
|
||||
T FetchSub(T iCount) {return ::InterlockedExchangeSub(&m_iCount, iCount);}
|
||||
|
||||
T SetCount(T iCount) {return (m_iCount = iCount);}
|
||||
T ResetCount() {return SetCount(0);}
|
||||
T GetCount() {return m_iCount;}
|
||||
|
||||
T operator ++ () {return Increment();}
|
||||
T operator -- () {return Decrement();}
|
||||
T operator ++ (int) {return FetchAdd(1);}
|
||||
T operator -- (int) {return FetchSub(1);}
|
||||
T operator += (T iCount) {return AddFetch(iCount);}
|
||||
T operator -= (T iCount) {return SubFetch(iCount);}
|
||||
T operator = (T iCount) {return SetCount(iCount);}
|
||||
operator T () {return GetCount();}
|
||||
|
||||
public:
|
||||
CSafeCounterT(T iCount = 0) : m_iCount(iCount) {}
|
||||
|
||||
protected:
|
||||
volatile T m_iCount;
|
||||
};
|
||||
|
||||
template<typename T, typename = enable_if_t<is_arithmetic<T>::value>> class CUnsafeCounterT
|
||||
{
|
||||
public:
|
||||
T Increment() {return ++m_iCount;}
|
||||
T Decrement() {return --m_iCount;}
|
||||
T AddFetch(T iCount) {return m_iCount += iCount;}
|
||||
T SubFetch(T iCount) {return m_iCount -= iCount;}
|
||||
T FetchAdd(T iCount) {T rs = m_iCount; m_iCount += iCount; return rs;}
|
||||
T FetchSub(T iCount) {T rs = m_iCount; m_iCount -= iCount; return rs;}
|
||||
|
||||
T SetCount(T iCount) {return (m_iCount = iCount);}
|
||||
T ResetCount() {return SetCount(0);}
|
||||
T GetCount() {return m_iCount;}
|
||||
|
||||
T operator ++ () {return Increment();}
|
||||
T operator -- () {return Decrement();}
|
||||
T operator ++ (int) {return FetchAdd(1);}
|
||||
T operator -- (int) {return FetchSub(1);}
|
||||
T operator += (T iCount) {return AddFetch(iCount);}
|
||||
T operator -= (T iCount) {return SubFetch(iCount);}
|
||||
T operator = (T iCount) {return SetCount(iCount);}
|
||||
operator T () {return GetCount();}
|
||||
|
||||
public:
|
||||
CUnsafeCounterT(T iCount = 0) : m_iCount(iCount) {}
|
||||
|
||||
protected:
|
||||
T m_iCount;
|
||||
};
|
||||
|
||||
template<class CCounter> class CLocalCounter
|
||||
{
|
||||
public:
|
||||
CLocalCounter(CCounter& obj) : m_counter(obj) {m_counter.Increment();}
|
||||
~CLocalCounter() {m_counter.Decrement();}
|
||||
private:
|
||||
CCounter& m_counter;
|
||||
};
|
||||
|
||||
using CSafeCounter = CSafeCounterT<INT>;
|
||||
using CSafeBigCounter = CSafeCounterT<LONGLONG>;
|
||||
using CUnsafeCounter = CUnsafeCounterT<INT>;
|
||||
using CUnsafeBigCounter = CUnsafeCounterT<LONGLONG>;
|
||||
|
||||
using CLocalSafeCounter = CLocalCounter<CSafeCounter>;
|
||||
using CLocalSafeBigCounter = CLocalCounter<CSafeBigCounter>;
|
||||
using CLocalUnsafeCounter = CLocalCounter<CUnsafeCounter>;
|
||||
using CLocalUnsafeBigCounter = CLocalCounter<CUnsafeBigCounter>;
|
||||
24
common/Event.cpp
Normal file
24
common/Event.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Event.h"
|
||||
530
common/Event.h
Normal file
530
common/Event.h
Normal file
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Singleton.h"
|
||||
#include "FuncHelper.h"
|
||||
#include "PollHelper.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
class CPipeEvent
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
EVT_1 = 0x01,
|
||||
EVT_WAKEUP = EVT_1,
|
||||
EVT_EXIT = 0x7F,
|
||||
EVT_SIG_0 = 0x80,
|
||||
EVT_SIG_MAX = EVT_SIG_0 + _NSIG,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
int Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
||||
{
|
||||
pollfd pfd = {m_fd[0], POLLIN};
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
int rs = (int)::PollForSingleObject(pfd, lTimeout, pSigSet);
|
||||
|
||||
if(rs <= TIMEOUT) return rs;
|
||||
|
||||
if(pfd.revents & POLLIN)
|
||||
{
|
||||
BYTE v;
|
||||
|
||||
if(!Get(v))
|
||||
return HAS_ERROR;
|
||||
|
||||
if(v == 0)
|
||||
continue;
|
||||
|
||||
return (int)v;
|
||||
}
|
||||
|
||||
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
||||
{
|
||||
::SetLastError(ERROR_BROKEN_PIPE);
|
||||
return HAS_ERROR;
|
||||
}
|
||||
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL Set(BYTE bVal = EVT_WAKEUP)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(bVal != 0);
|
||||
|
||||
return VERIFY(write(m_fd[1], &bVal, 1) > 0);
|
||||
}
|
||||
|
||||
BOOL Get(BYTE& v)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
int rs = (int)read(m_fd[0], &v, 1);
|
||||
|
||||
if(IS_HAS_ERROR(rs))
|
||||
{
|
||||
if(IS_WOULDBLOCK_ERROR())
|
||||
v = 0;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
else if(rs == 0)
|
||||
{
|
||||
::SetLastError(ERROR_BROKEN_PIPE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
BYTE v;
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
if(!Get(v))
|
||||
return FALSE;
|
||||
|
||||
if(v == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL SetSignal(BYTE bSigVal)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(bSigVal > 0 && bSigVal < _NSIG);
|
||||
|
||||
return Set((BYTE)(EVT_SIG_0 + bSigVal));
|
||||
}
|
||||
|
||||
static inline BYTE ToSignalValue(int iWaitResult)
|
||||
{
|
||||
if(iWaitResult <= EVT_SIG_0 || iWaitResult >= EVT_SIG_MAX)
|
||||
return 0;
|
||||
|
||||
return (BYTE)(iWaitResult - EVT_SIG_0);
|
||||
}
|
||||
|
||||
BOOL IsValid() {return IS_VALID_FD(m_fd[0]) && IS_VALID_FD(m_fd[1]);}
|
||||
|
||||
operator FD () {return m_fd[0];}
|
||||
FD GetFD () {return m_fd[0];}
|
||||
|
||||
public:
|
||||
CPipeEvent()
|
||||
{
|
||||
VERIFY_IS_NO_ERROR(pipe2(m_fd, O_NONBLOCK | O_CLOEXEC));
|
||||
VERIFY(::fcntl_SETFL(m_fd[0], O_NOATIME));
|
||||
VERIFY(::fcntl_SETFL(m_fd[1], O_NOATIME));
|
||||
}
|
||||
|
||||
~CPipeEvent()
|
||||
{
|
||||
close(m_fd[1]);
|
||||
close(m_fd[0]);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CPipeEvent)
|
||||
|
||||
private:
|
||||
FD m_fd[2] = {INVALID_FD, INVALID_FD};
|
||||
};
|
||||
|
||||
template<bool is_sem_mode = false> class CCounterEvent
|
||||
{
|
||||
public:
|
||||
|
||||
eventfd_t Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
||||
{
|
||||
pollfd pfd = {m_evt, POLLIN};
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
||||
|
||||
if(rs <= TIMEOUT) return (eventfd_t)rs;
|
||||
|
||||
if(pfd.revents & POLLIN)
|
||||
{
|
||||
eventfd_t v;
|
||||
|
||||
if(!Get(v))
|
||||
return HAS_ERROR;
|
||||
|
||||
if(v == 0)
|
||||
continue;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
||||
{
|
||||
::SetLastError(ERROR_HANDLES_CLOSED);
|
||||
return HAS_ERROR;
|
||||
}
|
||||
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL Set(eventfd_t val = 1)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(val > 0);
|
||||
|
||||
int rs = eventfd_write(m_evt, val);
|
||||
return VERIFY_IS_NO_ERROR(rs);
|
||||
}
|
||||
|
||||
BOOL Get(eventfd_t& v)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
if(IS_HAS_ERROR(eventfd_read(m_evt, &v)))
|
||||
{
|
||||
if(IS_WOULDBLOCK_ERROR())
|
||||
v = 0;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
eventfd_t v;
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
if(!Get(v))
|
||||
return FALSE;
|
||||
|
||||
if(v == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL IsValid() {return IS_VALID_FD(m_evt);}
|
||||
|
||||
operator FD () {return m_evt;}
|
||||
FD GetFD () {return m_evt;}
|
||||
|
||||
public:
|
||||
CCounterEvent(int iInitCount = 0)
|
||||
{
|
||||
int iFlag = EFD_NONBLOCK | EFD_CLOEXEC | (is_sem_mode ? EFD_SEMAPHORE : 0);
|
||||
m_evt = eventfd(iInitCount, iFlag);
|
||||
|
||||
VERIFY(IsValid());
|
||||
}
|
||||
|
||||
~CCounterEvent()
|
||||
{
|
||||
if(IsValid()) close(m_evt);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CCounterEvent)
|
||||
|
||||
private:
|
||||
FD m_evt = INVALID_FD;
|
||||
};
|
||||
|
||||
using CSimpleEvent = CCounterEvent<false>;
|
||||
using CSemaphoreEvent = CCounterEvent<true>;
|
||||
using CEvt = CSimpleEvent;
|
||||
|
||||
class CTimerEvent
|
||||
{
|
||||
public:
|
||||
|
||||
ULLONG Wait(long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
||||
{
|
||||
pollfd pfd = {m_tmr, POLLIN};
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
SSIZE_T rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
||||
|
||||
if(rs <= TIMEOUT) return (ULLONG)rs;
|
||||
|
||||
if(pfd.revents & POLLIN)
|
||||
{
|
||||
BOOL ok;
|
||||
ULLONG v;
|
||||
|
||||
if(!Get(v, ok))
|
||||
return HAS_ERROR;
|
||||
|
||||
if(!ok)
|
||||
continue;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
||||
{
|
||||
::SetLastError(ERROR_HANDLES_CLOSED);
|
||||
return HAS_ERROR;
|
||||
}
|
||||
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL Set(LLONG llInterval, LLONG llStart = -1)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(llInterval >= 0L);
|
||||
|
||||
if(llStart < 0)
|
||||
llStart = llInterval;
|
||||
|
||||
itimerspec its;
|
||||
|
||||
::MillisecondToTimespec(llStart, its.it_value);
|
||||
::MillisecondToTimespec(llInterval, its.it_interval);
|
||||
|
||||
int rs = timerfd_settime(m_tmr, 0, &its, nullptr);
|
||||
return VERIFY_IS_NO_ERROR(rs);
|
||||
}
|
||||
|
||||
BOOL Get(ULLONG &v, BOOL& ok)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
return ::ReadTimer(m_tmr, &v, &ok);
|
||||
}
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
BOOL ok;
|
||||
ULLONG v;
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
if(!Get(v, ok))
|
||||
return FALSE;
|
||||
|
||||
if(!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL GetTime(LLONG& lStart, LLONG& lInterval)
|
||||
{
|
||||
itimerspec its;
|
||||
|
||||
if(IS_HAS_ERROR(timerfd_gettime(m_tmr, &its)))
|
||||
return FALSE;
|
||||
|
||||
lStart = ::TimespecToMillisecond(its.it_value);
|
||||
lInterval = ::TimespecToMillisecond(its.it_interval);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL IsValid() {return IS_VALID_FD(m_tmr);}
|
||||
|
||||
operator FD () {return m_tmr;}
|
||||
FD GetFD () {return m_tmr;}
|
||||
|
||||
public:
|
||||
CTimerEvent(bool bRealTimeClock = FALSE)
|
||||
{
|
||||
int iCID = (bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC);
|
||||
m_tmr = timerfd_create(iCID, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
|
||||
VERIFY(IsValid());
|
||||
}
|
||||
|
||||
~CTimerEvent()
|
||||
{
|
||||
if(IsValid()) close(m_tmr);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CTimerEvent)
|
||||
|
||||
private:
|
||||
FD m_tmr = INVALID_FD;
|
||||
};
|
||||
|
||||
class CSignalEvent
|
||||
{
|
||||
public:
|
||||
|
||||
int Wait(signalfd_siginfo& sgInfo, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr)
|
||||
{
|
||||
m_dwTID = SELF_THREAD_ID;
|
||||
pollfd pfd = {m_sig, POLLIN};
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
long rs = ::PollForSingleObject(pfd, lTimeout, pSigSet);
|
||||
|
||||
if(rs <= TIMEOUT) return (int)rs;
|
||||
|
||||
if(pfd.revents & POLLIN)
|
||||
{
|
||||
BOOL ok;
|
||||
|
||||
if(!Get(sgInfo, ok))
|
||||
return HAS_ERROR;
|
||||
|
||||
if(!ok)
|
||||
continue;
|
||||
|
||||
return sgInfo.ssi_signo;
|
||||
}
|
||||
|
||||
if(pfd.revents & _POLL_ALL_ERROR_EVENTS)
|
||||
{
|
||||
::SetLastError(ERROR_HANDLES_CLOSED);
|
||||
return HAS_ERROR;
|
||||
}
|
||||
|
||||
ASSERT(FALSE);
|
||||
}
|
||||
|
||||
m_dwTID = 0;
|
||||
}
|
||||
|
||||
BOOL Set(int iSig, const sigval sgVal, THR_ID dwTID = 0)
|
||||
{
|
||||
if(dwTID == 0)
|
||||
{
|
||||
dwTID = m_dwTID;
|
||||
|
||||
if(dwTID == 0)
|
||||
{
|
||||
::SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
int rs = pthread_sigqueue(dwTID, iSig, sgVal);
|
||||
#else
|
||||
int rs = pthread_kill(dwTID, iSig);
|
||||
#endif
|
||||
|
||||
return IS_NO_ERROR(rs);
|
||||
}
|
||||
|
||||
BOOL Get(signalfd_siginfo& v, BOOL& ok)
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
static const SSIZE_T SIZE = sizeof(signalfd_siginfo);
|
||||
|
||||
if(read(m_sig, &v, SIZE) == SIZE)
|
||||
ok = TRUE;
|
||||
{
|
||||
if(IS_WOULDBLOCK_ERROR())
|
||||
ok = FALSE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
BOOL ok;
|
||||
signalfd_siginfo v;
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
if(!Get(v, ok))
|
||||
return FALSE;
|
||||
|
||||
if(!ok)
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL Mask(const sigset_t* pSigMask)
|
||||
{
|
||||
if(!pSigMask)
|
||||
{
|
||||
if(!IsValid()) return TRUE;
|
||||
return IS_NO_ERROR(close(m_sig));
|
||||
}
|
||||
|
||||
FD sig = signalfd(m_sig, pSigMask, SFD_NONBLOCK | SFD_CLOEXEC);
|
||||
|
||||
if(IS_VALID_FD(sig))
|
||||
{
|
||||
m_sig = sig;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL IsValid() {return IS_VALID_FD(m_sig);}
|
||||
|
||||
operator FD () {return m_sig;}
|
||||
FD GetFD () {return m_sig;}
|
||||
|
||||
public:
|
||||
CSignalEvent(const sigset_t* pSigMask = nullptr)
|
||||
{
|
||||
if(pSigMask) VERIFY(Mask(pSigMask));
|
||||
}
|
||||
|
||||
~CSignalEvent()
|
||||
{
|
||||
if(IsValid()) close(m_sig);
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSignalEvent)
|
||||
|
||||
private:
|
||||
FD m_sig = INVALID_FD;
|
||||
THR_ID m_dwTID = 0;
|
||||
};
|
||||
237
common/FileHelper.cpp
Normal file
237
common/FileHelper.cpp
Normal file
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "FileHelper.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
CString GetCurrentDirectory()
|
||||
{
|
||||
char szPath[MAX_PATH];
|
||||
|
||||
if(getcwd(szPath, sizeof(szPath) - 1) == nullptr)
|
||||
szPath[0] = 0;
|
||||
|
||||
return szPath;
|
||||
}
|
||||
|
||||
CString GetModuleFileName(pid_t pid)
|
||||
{
|
||||
if(pid == 0)
|
||||
pid = SELF_PROCESS_ID;
|
||||
|
||||
char szLink[MAX_PATH];
|
||||
char szPath[MAX_PATH];
|
||||
|
||||
sprintf(szLink, "/proc/%d/exe", pid);
|
||||
|
||||
SSIZE_T rs = readlink(szLink, szPath, sizeof(szPath) - 1);
|
||||
|
||||
if(rs < 0) rs = 0;
|
||||
szPath[rs] = 0;
|
||||
|
||||
return szPath;
|
||||
}
|
||||
|
||||
BOOL SetCurrentPathToModulePath(pid_t pid)
|
||||
{
|
||||
CString strPath = GetModuleFileName(pid);
|
||||
|
||||
if(strPath.IsEmpty())
|
||||
return FALSE;
|
||||
|
||||
CString::size_type pos = strPath.rfind('/');
|
||||
|
||||
if(pos == CString::npos)
|
||||
return FALSE;
|
||||
|
||||
return IS_NO_ERROR(chdir(strPath.substr(0, pos + 1)));
|
||||
}
|
||||
|
||||
BOOL CFile::Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode)
|
||||
{
|
||||
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
m_fd = open(lpszFilePath, iFlag, iMode);
|
||||
|
||||
return IS_VALID_FD(m_fd);
|
||||
}
|
||||
|
||||
BOOL CFile::Close()
|
||||
{
|
||||
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
if(IS_NO_ERROR(close(m_fd)))
|
||||
{
|
||||
m_fd = INVALID_FD;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CFile::Stat(struct stat& st)
|
||||
{
|
||||
CHECK_ERROR_INVOKE(fstat(m_fd, &st));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CFile::GetSize(SIZE_T& dwSize)
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_IS_OK(Stat(st));
|
||||
|
||||
dwSize = st.st_size;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL CFile::IsDirectory()
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_IS_OK(Stat(st));
|
||||
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
BOOL CFile::IsFile()
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_IS_OK(Stat(st));
|
||||
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
BOOL CFile::IsExist(LPCTSTR lpszFilePath)
|
||||
{
|
||||
return IS_NO_ERROR(access(lpszFilePath, F_OK));
|
||||
}
|
||||
|
||||
BOOL CFile::IsDirectory(LPCTSTR lpszFilePath)
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
|
||||
|
||||
return S_ISDIR(st.st_mode);
|
||||
}
|
||||
|
||||
BOOL CFile::IsFile(LPCTSTR lpszFilePath)
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_ERROR_INVOKE(stat(lpszFilePath, &st));
|
||||
|
||||
return S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
BOOL CFile::IsLink(LPCTSTR lpszFilePath)
|
||||
{
|
||||
struct stat st;
|
||||
CHECK_ERROR_INVOKE(lstat(lpszFilePath, &st));
|
||||
|
||||
return S_ISLNK(st.st_mode);
|
||||
}
|
||||
|
||||
BOOL CFileMapping::Map(LPCTSTR lpszFilePath, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
|
||||
{
|
||||
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
FD fd = INVALID_FD;
|
||||
|
||||
if(lpszFilePath != nullptr)
|
||||
{
|
||||
int iFileFlag = O_RDONLY;
|
||||
|
||||
if(iProtected & PROT_WRITE)
|
||||
{
|
||||
if(iProtected & PROT_READ)
|
||||
iFileFlag = O_RDWR;
|
||||
else
|
||||
iFileFlag = O_WRONLY;
|
||||
}
|
||||
|
||||
fd = open(lpszFilePath, iFileFlag);
|
||||
CHECK_ERROR_FD(fd);
|
||||
}
|
||||
|
||||
BOOL isOK = Map(fd, dwSize, dwOffset, iProtected, iFlag);
|
||||
|
||||
if(IS_VALID_FD(fd)) EXECUTE_RESTORE_ERROR(close(fd));
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CFileMapping::Map(FD fd, SIZE_T dwSize, SIZE_T dwOffset, int iProtected, int iFlag)
|
||||
{
|
||||
CHECK_ERROR(!IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
if(IS_INVALID_FD(fd))
|
||||
{
|
||||
CHECK_EINVAL((iFlag & MAP_ANONYMOUS) && (dwSize > 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK_EINVAL((iFlag & MAP_ANONYMOUS) == 0);
|
||||
|
||||
struct stat st;
|
||||
CHECK_ERROR_INVOKE(fstat(fd, &st));
|
||||
|
||||
CHECK_ERROR(S_ISREG(st.st_mode), ERROR_BAD_FILE_TYPE);
|
||||
|
||||
if(dwSize == 0)
|
||||
dwSize = st.st_size;
|
||||
}
|
||||
|
||||
m_pv = (PBYTE)mmap(nullptr, dwSize, iProtected, iFlag, fd, dwOffset);
|
||||
|
||||
if(IsValid())
|
||||
{
|
||||
m_dwSize = dwSize;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CFileMapping::Unmap()
|
||||
{
|
||||
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
if(IS_NO_ERROR(munmap(m_pv, m_dwSize)))
|
||||
{
|
||||
m_pv = INVALID_MAP_ADDR;
|
||||
m_dwSize = 0;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CFileMapping::MSync(int iFlag, SIZE_T dwSize)
|
||||
{
|
||||
CHECK_ERROR(IsValid(), ERROR_INVALID_STATE);
|
||||
|
||||
if(dwSize == 0) dwSize = m_dwSize;
|
||||
|
||||
return IS_NO_ERROR(msync(m_pv, dwSize, iFlag));
|
||||
}
|
||||
123
common/FileHelper.h
Normal file
123
common/FileHelper.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FuncHelper.h"
|
||||
#include "StringT.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#define INVALID_MAP_ADDR ((PBYTE)(MAP_FAILED))
|
||||
|
||||
CString GetCurrentDirectory();
|
||||
CString GetModuleFileName(pid_t pid = 0);
|
||||
BOOL SetCurrentPathToModulePath(pid_t pid = 0);
|
||||
|
||||
class CFile
|
||||
{
|
||||
public:
|
||||
BOOL Open(LPCTSTR lpszFilePath, int iFlag, mode_t iMode = 0);
|
||||
BOOL Close();
|
||||
BOOL Stat(struct stat& st);
|
||||
BOOL GetSize(SIZE_T& dwSize);
|
||||
|
||||
SSIZE_T Read(PVOID pBuffer, SIZE_T dwCount)
|
||||
{return read(m_fd, pBuffer, dwCount);}
|
||||
SSIZE_T Write(PVOID pBuffer, SIZE_T dwCount)
|
||||
{return write(m_fd, pBuffer, dwCount);}
|
||||
SSIZE_T PRead(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
|
||||
{return pread(m_fd, pBuffer, dwCount, dwOffset);}
|
||||
SSIZE_T PWrite(PVOID pBuffer, SIZE_T dwCount, SIZE_T dwOffset)
|
||||
{return pwrite(m_fd, pBuffer, dwCount, dwOffset);}
|
||||
SSIZE_T ReadV(const iovec* pVec, int iVecCount)
|
||||
{return readv(m_fd, pVec, iVecCount);}
|
||||
SSIZE_T WriteV(const iovec* pVec, int iVecCount)
|
||||
{return writev(m_fd, pVec, iVecCount);}
|
||||
SSIZE_T Seek(SSIZE_T lOffset, int iWhence)
|
||||
{return lseek(m_fd, lOffset, iWhence);}
|
||||
|
||||
BOOL IsValid() {return IS_VALID_FD(m_fd);}
|
||||
operator FD () {return m_fd;}
|
||||
|
||||
BOOL IsExist() {return IsValid();}
|
||||
|
||||
BOOL IsDirectory();
|
||||
BOOL IsFile();
|
||||
|
||||
static BOOL IsExist(LPCTSTR lpszFilePath);
|
||||
static BOOL IsDirectory(LPCTSTR lpszFilePath);
|
||||
static BOOL IsFile(LPCTSTR lpszFilePath);
|
||||
static BOOL IsLink(LPCTSTR lpszFilePath);
|
||||
|
||||
public:
|
||||
CFile(LPCTSTR lpszFilePath = nullptr, int iFlag = O_RDONLY, mode_t iMode = 0)
|
||||
: m_fd(INVALID_FD)
|
||||
{
|
||||
if(lpszFilePath != nullptr)
|
||||
Open(lpszFilePath, iFlag, iMode);
|
||||
}
|
||||
|
||||
~CFile()
|
||||
{
|
||||
if(IsValid())
|
||||
Close();
|
||||
}
|
||||
|
||||
private:
|
||||
FD m_fd;
|
||||
};
|
||||
|
||||
class CFileMapping
|
||||
{
|
||||
public:
|
||||
BOOL Map(LPCTSTR lpszFilePath, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
|
||||
BOOL Map(FD fd, SIZE_T dwSize = 0, SIZE_T dwOffset = 0, int iProtected = PROT_READ, int iFlag = MAP_PRIVATE);
|
||||
BOOL Unmap();
|
||||
BOOL MSync(int iFlag = MS_SYNC, SIZE_T dwSize = 0);
|
||||
|
||||
BOOL IsValid () {return m_pv != INVALID_MAP_ADDR;}
|
||||
SIZE_T Size () {return m_dwSize;}
|
||||
LPBYTE Ptr () {return m_pv;}
|
||||
operator LPBYTE () {return Ptr();}
|
||||
|
||||
public:
|
||||
CFileMapping()
|
||||
: m_pv(INVALID_MAP_ADDR)
|
||||
, m_dwSize(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
~CFileMapping()
|
||||
{
|
||||
if(IsValid())
|
||||
Unmap();
|
||||
}
|
||||
|
||||
private:
|
||||
PBYTE m_pv;
|
||||
SIZE_T m_dwSize;
|
||||
};
|
||||
393
common/FuncHelper.cpp
Normal file
393
common/FuncHelper.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "FuncHelper.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <sched.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <sys/timeb.h>
|
||||
#include <execinfo.h>
|
||||
#ifdef __GNUC__
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
INT WaitFor(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
|
||||
{
|
||||
timeval tv {(time_t)dwSecond, (suseconds_t)(dwMillSecond * 1000)};
|
||||
|
||||
if(bExceptThreadInterrupted)
|
||||
return NO_EINTR_EXCEPT_THR_INTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
|
||||
|
||||
return NO_EINTR_INT(select(0, nullptr, nullptr, nullptr, &tv));
|
||||
}
|
||||
|
||||
INT Sleep(DWORD dwMillSecond, DWORD dwSecond, BOOL bExceptThreadInterrupted)
|
||||
{
|
||||
timespec ts_req = {(time_t)dwSecond, (long)(dwMillSecond * 1000000)};
|
||||
timespec ts_rem = ts_req;
|
||||
INT rs = NO_ERROR;
|
||||
|
||||
while(IS_HAS_ERROR(rs = nanosleep(&ts_req, &ts_rem)))
|
||||
{
|
||||
if(!IS_INTR_ERROR())
|
||||
break;
|
||||
else
|
||||
{
|
||||
if(bExceptThreadInterrupted && ::IsThreadInterrupted())
|
||||
break;
|
||||
}
|
||||
|
||||
ts_req = ts_rem;
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
__time64_t _time64(time_t* ptm)
|
||||
{
|
||||
return (__time64_t)time(ptm);
|
||||
}
|
||||
|
||||
__time64_t _mkgmtime64(tm* ptm)
|
||||
{
|
||||
return (__time64_t)timegm(ptm);
|
||||
}
|
||||
|
||||
tm* _gmtime64(tm* ptm, __time64_t* pt)
|
||||
{
|
||||
time_t t = (time_t)(*pt);
|
||||
|
||||
return gmtime_r(&t, ptm);
|
||||
}
|
||||
|
||||
DWORD TimeGetTime()
|
||||
{
|
||||
return (DWORD)TimeGetTime64();
|
||||
}
|
||||
|
||||
ULLONG TimeGetTime64()
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
|
||||
timeb tb;
|
||||
|
||||
if(ftime(&tb) == NO_ERROR)
|
||||
return (((ULLONG)(tb.time)) * 1000 + tb.millitm);
|
||||
|
||||
#else
|
||||
|
||||
timespec ts;
|
||||
|
||||
if(clock_gettime(CLOCK_MONOTONIC, &ts) == NO_ERROR)
|
||||
return (((ULLONG)(ts.tv_sec)) * 1000 + ts.tv_nsec / 1000000);
|
||||
|
||||
#endif
|
||||
|
||||
return 0ull;
|
||||
}
|
||||
|
||||
DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent)
|
||||
{
|
||||
if(dwCurrent == 0)
|
||||
dwCurrent = ::TimeGetTime();
|
||||
|
||||
return dwCurrent - dwOriginal;
|
||||
}
|
||||
|
||||
ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent)
|
||||
{
|
||||
if(ullCurrent == 0)
|
||||
ullCurrent = ::TimeGetTime64();
|
||||
|
||||
return ullCurrent - ullOriginal;
|
||||
}
|
||||
|
||||
LLONG TimevalToMillisecond(const timeval& tv)
|
||||
{
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
timeval& MillisecondToTimeval(LLONG ms, timeval& tv)
|
||||
{
|
||||
tv.tv_sec = (time_t)(ms / 1000);
|
||||
tv.tv_usec = (suseconds_t)((ms % 1000) * 1000);
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
||||
LLONG TimespecToMillisecond(const timespec& ts)
|
||||
{
|
||||
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
||||
}
|
||||
|
||||
timespec& MillisecondToTimespec(LLONG ms, timespec& ts)
|
||||
{
|
||||
ts.tv_sec = (time_t)(ms / 1000);
|
||||
ts.tv_nsec = (long)((ms % 1000) * 1000000);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz)
|
||||
{
|
||||
gettimeofday(&tv, ptz);
|
||||
|
||||
tv.tv_sec += (time_t)(ms / 1000);
|
||||
tv.tv_usec += (suseconds_t)((ms % 1000) * 1000);
|
||||
|
||||
return tv;
|
||||
}
|
||||
|
||||
timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid)
|
||||
{
|
||||
clock_gettime(clkid, &ts);
|
||||
|
||||
ts.tv_sec += (time_t)(ms / 1000);
|
||||
ts.tv_nsec += (long)((ms % 1000) * 1000000);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
FD CreateTimer(LLONG llInterval, LLONG llStart, BOOL bRealTimeClock)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(llInterval >= 0L);
|
||||
|
||||
if(llStart < 0)
|
||||
llStart = llInterval;
|
||||
|
||||
FD fdTimer = timerfd_create((bRealTimeClock ? CLOCK_REALTIME : CLOCK_MONOTONIC), TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
|
||||
itimerspec its;
|
||||
|
||||
::MillisecondToTimespec(llStart, its.it_value);
|
||||
::MillisecondToTimespec(llInterval, its.it_interval);
|
||||
|
||||
if(IS_HAS_ERROR(timerfd_settime(fdTimer, 0, &its, nullptr)))
|
||||
{
|
||||
close(fdTimer);
|
||||
fdTimer = INVALID_FD;
|
||||
}
|
||||
|
||||
return fdTimer;
|
||||
}
|
||||
|
||||
BOOL ReadTimer(FD tmr, ULLONG* pVal, BOOL* pRs)
|
||||
{
|
||||
static const SSIZE_T SIZE = sizeof(ULLONG);
|
||||
|
||||
if(pVal == nullptr)
|
||||
pVal = CreateLocalObject(ULLONG);
|
||||
if(pRs == nullptr)
|
||||
pRs = CreateLocalObject(BOOL);
|
||||
|
||||
if(read(tmr, pVal, SIZE) == SIZE)
|
||||
*pRs = TRUE;
|
||||
else
|
||||
{
|
||||
*pRs = FALSE;
|
||||
|
||||
if(!IS_WOULDBLOCK_ERROR())
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet)
|
||||
{
|
||||
int val = fcntl(fd, F_GETFL);
|
||||
|
||||
if(IS_HAS_ERROR(val))
|
||||
return FALSE;
|
||||
|
||||
val = bSet ? (val | fl) : (val & (~fl));
|
||||
|
||||
return IS_NO_ERROR(fcntl(fd, F_SETFL , val));
|
||||
}
|
||||
|
||||
void PrintStackTrace()
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
|
||||
const int MAX_SIZE = 51;
|
||||
void* arr[MAX_SIZE];
|
||||
|
||||
int size = backtrace(arr, MAX_SIZE);
|
||||
char** messages = backtrace_symbols(arr, size);
|
||||
|
||||
for(int i = 1; i < size && messages != nullptr; i++)
|
||||
{
|
||||
char* mangled_name = nullptr;
|
||||
char* offset_end = nullptr;
|
||||
const char* offset_begin = nullptr;
|
||||
|
||||
for(char* p = messages[i]; *p; ++p)
|
||||
{
|
||||
if(*p == '(')
|
||||
{
|
||||
mangled_name = p;
|
||||
}
|
||||
else if(*p == '+')
|
||||
{
|
||||
offset_begin = p;
|
||||
}
|
||||
else if(*p == ')')
|
||||
{
|
||||
offset_end = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mangled_name && offset_end && mangled_name < offset_end)
|
||||
{
|
||||
*mangled_name++ = 0;
|
||||
*offset_end++ = 0;
|
||||
|
||||
if(offset_begin == nullptr)
|
||||
offset_begin = "";
|
||||
else
|
||||
*(char*)offset_begin++ = 0;
|
||||
|
||||
while(*offset_end == ' ')
|
||||
++offset_end;
|
||||
|
||||
#ifdef __GNUC__
|
||||
int status;
|
||||
char* real_name = abi::__cxa_demangle(mangled_name, nullptr, nullptr, &status);
|
||||
|
||||
if(status == 0)
|
||||
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], real_name, offset_begin, offset_end);
|
||||
else
|
||||
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
|
||||
|
||||
if(real_name != nullptr)
|
||||
free(real_name);
|
||||
#else
|
||||
FPRINTLN(stderr, " -> [%02d] %s : (%s+%s) %s", i, messages[i], mangled_name, offset_begin, offset_end);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
FPRINTLN(stderr, " -> [%02d] %s", i, messages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(messages);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void __EXIT_FN_(void (*fn)(int), LPCSTR lpszFnName, int* lpiExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
|
||||
{
|
||||
if(iErrno >= 0)
|
||||
SetLastError(iErrno);
|
||||
else
|
||||
iErrno = GetLastError();
|
||||
|
||||
if(!lpszTitle)
|
||||
{
|
||||
lpszTitle = CreateLocalObjects(char, 64);
|
||||
|
||||
if(lpiExitCode)
|
||||
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s(%d) [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, *lpiExitCode, iErrno);
|
||||
else
|
||||
sprintf((LPSTR)lpszTitle, "(#%d, 0x%zX) > %s() [%d]", SELF_PROCESS_ID, (SIZE_T)SELF_THREAD_ID, lpszFnName, iErrno);
|
||||
}
|
||||
|
||||
if(lpszFile && iLine > 0)
|
||||
FPRINTLN(stderr, "%s : %s\n => %s (%d) : %s", lpszTitle, strerror(iErrno), lpszFile, iLine, lpszFunc ? lpszFunc : "");
|
||||
else
|
||||
FPRINTLN(stderr, "%s : %s", lpszTitle, strerror(iErrno));
|
||||
|
||||
if(lpiExitCode)
|
||||
fn(*lpiExitCode);
|
||||
else
|
||||
((void (*)())fn)();
|
||||
}
|
||||
|
||||
void EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
|
||||
{
|
||||
__EXIT_FN_(exit, "exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
|
||||
}
|
||||
|
||||
void _EXIT(int iExitCode, int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
|
||||
{
|
||||
__EXIT_FN_(_exit, "_exit", &iExitCode, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
|
||||
}
|
||||
|
||||
void ABORT(int iErrno, LPCSTR lpszFile, int iLine, LPCSTR lpszFunc, LPCSTR lpszTitle)
|
||||
{
|
||||
__EXIT_FN_((void (*)(int))abort, "abort", nullptr, iErrno, lpszFile, iLine, lpszFunc, lpszTitle);
|
||||
}
|
||||
|
||||
BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq)
|
||||
{
|
||||
UINT uiSequence = InterlockedIncrement(&vuiSeq);
|
||||
return SetThreadName(tid, lpszPrefix, uiSequence);
|
||||
}
|
||||
|
||||
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence)
|
||||
{
|
||||
int iMaxSeqLength = (int)(MAX_THREAD_NAME_LENGTH - lstrlen(lpszPrefix));
|
||||
|
||||
ASSERT(iMaxSeqLength > 0);
|
||||
|
||||
if(iMaxSeqLength <= 0)
|
||||
{
|
||||
::SetLastError(ERROR_OUT_OF_RANGE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ULONGLONG uiDiv = 1;
|
||||
|
||||
for(int i = 0; i < iMaxSeqLength; i++)
|
||||
uiDiv *= 10;
|
||||
|
||||
uiSequence = (UINT)(uiSequence % uiDiv);
|
||||
|
||||
CString strName;
|
||||
strName.Format(_T("%s%u"), lpszPrefix, uiSequence);
|
||||
|
||||
return SetThreadName(tid, strName);
|
||||
}
|
||||
|
||||
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName)
|
||||
{
|
||||
ASSERT(lstrlen(lpszName) <= MAX_THREAD_NAME_LENGTH);
|
||||
|
||||
if(tid == 0)
|
||||
tid = SELF_THREAD_ID;
|
||||
|
||||
int rs = pthread_setname_np(tid, CT2A(lpszName));
|
||||
|
||||
CHECK_ERROR_CODE(rs)
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
426
common/FuncHelper.h
Normal file
426
common/FuncHelper.h
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "hpsocket/GlobalErrno.h"
|
||||
#include "SysHelper.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sysexits.h>
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <malloc.h>
|
||||
#include <alloca.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
typedef atomic_ulong atomic_tid;
|
||||
|
||||
#define FPRINTLN(fd, fmt, ...) fprintf((fd), fmt "\n", ##__VA_ARGS__)
|
||||
#else
|
||||
typedef atomic_long atomic_tid;
|
||||
|
||||
#if defined(stdout)
|
||||
#undef stdout
|
||||
#endif
|
||||
|
||||
#if defined(stderr)
|
||||
#undef stderr
|
||||
#endif
|
||||
|
||||
#define stdout nullptr
|
||||
#define stderr nullptr
|
||||
|
||||
#define FPRINTLN(fd, fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define PRINTLN(fmt, ...) FPRINTLN(stdout, fmt, ##__VA_ARGS__)
|
||||
|
||||
#if defined(DEBUG) && defined(DEBUG_TRACE)
|
||||
#define TRACE(fmt, ...) PRINTLN("> TRC (0x%zX, %d) " fmt, (SIZE_T)SELF_THREAD_ID, SELF_NATIVE_THREAD_ID, ##__VA_ARGS__)
|
||||
#define ASSERT(expr) ((expr) ? TRUE : (::PrintStackTrace(), assert(FALSE), FALSE))
|
||||
#else
|
||||
#define TRACE(fmt, ...)
|
||||
#define ASSERT(expr) assert(expr)
|
||||
#endif
|
||||
|
||||
#define VERIFY(expr) ((expr) ? TRUE : (::PrintStackTrace(), ERROR_ABORT2(ERROR_VERIFY_CHECK), FALSE))
|
||||
#define ASSERT_IS_NO_ERROR(expr) ASSERT(IS_NO_ERROR(expr))
|
||||
#define VERIFY_IS_NO_ERROR(expr) VERIFY(IS_NO_ERROR(expr))
|
||||
#define ENSURE(expr) VERIFY(expr)
|
||||
#define ENSURE_IS_NO_ERROR(expr) VERIFY_IS_NO_ERROR(expr)
|
||||
|
||||
#define TEMP_FAILURE_RETRY_INT(exp) ((int)TEMP_FAILURE_RETRY(exp))
|
||||
|
||||
#define NO_EINTR TEMP_FAILURE_RETRY
|
||||
#define NO_EINTR_INT TEMP_FAILURE_RETRY_INT
|
||||
|
||||
#define CHECK_IS_OK(expr) {if(IS_NOT_OK(expr)) return FALSE;}
|
||||
#define CHECK_ERROR_FD(fd) {if(IS_INVALID_FD(fd)) return FALSE;}
|
||||
#define CHECK_ERROR_INVOKE(expr) {if(!IS_NO_ERROR(expr)) return FALSE;}
|
||||
#define CHECK_ERROR_CODE(rs) {if(!IS_NO_ERROR(rs)) {::SetLastError(rs); return FALSE;}}
|
||||
#define CHECK_ERROR(expr, code) {if(!(expr)) {::SetLastError(code); return FALSE;}}
|
||||
#define CHECK_EINVAL(expr) CHECK_ERROR(expr, ERROR_INVALID_PARAMETER)
|
||||
#define ASSERT_CHECK_ERROR(expr, code) {ASSERT(expr); CHECK_ERROR(expr, code);}
|
||||
#define ASSERT_CHECK_EINVAL(expr) {ASSERT(expr); CHECK_EINVAL(expr);}
|
||||
|
||||
#define SUCCEEDED(rs) IS_NO_ERROR(rs)
|
||||
#define FAILED(rs) (!SUCCEEDED(rs))
|
||||
#define IS_OK(rs) ((BOOL)(rs))
|
||||
#define IS_NOT_OK(rs) (!IS_OK(rs))
|
||||
|
||||
#define IS_ERROR(code) (::GetLastError() == (code))
|
||||
#define CONTINUE_IF_ERROR(code) {if(IS_ERROR(code)) continue;}
|
||||
#define BREAK_IF_ERROR(code) {if(IS_ERROR(code)) break;}
|
||||
|
||||
#define IS_WOULDBLOCK_ERROR() IS_ERROR(ERROR_WOULDBLOCK)
|
||||
#define CONTINUE_WOULDBLOCK_ERROR() CONTINUE_IF_ERROR(ERROR_WOULDBLOCK)
|
||||
#define BREAK_WOULDBLOCK_ERROR() BREAK_IF_ERROR(ERROR_WOULDBLOCK)
|
||||
#define IS_IO_PENDING_ERROR() IS_ERROR(ERROR_IO_PENDING)
|
||||
#define CONTINUE_IO_PENDING_ERROR() CONTINUE_IF_ERROR(ERROR_IO_PENDING)
|
||||
#define BREAK_IO_PENDING_ERROR() BREAK_IF_ERROR(ERROR_IO_PENDING)
|
||||
#define IS_INTR_ERROR() IS_ERROR(ERROR_INTR)
|
||||
#define CONTINUE_INTR_ERROR() CONTINUE_IF_ERROR(ERROR_INTR)
|
||||
#define BREAK_INTR_ERROR() BREAK_IF_ERROR(ERROR_INTR)
|
||||
|
||||
#define EqualMemory(dest, src, len) (!memcmp((dest), (src), (len)))
|
||||
#define MoveMemory(dest, src, len) memmove((dest), (src), (len))
|
||||
#define CopyMemory(dest, src, len) memcpy((dest), (src), (len))
|
||||
#define FillMemory(dest, len, ch) memset((dest), (ch), (len))
|
||||
#define ZeroMemory(dest, len) FillMemory((dest), (len), 0)
|
||||
#define ZeroObject(obj) ZeroMemory((&(obj)), sizeof(obj))
|
||||
|
||||
inline void SetLastError(int code) {errno = code;}
|
||||
inline int GetLastError() {return errno;}
|
||||
inline LPCSTR GetErrorStr(int code) {return strerror(code);}
|
||||
inline LPCSTR GetLastErrorStr() {return GetErrorStr(errno);}
|
||||
inline void PrintError(LPCSTR subject) {perror(subject);}
|
||||
|
||||
#define EXECUTE_RESET_ERROR(expr) (::SetLastError(0), (expr))
|
||||
#define EXECUTE_RESTORE_ERROR(expr) {int __le_ = ::GetLastError(); (expr); ::SetLastError(__le_);}
|
||||
#define EXECUTE_RESTORE_ERROR_RT(T, expr)\
|
||||
({int __le_ = ::GetLastError(); T __rs_ = (expr); ::SetLastError(__le_); __rs_;})
|
||||
#define ENSURE_ERROR(def_code) ({int __le_ = ::GetLastError(); if(__le_ == NO_ERROR) __le_ = (def_code); __le_;})
|
||||
#define ENSURE_ERROR_CANCELLED ENSURE_ERROR(ERROR_CANCELLED)
|
||||
#define TRIGGER(expr) EXECUTE_RESET_ERROR((expr))
|
||||
|
||||
#define _msize(p) malloc_usable_size(p)
|
||||
#define CreateLocalObjects(T, n) ((T*)alloca(sizeof(T) * (n)))
|
||||
#define CreateLocalObject(T) CreateLocalObjects(T, 1)
|
||||
#define CallocObjects(T, n) ((T*)calloc((n), sizeof(T)))
|
||||
|
||||
#define MALLOC(T, n) ((T*)malloc(sizeof(T) * (n)))
|
||||
#define REALLOC(T, p, n) ((T*)realloc((PVOID)(p), sizeof(T) * (n)))
|
||||
#define FREE(p) free((PVOID)(p))
|
||||
#define CALLOC(n, s) calloc((n), (s))
|
||||
|
||||
#define InterlockedExchangeAdd(p, n) __atomic_fetch_add((p), (n), memory_order_seq_cst)
|
||||
#define InterlockedExchangeSub(p, n) __atomic_fetch_sub((p), (n), memory_order_seq_cst)
|
||||
#define InterlockedAdd(p, n) __atomic_add_fetch((p), (n), memory_order_seq_cst)
|
||||
#define InterlockedSub(p, n) __atomic_sub_fetch((p), (n), memory_order_seq_cst)
|
||||
#define InterlockedIncrement(p) InterlockedAdd((p), 1)
|
||||
#define InterlockedDecrement(p) InterlockedSub((p), 1)
|
||||
|
||||
#define ERROR_EXIT2(code, err) EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
#define ERROR__EXIT2(code, err) _EXIT((code), (err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
#define ERROR_ABORT2(err) ABORT((err), __FILE__, __LINE__, __PRETTY_FUNCTION__)
|
||||
|
||||
#define ERROR_EXIT(code) ERROR_EXIT2((code), -1)
|
||||
#define ERROR__EXIT(code) ERROR__EXIT2((code), -1)
|
||||
#define ERROR_ABORT() ERROR_ABORT2(-1)
|
||||
|
||||
#define IS_VALID_FD(fd) ((fd) != INVALID_FD)
|
||||
#define IS_INVALID_FD(fd) (!IS_VALID_FD(fd))
|
||||
|
||||
#define IS_VALID_PVOID(pv) ((pv) != INVALID_PVOID)
|
||||
#define IS_INVALID_PVOID(pv) (!IS_VALID_PVOID(pv))
|
||||
|
||||
#define TO_PVOID(v) ((PVOID)(UINT_PTR)(v))
|
||||
#define FROM_PVOID(T, pv) ((T)(UINT_PTR)(pv))
|
||||
|
||||
#define IS_NULL(v) ((v) == nullptr)
|
||||
#define IS_NOT_NULL(v) (!IS_NULL(v))
|
||||
|
||||
#define stricmp strcasecmp
|
||||
#define strnicmp strncasecmp
|
||||
#define wcsicmp wcscasecmp
|
||||
#define wcsnicmp wcsncasecmp
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define tstrchr wcschr
|
||||
#define tstrrchr wcsrchr
|
||||
#define tstrstr wcsstr
|
||||
#define tstrpbrk wcspbrk
|
||||
#define tstrtok wcstok
|
||||
|
||||
#define stscanf swscanf
|
||||
#define tstrlen wcslen
|
||||
#define tstrcpy wcscpy
|
||||
#define tstrcmp wcscmp
|
||||
#define tstricmp wcsicmp
|
||||
#define tstrncpy wcsncpy
|
||||
#define tstrncmp wcsncmp
|
||||
#define tstrnicmp wcsnicmp
|
||||
#define tstrspn wcsspn
|
||||
#define tstrcspn wcscspn
|
||||
#define wsprintf swprintf
|
||||
#else
|
||||
#define tstrchr strchr
|
||||
#define tstrrchr strrchr
|
||||
#define tstrstr strstr
|
||||
#define tstrpbrk strpbrk
|
||||
#define tstrtok strtok_r
|
||||
|
||||
#define stscanf sscanf
|
||||
#define tstrlen strlen
|
||||
#define tstrcpy strcpy
|
||||
#define tstrcmp strcmp
|
||||
#define tstricmp stricmp
|
||||
#define tstrncpy strncpy
|
||||
#define tstrncmp strncmp
|
||||
#define tstrnicmp strnicmp
|
||||
#define tstrspn strspn
|
||||
#define tstrcspn strcspn
|
||||
#define wsprintf sprintf
|
||||
#endif
|
||||
|
||||
inline const char* StrChr(const char* s, char c) {return strchr(s, c);}
|
||||
inline const char* StrRChr(const char* s, char c) {return strrchr(s, c);}
|
||||
inline const char* StrStr(const char* h, const char* n) {return strstr(h, n);}
|
||||
inline const char* StrPBrk(const char* s, const char* a) {return strpbrk(s, a);}
|
||||
inline const wchar_t* StrChr(const wchar_t* s, wchar_t c) {return wcschr(s, c);}
|
||||
inline const wchar_t* StrRChr(const wchar_t* s, wchar_t c) {return wcsrchr(s, c);}
|
||||
inline const wchar_t* StrStr(const wchar_t* h, const wchar_t* n) {return wcsstr(h, n);}
|
||||
inline const wchar_t* StrPBrk(const wchar_t* s, const wchar_t* a) {return wcspbrk(s, a);}
|
||||
inline LPSTR StrSep2(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n") {LPSTR lpTok; while((lpTok = strsep(lpStr, lpDelim)) != nullptr && lpTok[0] == 0); return lpTok;}
|
||||
inline LPSTR TrimLeft(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n") {while((*lpStr)[0] != 0 && ::StrChr(lpDelim, (*lpStr)[0]) != nullptr) ++(*lpStr); return (*lpStr);}
|
||||
inline LPSTR TrimRitht(LPSTR* lpStr, LPCSTR lpDelim = " \t\r\n")
|
||||
{
|
||||
LPSTR lpEnd = (*lpStr) + strlen(*lpStr) - 1;
|
||||
LPSTR lpCur = lpEnd;
|
||||
|
||||
while(lpCur >= (*lpStr) && ::StrChr(lpDelim, lpCur[0]) != nullptr)
|
||||
--lpCur;
|
||||
|
||||
if(lpCur != lpEnd)
|
||||
lpCur[1] = 0;
|
||||
|
||||
return (*lpStr);
|
||||
}
|
||||
|
||||
inline BOOL IsStrEmptyA(LPCSTR lpsz) {return (lpsz == nullptr || lpsz[0] == 0);}
|
||||
inline BOOL IsStrEmptyW(LPCWSTR lpsz) {return (lpsz == nullptr || lpsz[0] == 0);}
|
||||
inline BOOL IsStrNotEmptyA(LPCSTR lpsz) {return !IsStrEmptyA(lpsz);}
|
||||
inline BOOL IsStrNotEmptyW(LPCWSTR lpsz){return !IsStrEmptyW(lpsz);}
|
||||
inline LPCSTR SafeStrA(LPCSTR lpsz) {return (lpsz != nullptr) ? lpsz : "";}
|
||||
inline LPCWSTR SafeStrW(LPCWSTR lpsz) {return (lpsz != nullptr) ? lpsz : L"";}
|
||||
|
||||
#ifdef _UNICODE
|
||||
#define IsStrEmpty(lpsz) IsStrEmptyW(lpsz)
|
||||
#define IsStrNotEmpty(lpsz) IsStrNotEmptyW(lpsz)
|
||||
#define SafeStr(lpsz) SafeStrW(lpsz)
|
||||
#else
|
||||
#define IsStrEmpty(lpsz) IsStrEmptyA(lpsz)
|
||||
#define IsStrNotEmpty(lpsz) IsStrNotEmptyA(lpsz)
|
||||
#define SafeStr(lpsz) SafeStrA(lpsz)
|
||||
#endif
|
||||
|
||||
inline int lstrlen(LPCTSTR p) {return (int)tstrlen(p);}
|
||||
inline LPTSTR lstrcpy(LPTSTR d, LPCTSTR s) {return tstrcpy(d, s);}
|
||||
inline LPTSTR lstrncpy(LPTSTR d, LPCTSTR s, size_t n) {return tstrncpy(d, s, n);}
|
||||
inline int lstrcmp(LPCTSTR s1, LPCTSTR s2) {return tstrcmp(s1, s2);}
|
||||
inline int lstrncmp(LPCTSTR s1, LPCTSTR s2, size_t n) {return tstrncmp(s1, s2, n);}
|
||||
inline int lstricmp(LPCTSTR s1, LPCTSTR s2) {return tstricmp(s1, s2);}
|
||||
inline int lstrnicmp(LPCTSTR s1, LPCTSTR s2, size_t n) {return tstrnicmp(s1, s2, n);}
|
||||
inline int lstrspn(LPCTSTR s, LPCTSTR accept) {return (int)tstrspn(s, accept);}
|
||||
inline int lstrcspn(LPCTSTR s, LPCTSTR accept) {return (int)tstrcspn(s, accept);}
|
||||
|
||||
template <typename T, size_t N> char (&_ArraySizeHelper(T(&arr)[N]))[N];
|
||||
template <typename T, size_t N> char (&_ArraySizeHelper(const T(&arr)[N]))[N];
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(_ArraySizeHelper(arr)))
|
||||
|
||||
#ifndef _countof
|
||||
#define _countof(arr) ARRAY_SIZE(arr)
|
||||
#endif
|
||||
|
||||
#ifndef __countof
|
||||
#define __countof(arr) ARRAY_SIZE(arr)
|
||||
#endif
|
||||
|
||||
#define THREAD_YIELD_CYCLE 63
|
||||
#define THREAD_SWITCH_CYCLE 4095
|
||||
|
||||
inline void YieldThread(UINT i = THREAD_YIELD_CYCLE)
|
||||
{
|
||||
if((i & THREAD_SWITCH_CYCLE) == THREAD_SWITCH_CYCLE)
|
||||
::SwitchToThread();
|
||||
else if((i & THREAD_YIELD_CYCLE) == THREAD_YIELD_CYCLE)
|
||||
::YieldProcessor();
|
||||
}
|
||||
|
||||
INT WaitFor(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
|
||||
INT Sleep(DWORD dwMillSecond, DWORD dwSecond = 0, BOOL bExceptThreadInterrupted = FALSE);
|
||||
|
||||
__time64_t _time64(time_t* ptm = nullptr);
|
||||
__time64_t _mkgmtime64(tm* ptm);
|
||||
tm* _gmtime64(tm* ptm, __time64_t* pt);
|
||||
|
||||
DWORD TimeGetTime();
|
||||
ULLONG TimeGetTime64();
|
||||
DWORD GetTimeGap32(DWORD dwOriginal, DWORD dwCurrent = 0);
|
||||
ULLONG GetTimeGap64(ULLONG ullOriginal, ULONGLONG ullCurrent = 0);
|
||||
LLONG TimevalToMillisecond(const timeval& tv);
|
||||
timeval& MillisecondToTimeval(LLONG ms, timeval& tv);
|
||||
LLONG TimespecToMillisecond(const timespec& ts);
|
||||
timespec& MillisecondToTimespec(LLONG ms, timespec& ts);
|
||||
timeval& GetFutureTimeval(LLONG ms, timeval& tv, struct timezone* ptz = nullptr);
|
||||
timespec& GetFutureTimespec(LLONG ms, timespec& ts, clockid_t clkid = CLOCK_MONOTONIC);
|
||||
|
||||
FD CreateTimer(LLONG llInterval, LLONG llStart = -1, BOOL bRealTimeClock = FALSE);
|
||||
BOOL ReadTimer(FD tmr, ULLONG* pVal = nullptr, BOOL* pRs = nullptr);
|
||||
|
||||
BOOL fcntl_SETFL(FD fd, INT fl, BOOL bSet = TRUE);
|
||||
|
||||
void PrintStackTrace();
|
||||
void EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
|
||||
void _EXIT(int iExitCode = 0, int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
|
||||
void ABORT(int iErrno = -1, LPCSTR lpszFile = nullptr, int iLine = 0, LPCSTR lpszFunc = nullptr, LPCSTR lpszTitle = nullptr);
|
||||
|
||||
/* ¹¤×÷Ïß³ÌÃû³Æ×î´ó³¤¶È */
|
||||
#define MAX_THREAD_NAME_LENGTH 15
|
||||
|
||||
BOOL SetSequenceThreadName(THR_ID tid, LPCTSTR lpszPrefix, volatile UINT& vuiSeq);
|
||||
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszPrefix, UINT uiSequence);
|
||||
BOOL SetThreadName(THR_ID tid, LPCTSTR lpszName);
|
||||
|
||||
template<typename T, typename = enable_if_t<is_integral<T>::value>>
|
||||
inline bool IS_INFINITE(T v)
|
||||
{
|
||||
return v == (T)INFINITE;
|
||||
}
|
||||
|
||||
template<typename T, typename = enable_if_t<is_integral<T>::value>>
|
||||
inline bool IS_HAS_ERROR(T v)
|
||||
{
|
||||
return v == (T)HAS_ERROR;
|
||||
}
|
||||
|
||||
template<typename T, typename = enable_if_t<is_integral<T>::value>>
|
||||
inline bool IS_NO_ERROR(T v)
|
||||
{
|
||||
return v == (T)NO_ERROR;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T InterlockedCompareExchange(volatile T* _Tgt, T _Value, T _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
|
||||
{
|
||||
__atomic_compare_exchange_n(_Tgt, &_Exp, _Value, _bWeek, m1, m2);
|
||||
return _Exp;
|
||||
}
|
||||
|
||||
template<typename T, typename V, typename E, typename = enable_if_t<is_same<decay_t<T>, decay_t<V>>::value && is_same<decay_t<V>, decay_t<E>>::value>>
|
||||
inline V* InterlockedCompareExchangePointer(volatile T** _Tgt, V* _Value, E* _Exp, BOOL _bWeek = FALSE, memory_order m1 = memory_order_seq_cst, memory_order m2 = memory_order_seq_cst)
|
||||
{
|
||||
return (V*)(ULONG_PTR)InterlockedCompareExchange((volatile ULONG_PTR*)(volatile PVOID*)_Tgt, (ULONG_PTR)(PVOID)_Value, (ULONG_PTR)(PVOID)_Exp, _bWeek, m1, m2);
|
||||
}
|
||||
|
||||
template<typename T, typename ... A>
|
||||
inline T* ConstructObject(T* p, A&& ... args)
|
||||
{
|
||||
return new (p) T(forward<A>(args) ...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void DestructObject(T* p)
|
||||
{
|
||||
p->T::~T();
|
||||
}
|
||||
|
||||
template<typename T1, typename T2, typename = enable_if_t<is_same<decay_t<T1>, decay_t<T2>>::value>>
|
||||
inline void CopyPlainObject(T1* p1, const T2* p2)
|
||||
{
|
||||
CopyMemory(p1, p2, sizeof(T1));
|
||||
}
|
||||
|
||||
template<typename T, typename C, typename = enable_if_t<is_integral<T>::value && (is_same<C, char>::value || is_same<C, wchar_t>::value)>>
|
||||
C* _n_2_c(T value, C* lpszDest, int radix)
|
||||
{
|
||||
static const C* dig = "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
bool neg = false;
|
||||
|
||||
if(is_signed<T>::value && value < 0)
|
||||
{
|
||||
value = -value;
|
||||
neg = true;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
do
|
||||
{
|
||||
lpszDest[n++] = dig[value % radix];
|
||||
value /= radix;
|
||||
} while(value);
|
||||
|
||||
if(neg) lpszDest[n++] = '-';
|
||||
lpszDest[n] = 0;
|
||||
|
||||
C c, *p, *q;
|
||||
for(p = lpszDest, q = p + n - 1; p < q; ++p, --q)
|
||||
c = *p, *p = *q, *q = c;
|
||||
|
||||
return lpszDest;
|
||||
}
|
||||
|
||||
#define itoa(v, p, r) _n_2_c<INT, char>((v), (p), (r))
|
||||
#define ltoa(v, p, r) _n_2_c<LONG, char>((v), (p), (r))
|
||||
#define lltoa(v, p, r) _n_2_c<LLONG, char>((v), (p), (r))
|
||||
#define uitoa(v, p, r) _n_2_c<UINT, char>((v), (p), (r))
|
||||
#define ultoa(v, p, r) _n_2_c<ULONG, char>((v), (p), (r))
|
||||
#define ulltoa(v, p, r) _n_2_c<ULLONG, char>((v), (p), (r))
|
||||
#define itow(v, p, r) _n_2_c<INT, wchar_t>((v), (p), (r))
|
||||
#define ltow(v, p, r) _n_2_c<LONG, wchar_t>((v), (p), (r))
|
||||
#define lltow(v, p, r) _n_2_c<LLONG, wchar_t>((v), (p), (r))
|
||||
#define uitow(v, p, r) _n_2_c<UINT, wchar_t>((v), (p), (r))
|
||||
#define ultow(v, p, r) _n_2_c<ULONG, wchar_t>((v), (p), (r))
|
||||
#define ulltow(v, p, r) _n_2_c<ULLONG, wchar_t>((v), (p), (r))
|
||||
|
||||
#define HEX_CHAR_TO_VALUE(c) (c <= '9' ? c - '0' : (c <= 'F' ? c - 'A' + 0x0A : c - 'a' + 0X0A))
|
||||
#define HEX_DOUBLE_CHAR_TO_VALUE(pc) ((BYTE)(((HEX_CHAR_TO_VALUE(*(pc))) << 4) | (HEX_CHAR_TO_VALUE(*((pc) + 1)))))
|
||||
#define HEX_VALUE_TO_CHAR(n) (n <= 9 ? n + '0' : (n <= 'F' ? n + 'A' - 0X0A : n + 'a' - 0X0A))
|
||||
#define HEX_VALUE_TO_DOUBLE_CHAR(pc, n) {*(pc) = (BYTE)HEX_VALUE_TO_CHAR((n >> 4)); *((pc) + 1) = (BYTE)HEX_VALUE_TO_CHAR((n & 0X0F));}
|
||||
40
common/GeneralHelper.h
Normal file
40
common/GeneralHelper.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "hpsocket/GlobalErrno.h"
|
||||
#include "Singleton.h"
|
||||
#include "STLHelper.h"
|
||||
#include "FuncHelper.h"
|
||||
#include "StringT.h"
|
||||
#include "SysHelper.h"
|
||||
#include "PrivateHeap.h"
|
||||
#include "Semaphore.h"
|
||||
#include "RWLock.h"
|
||||
#include "BufferPtr.h"
|
||||
#include "Event.h"
|
||||
#include "CriSec.h"
|
||||
#include "Thread.h"
|
||||
#include "SignalHandler.h"
|
||||
348
common/IODispatcher.cpp
Normal file
348
common/IODispatcher.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "IODispatcher.h"
|
||||
#include "FuncHelper.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <pthread.h>
|
||||
|
||||
volatile UINT CIODispatcher::sm_uiNum = MAXUINT;
|
||||
LPCTSTR CIODispatcher::WORKER_THREAD_PREFIX = _T("io-disp-");
|
||||
|
||||
BOOL CIODispatcher::Start(IIOHandler* pHandler, int iWorkerMaxEvents, int iWorkers)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(pHandler && iWorkerMaxEvents >= 0 && iWorkers >= 0);
|
||||
CHECK_ERROR(!HasStarted(), ERROR_INVALID_STATE);
|
||||
|
||||
if(iWorkerMaxEvents == 0) iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS;
|
||||
if(iWorkers == 0) iWorkers = DEFAULT_WORKER_THREAD_COUNT;
|
||||
|
||||
m_iMaxEvents = iWorkerMaxEvents;
|
||||
m_iWorkers = iWorkers;
|
||||
m_pHandler = pHandler;
|
||||
|
||||
m_evExit = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC | EFD_SEMAPHORE);
|
||||
|
||||
if(IS_INVALID_FD(m_evExit))
|
||||
goto START_ERROR;
|
||||
|
||||
m_pContexts = make_unique<TDispContext[]>(m_iWorkers);
|
||||
|
||||
for(int i = 0; i < m_iWorkers; i++)
|
||||
{
|
||||
TDispContext& ctx = m_pContexts[i];
|
||||
|
||||
ctx.m_iIndex = i;
|
||||
|
||||
ctx.m_epoll = epoll_create1(EPOLL_CLOEXEC);
|
||||
CHECK_ERROR_FD(ctx.m_epoll);
|
||||
|
||||
ctx.m_evCmd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
|
||||
if(IS_INVALID_FD(ctx.m_evCmd))
|
||||
goto START_ERROR;
|
||||
|
||||
if(!VERIFY(AddFD(i, ctx.m_evCmd, EPOLLIN | EPOLLET, &ctx.m_evCmd)))
|
||||
goto START_ERROR;
|
||||
|
||||
if(!VERIFY(AddFD(i, m_evExit, EPOLLIN, &m_evExit)))
|
||||
goto START_ERROR;
|
||||
|
||||
sigset_t ss;
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGPIPE);
|
||||
|
||||
VERIFY_IS_NO_ERROR(pthread_sigmask(SIG_BLOCK, &ss, nullptr));
|
||||
|
||||
ctx.m_pWorker = make_unique<CWorkerThread>();
|
||||
|
||||
if(!VERIFY(ctx.m_pWorker->Start(this, &CIODispatcher::WorkerProc, &ctx)))
|
||||
goto START_ERROR;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
START_ERROR:
|
||||
EXECUTE_RESTORE_ERROR(Stop(FALSE));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::Stop(BOOL bCheck)
|
||||
{
|
||||
if(bCheck) CHECK_ERROR(HasStarted(), ERROR_INVALID_STATE);
|
||||
|
||||
BOOL isOK = TRUE;
|
||||
|
||||
if(m_pContexts)
|
||||
{
|
||||
isOK &= IS_NO_ERROR(eventfd_write(m_evExit, m_iWorkers));
|
||||
|
||||
for(int i = 0; i < m_iWorkers; i++)
|
||||
{
|
||||
TDispContext& ctx = m_pContexts[i];
|
||||
|
||||
if(ctx.m_pWorker)
|
||||
isOK &= ctx.m_pWorker->Join();
|
||||
|
||||
if(!ctx.m_queue.IsEmpty())
|
||||
{
|
||||
TDispCommand* pCmd = nullptr;
|
||||
|
||||
while(ctx.m_queue.PopFront(&pCmd))
|
||||
TDispCommand::Destruct(pCmd);
|
||||
|
||||
VERIFY(ctx.m_queue.IsEmpty());
|
||||
}
|
||||
|
||||
if(IS_VALID_FD(ctx.m_evCmd))
|
||||
isOK &= IS_NO_ERROR(close(ctx.m_evCmd));
|
||||
|
||||
if(IS_VALID_FD(ctx.m_epoll))
|
||||
isOK &= IS_NO_ERROR(close(ctx.m_epoll));
|
||||
}
|
||||
}
|
||||
|
||||
if(IS_VALID_FD(m_evExit))
|
||||
isOK &= IS_NO_ERROR(close(m_evExit));
|
||||
|
||||
Reset();
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
VOID CIODispatcher::Reset()
|
||||
{
|
||||
m_uiSeq = MAXUINT;
|
||||
m_iWorkers = 0;
|
||||
m_iMaxEvents= 0;
|
||||
m_evExit = INVALID_FD;
|
||||
m_pHandler = nullptr;
|
||||
m_pContexts = nullptr;
|
||||
}
|
||||
|
||||
VOID CIODispatcher::MakePrefix()
|
||||
{
|
||||
UINT uiNumber = ::InterlockedIncrement(&sm_uiNum);
|
||||
|
||||
m_strPrefix.Format(_T("%s%u-"), WORKER_THREAD_PREFIX, uiNumber);
|
||||
}
|
||||
|
||||
TDispContext& CIODispatcher::GetContext(int idx, FD fd)
|
||||
{
|
||||
if(idx < 0) idx = fd;
|
||||
ASSERT(idx >= 0);
|
||||
if(idx >= m_iWorkers) idx %= m_iWorkers;
|
||||
|
||||
return m_pContexts[idx];
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::SendCommandByIndex(int idx, USHORT t, UINT_PTR wp, UINT_PTR lp)
|
||||
{
|
||||
return SendCommandByIndex(idx, TDispCommand::Construct(t, wp, lp));
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::SendCommandByIndex(int idx, TDispCommand* pCmd)
|
||||
{
|
||||
TDispContext& ctx = GetContextByIndex(idx);
|
||||
return SendCommand(ctx, pCmd);
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::SendCommandByFD(FD fd, USHORT t, UINT_PTR wp, UINT_PTR lp)
|
||||
{
|
||||
return SendCommandByFD(fd, TDispCommand::Construct(t, wp, lp));
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::SendCommandByFD(FD fd, TDispCommand* pCmd)
|
||||
{
|
||||
TDispContext& ctx = GetContextByFD(fd);
|
||||
return SendCommand(ctx, pCmd);
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::SendCommand(TDispContext& ctx, TDispCommand* pCmd)
|
||||
{
|
||||
ctx.m_queue.PushBack(pCmd);
|
||||
return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, 1));
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv)
|
||||
{
|
||||
const TDispContext& ctx = GetContext(idx, fd);
|
||||
|
||||
epoll_event evt = {mask, pv};
|
||||
return IS_NO_ERROR(epoll_ctl(ctx.m_epoll, op, fd, &evt));
|
||||
}
|
||||
|
||||
int CIODispatcher::WorkerProc(TDispContext* pContext)
|
||||
{
|
||||
::SetSequenceThreadName(SELF_THREAD_ID, m_strPrefix, m_uiSeq);
|
||||
|
||||
m_pHandler->OnDispatchThreadStart(SELF_THREAD_ID);
|
||||
|
||||
BOOL bRun = TRUE;
|
||||
unique_ptr<epoll_event[]> pEvents = make_unique<epoll_event[]>(m_iMaxEvents);
|
||||
|
||||
while(bRun)
|
||||
{
|
||||
int rs = NO_EINTR_INT(epoll_pwait(pContext->m_epoll, pEvents.get(), m_iMaxEvents, INFINITE, nullptr));
|
||||
|
||||
if(rs <= TIMEOUT)
|
||||
ERROR_ABORT();
|
||||
|
||||
for(int i = 0; i < rs; i++)
|
||||
{
|
||||
UINT events = pEvents[i].events;
|
||||
PVOID ptr = pEvents[i].data.ptr;
|
||||
|
||||
if(ptr == &pContext->m_evCmd)
|
||||
ProcessCommand(pContext, events);
|
||||
else if(ptr == &m_evExit)
|
||||
bRun = ProcessExit(pContext, events);
|
||||
else
|
||||
ProcessIo(pContext, ptr, events);
|
||||
}
|
||||
}
|
||||
|
||||
m_pHandler->OnDispatchThreadEnd(SELF_THREAD_ID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::ProcessCommand(TDispContext* pContext, UINT events)
|
||||
{
|
||||
if(events & _EPOLL_ALL_ERROR_EVENTS)
|
||||
ERROR_ABORT();
|
||||
|
||||
if(!(events & EPOLLIN))
|
||||
return FALSE;
|
||||
|
||||
BOOL isOK = TRUE;
|
||||
|
||||
eventfd_t v;
|
||||
|
||||
int rs = eventfd_read(pContext->m_evCmd, &v);
|
||||
|
||||
if(IS_NO_ERROR(rs))
|
||||
{
|
||||
ASSERT(v > 0);
|
||||
|
||||
TDispCommand* pCmd = nullptr;
|
||||
|
||||
while(pContext->m_queue.PopFront(&pCmd))
|
||||
{
|
||||
m_pHandler->OnCommand(pContext, pCmd);
|
||||
TDispCommand::Destruct(pCmd);
|
||||
}
|
||||
}
|
||||
else if(IS_HAS_ERROR(rs))
|
||||
{
|
||||
ASSERT(IS_WOULDBLOCK_ERROR());
|
||||
|
||||
isOK = FALSE;
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::ProcessExit(const TDispContext* pContext, UINT events)
|
||||
{
|
||||
if(events & _EPOLL_ALL_ERROR_EVENTS)
|
||||
ERROR_ABORT();
|
||||
|
||||
if(!(events & EPOLLIN))
|
||||
return TRUE;
|
||||
|
||||
BOOL bRun = TRUE;
|
||||
|
||||
eventfd_t v;
|
||||
|
||||
int rs = eventfd_read(m_evExit, &v);
|
||||
|
||||
if(IS_HAS_ERROR(rs))
|
||||
ASSERT(IS_WOULDBLOCK_ERROR());
|
||||
else
|
||||
{
|
||||
ASSERT(v == 1);
|
||||
bRun = FALSE;
|
||||
}
|
||||
|
||||
return bRun;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::ProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
if(!m_pHandler->OnBeforeProcessIo(pContext, pv, events))
|
||||
return FALSE;
|
||||
|
||||
BOOL rs = DoProcessIo(pContext, pv, events);
|
||||
m_pHandler->OnAfterProcessIo(pContext, pv, events, rs);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events)
|
||||
{
|
||||
if(events & EPOLLERR)
|
||||
return m_pHandler->OnError(pContext, pv, events);
|
||||
if((events & EPOLLPRI) && !m_pHandler->OnReadyPrivilege(pContext, pv, events))
|
||||
return FALSE;
|
||||
if((events & EPOLLIN) && !m_pHandler->OnReadyRead(pContext, pv, events))
|
||||
return FALSE;
|
||||
if((events & EPOLLOUT) && !m_pHandler->OnReadyWrite(pContext, pv, events))
|
||||
return FALSE;
|
||||
if((events & (_EPOLL_HUNGUP_EVENTS)) && !m_pHandler->OnHungUp(pContext, pv, events))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
FD CIODispatcher::AddTimer(int idx, LLONG llInterval, PVOID pv)
|
||||
{
|
||||
FD fdTimer = ::CreateTimer(llInterval);
|
||||
|
||||
if(IS_VALID_FD(fdTimer))
|
||||
{
|
||||
if(!AddFD(idx, fdTimer, EPOLLIN | EPOLLET, pv))
|
||||
{
|
||||
close(fdTimer);
|
||||
fdTimer = INVALID_FD;
|
||||
}
|
||||
}
|
||||
|
||||
return fdTimer;
|
||||
}
|
||||
|
||||
BOOL CIODispatcher::DelTimer(int idx, FD fdTimer)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
if(IS_VALID_FD(fdTimer))
|
||||
{
|
||||
if(DelFD(idx, fdTimer))
|
||||
isOK = TRUE;
|
||||
|
||||
close(fdTimer);
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
272
common/IODispatcher.h
Normal file
272
common/IODispatcher.h
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Singleton.h"
|
||||
#include "RingBuffer.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define _EPOLL_READ_PRI_EVENTS (EPOLLPRI | EPOLLRDHUP)
|
||||
#define _EPOLL_READ_EVENTS (EPOLLIN | EPOLLRDHUP)
|
||||
#define _EPOLL_ALL_READ_EVENTS (_EPOLL_READ_EVENTS | _EPOLL_READ_PRI_EVENTS)
|
||||
#define _EPOLL_WRITE_EVENTS (EPOLLOUT)
|
||||
#define _EPOLL_NORMAL_RW_EVENTS (_EPOLL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
|
||||
#define _EPOLL_ALL_RW_EVENTS (_EPOLL_ALL_READ_EVENTS | _EPOLL_WRITE_EVENTS)
|
||||
#define _EPOLL_ERROR_EVENTS (EPOLLERR)
|
||||
#define _EPOLL_HUNGUP_EVENTS (EPOLLHUP | EPOLLRDHUP)
|
||||
#define _EPOLL_ALL_ERROR_EVENTS (_EPOLL_ERROR_EVENTS | _EPOLL_HUNGUP_EVENTS)
|
||||
#define _EPOLL_ALL_NORMAL_EVENTS (_EPOLL_NORMAL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
|
||||
#define _EPOLL_ALL_EVENTS (_EPOLL_ALL_RW_EVENTS | _EPOLL_ALL_ERROR_EVENTS)
|
||||
|
||||
#define DISP_EVENT_FLAG_R 0x1
|
||||
#define DISP_EVENT_FLAG_W 0x2
|
||||
#define DISP_EVENT_FLAG_H 0x4
|
||||
|
||||
#define RETRIVE_EVENT_FLAG_R(evt) ((evt) & (_EPOLL_ALL_READ_EVENTS) ? DISP_EVENT_FLAG_R : 0)
|
||||
#define RETRIVE_EVENT_FLAG_W(evt) ((evt) & (_EPOLL_WRITE_EVENTS) ? DISP_EVENT_FLAG_W : 0)
|
||||
#define RETRIVE_EVENT_FLAG_RW(evt) (RETRIVE_EVENT_FLAG_R(evt) | RETRIVE_EVENT_FLAG_W(evt))
|
||||
#define RETRIVE_EVENT_FLAG_H(evt) ((evt) & (_EPOLL_HUNGUP_EVENTS) ? DISP_EVENT_FLAG_H : 0)
|
||||
|
||||
#ifndef EPOLLEXCLUSIVE
|
||||
#define EPOLLEXCLUSIVE (1u << 28)
|
||||
#endif
|
||||
|
||||
#define MAYBE_EPOLLEXCLUSIVE (::IsKernelVersionAbove(4, 5, 0) ? EPOLLEXCLUSIVE : 0)
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
struct TDispCommand;
|
||||
class CIODispatcher;
|
||||
|
||||
struct TDispContext
|
||||
{
|
||||
friend class CIODispatcher;
|
||||
|
||||
using CCommandQueue = CCASQueue<TDispCommand>;
|
||||
using CWorkerThread = CThread<CIODispatcher, TDispContext, int>;
|
||||
|
||||
public:
|
||||
int GetIndex() const {return m_iIndex;}
|
||||
THR_ID GetThreadId() const {return m_pWorker != nullptr ? m_pWorker->GetThreadID() : 0;}
|
||||
|
||||
public:
|
||||
TDispContext() {Reset();}
|
||||
~TDispContext() = default;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(TDispContext)
|
||||
|
||||
private:
|
||||
VOID Reset()
|
||||
{
|
||||
m_iIndex = -1;
|
||||
m_epoll = INVALID_FD;
|
||||
m_evCmd = INVALID_FD;
|
||||
m_pWorker = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_iIndex;
|
||||
FD m_epoll;
|
||||
FD m_evCmd;
|
||||
|
||||
CCommandQueue m_queue;
|
||||
unique_ptr<CWorkerThread> m_pWorker;
|
||||
};
|
||||
|
||||
struct TDispCommand
|
||||
{
|
||||
USHORT type;
|
||||
UINT_PTR wParam;
|
||||
UINT_PTR lParam;
|
||||
|
||||
static TDispCommand* Construct(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
|
||||
{return new TDispCommand(t, wp, lp);}
|
||||
|
||||
static VOID Destruct(TDispCommand* p)
|
||||
{if(p) delete p;}
|
||||
|
||||
private:
|
||||
TDispCommand(USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0)
|
||||
: type(t), wParam(wp), lParam(lp)
|
||||
{
|
||||
}
|
||||
|
||||
~TDispCommand() = default;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
class IIOHandler
|
||||
{
|
||||
public:
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) = 0;
|
||||
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) = 0;
|
||||
virtual BOOL OnReadyRead(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events) = 0;
|
||||
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) = 0;
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) = 0;
|
||||
|
||||
public:
|
||||
virtual ~IIOHandler() = default;
|
||||
};
|
||||
|
||||
class CIOHandler : public IIOHandler
|
||||
{
|
||||
public:
|
||||
virtual VOID OnCommand(const TDispContext* pContext, TDispCommand* pCmd) override {}
|
||||
|
||||
virtual BOOL OnBeforeProcessIo(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
|
||||
virtual VOID OnAfterProcessIo(const TDispContext* pContext, PVOID pv, UINT events, BOOL rs) override {}
|
||||
virtual BOOL OnReadyWrite(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
|
||||
virtual BOOL OnHungUp(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
|
||||
virtual BOOL OnError(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
|
||||
virtual BOOL OnReadyPrivilege(const TDispContext* pContext, PVOID pv, UINT events) override {return TRUE;}
|
||||
|
||||
virtual VOID OnDispatchThreadStart(THR_ID tid) override {}
|
||||
virtual VOID OnDispatchThreadEnd(THR_ID tid) override {}
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------------------------------------------------------------- //
|
||||
|
||||
class CIODispatcher
|
||||
{
|
||||
public:
|
||||
static const int DEF_WORKER_MAX_EVENTS = 64;
|
||||
|
||||
using CCommandQueue = TDispContext::CCommandQueue;
|
||||
using CWorkerThread = TDispContext::CWorkerThread;
|
||||
|
||||
public:
|
||||
BOOL Start(IIOHandler* pHandler, int iWorkerMaxEvents = DEF_WORKER_MAX_EVENTS, int iWorkers = 0);
|
||||
BOOL Stop(BOOL bCheck = TRUE);
|
||||
|
||||
BOOL SendCommandByIndex(int idx, TDispCommand* pCmd);
|
||||
BOOL SendCommandByIndex(int idx, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
|
||||
BOOL SendCommandByFD(FD fd, TDispCommand* pCmd);
|
||||
BOOL SendCommandByFD(FD fd, USHORT t, UINT_PTR wp = 0, UINT_PTR lp = 0);
|
||||
BOOL SendCommand(TDispContext& ctx, TDispCommand* pCmd);
|
||||
|
||||
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
|
||||
BOOL SendCommandsByIndex(int idx, const _List& cmds)
|
||||
{
|
||||
TDispContext& ctx = GetContextByIndex(idx);
|
||||
return SendCommands(ctx, cmds);
|
||||
}
|
||||
|
||||
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
|
||||
BOOL SendCommandsByFD(FD fd, const _List& cmds)
|
||||
{
|
||||
TDispContext& ctx = GetContextByFD(fd);
|
||||
return SendCommands(ctx, cmds);
|
||||
}
|
||||
|
||||
template<class _List, typename = enable_if_t<is_same<remove_reference_t<typename _List::reference>, TDispCommand*>::value>>
|
||||
BOOL SendCommands(TDispContext& ctx, const _List& cmds)
|
||||
{
|
||||
size_t size = cmds.size();
|
||||
if(size == 0) return FALSE;
|
||||
|
||||
for(auto it = cmds.begin(), end = cmds.end(); it != end; ++it)
|
||||
ctx.m_queue.PushBack(*it);
|
||||
|
||||
return VERIFY_IS_NO_ERROR(eventfd_write(ctx.m_evCmd, size));
|
||||
}
|
||||
|
||||
BOOL AddFD(int idx, FD fd, UINT mask, PVOID pv) {return CtlFD(idx, fd, EPOLL_CTL_ADD, mask, pv);}
|
||||
BOOL ModFD(int idx, FD fd, UINT mask, PVOID pv) {return CtlFD(idx, fd, EPOLL_CTL_MOD, mask, pv);}
|
||||
BOOL DelFD(int idx, FD fd) {return CtlFD(idx, fd, EPOLL_CTL_DEL, 0, nullptr);}
|
||||
BOOL CtlFD(int idx, FD fd, int op, UINT mask, PVOID pv);
|
||||
|
||||
|
||||
BOOL AddFD(FD fd, UINT mask, PVOID pv) {return CtlFD(-1, fd, EPOLL_CTL_ADD, mask, pv);}
|
||||
BOOL ModFD(FD fd, UINT mask, PVOID pv) {return CtlFD(-1, fd, EPOLL_CTL_MOD, mask, pv);}
|
||||
BOOL DelFD(FD fd) {return CtlFD(-1, fd, EPOLL_CTL_DEL, 0, nullptr);}
|
||||
BOOL CtlFD(FD fd, int op, UINT mask, PVOID pv) {return CtlFD(-1, fd, op, mask, pv);}
|
||||
|
||||
BOOL ProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
|
||||
|
||||
FD AddTimer (int idx, LLONG llInterval, PVOID pv);
|
||||
BOOL DelTimer (int idx, FD fdTimer);
|
||||
|
||||
FD AddTimer (LLONG llInterval, PVOID pv) {return AddTimer(-1, llInterval, pv);}
|
||||
BOOL DelTimer (FD fdTimer) {return DelTimer(-1, fdTimer);}
|
||||
|
||||
private:
|
||||
int WorkerProc(TDispContext* pContext);
|
||||
BOOL ProcessExit(const TDispContext* pContext, UINT events);
|
||||
BOOL ProcessCommand(TDispContext* pContext, UINT events);
|
||||
BOOL DoProcessIo(const TDispContext* pContext, PVOID pv, UINT events);
|
||||
|
||||
VOID Reset();
|
||||
VOID MakePrefix();
|
||||
|
||||
TDispContext& GetContextByIndex(int idx) {return GetContext(idx, -1);}
|
||||
TDispContext& GetContextByFD(FD fd) {return GetContext(-1, fd);}
|
||||
TDispContext& GetContext(int idx, FD fd);
|
||||
|
||||
public:
|
||||
const TDispContext& GetContextRefByIndex(int idx) {return GetContextByIndex(idx);}
|
||||
const TDispContext& GetContextRefByFD(FD fd) {return GetContextByFD(fd);}
|
||||
const TDispContext& GetContextRef(int idx, FD fd) {return GetContext(idx, fd);}
|
||||
|
||||
BOOL HasStarted() {return m_pHandler && m_pContexts;}
|
||||
int GetWorkers() {return m_iWorkers;}
|
||||
const TDispContext* GetContexts() {return m_pContexts.get();}
|
||||
|
||||
CIODispatcher() {MakePrefix(); Reset();}
|
||||
~CIODispatcher() {if(HasStarted()) Stop();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CIODispatcher)
|
||||
|
||||
private:
|
||||
static LPCTSTR WORKER_THREAD_PREFIX;
|
||||
static volatile UINT sm_uiNum;
|
||||
|
||||
volatile UINT m_uiSeq;
|
||||
CString m_strPrefix;
|
||||
|
||||
private:
|
||||
int m_iWorkers;
|
||||
int m_iMaxEvents;
|
||||
|
||||
FD m_evExit;
|
||||
|
||||
IIOHandler* m_pHandler;
|
||||
unique_ptr<TDispContext[]> m_pContexts;
|
||||
};
|
||||
62
common/PollHelper.cpp
Normal file
62
common/PollHelper.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "PollHelper.h"
|
||||
#include "FuncHelper.h"
|
||||
|
||||
long PollForSingleObject(pollfd& pfd, long lTimeout, const sigset_t* pSigSet)
|
||||
{
|
||||
return PollForMultipleObjects(&pfd, 1, lTimeout, pSigSet);
|
||||
}
|
||||
|
||||
long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout, const sigset_t* pSigSet)
|
||||
{
|
||||
ASSERT(iCount > 0 && iCount < (int)(sizeof(LONG) * 8));
|
||||
|
||||
timespec* pts = nullptr;
|
||||
|
||||
if(!IS_INFINITE(lTimeout))
|
||||
{
|
||||
pts = CreateLocalObject(timespec);
|
||||
::MillisecondToTimespec(lTimeout, *pts);
|
||||
}
|
||||
|
||||
while(TRUE)
|
||||
{
|
||||
int rs = NO_EINTR_INT(ppoll(pfds, iCount, pts, pSigSet));
|
||||
|
||||
if(rs <= TIMEOUT) return rs;
|
||||
|
||||
LONG lValue = 0L;
|
||||
|
||||
for(int i = 0; i < iCount; i++)
|
||||
{
|
||||
pollfd& pfd = pfds[i];
|
||||
|
||||
if(pfd.revents & _POLL_ALL_EVENTS)
|
||||
lValue |= (1 << i);
|
||||
}
|
||||
|
||||
return lValue;
|
||||
}
|
||||
}
|
||||
51
common/PollHelper.h
Normal file
51
common/PollHelper.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
|
||||
#if __USE_GNU
|
||||
#define _POLL_READ_PRI_EVENTS (POLLPRI | POLLRDHUP)
|
||||
#define _POLL_READ_EVENTS (POLLIN | POLLRDHUP)
|
||||
#define _POLL_HUNGUP_EVENTS (POLLHUP | POLLRDHUP)
|
||||
#else
|
||||
#define _POLL_READ_PRI_EVENTS (POLLPRI)
|
||||
#define _POLL_READ_EVENTS (POLLIN)
|
||||
#define _POLL_HUNGUP_EVENTS (POLLHUP)
|
||||
#endif
|
||||
|
||||
#define _POLL_ALL_READ_EVENTS (_POLL_READ_EVENTS | _POLL_READ_PRI_EVENTS)
|
||||
#define _POLL_WRITE_EVENTS (POLLOUT)
|
||||
#define _POLL_NORMAL_RW_EVENTS (_POLL_READ_EVENTS | _POLL_WRITE_EVENTS)
|
||||
#define _POLL_ALL_RW_EVENTS (_POLL_ALL_READ_EVENTS | _POLL_WRITE_EVENTS)
|
||||
#define _POLL_ERROR_EVENTS (POLLERR | POLLNVAL)
|
||||
#define _POLL_ALL_ERROR_EVENTS (_POLL_ERROR_EVENTS | _POLL_HUNGUP_EVENTS)
|
||||
#define _POLL_ALL_NORMAL_EVENTS (_POLL_NORMAL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
|
||||
#define _POLL_ALL_EVENTS (_POLL_ALL_RW_EVENTS | _POLL_ALL_ERROR_EVENTS)
|
||||
|
||||
long PollForSingleObject(pollfd& pfd, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);
|
||||
long PollForMultipleObjects(pollfd pfds[], int iCount, long lTimeout = INFINITE, const sigset_t* pSigSet = nullptr);
|
||||
152
common/PrivateHeap.h
Normal file
152
common/PrivateHeap.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Singleton.h"
|
||||
|
||||
#include <malloc.h>
|
||||
|
||||
#define HEAP_ZERO_MEMORY 0x08
|
||||
|
||||
class CGlobalHeapImpl
|
||||
{
|
||||
public:
|
||||
PVOID Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
|
||||
{
|
||||
PVOID pv = malloc(dwSize);
|
||||
|
||||
if(!pv)
|
||||
throw std::bad_alloc();
|
||||
|
||||
if(dwFlags & HEAP_ZERO_MEMORY)
|
||||
ZeroMemory(pv, dwSize);
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
PVOID ReAlloc(PVOID pvMemory, SIZE_T dwSize, DWORD dwFlags = 0)
|
||||
{
|
||||
PVOID pv = realloc(pvMemory, dwSize);
|
||||
|
||||
if(!pv)
|
||||
{
|
||||
if(pvMemory)
|
||||
free(pvMemory);
|
||||
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
|
||||
if(dwFlags & HEAP_ZERO_MEMORY)
|
||||
ZeroMemory(pv, dwSize);
|
||||
|
||||
return pv;
|
||||
}
|
||||
|
||||
BOOL Free(PVOID pvMemory, DWORD dwFlags = 0)
|
||||
{
|
||||
if(pvMemory)
|
||||
{
|
||||
free(pvMemory);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SIZE_T Compact (DWORD dwFlags = 0) {return -1;}
|
||||
SIZE_T Size (PVOID pvMemory, DWORD dwFlags = 0) {return _msize(pvMemory);}
|
||||
|
||||
BOOL IsValid() {return TRUE;}
|
||||
BOOL Reset() {return TRUE;}
|
||||
|
||||
public:
|
||||
CGlobalHeapImpl (DWORD dwOptions = 0, SIZE_T dwInitSize = 0, SIZE_T dwMaxSize = 0) {}
|
||||
~CGlobalHeapImpl() {}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CGlobalHeapImpl)
|
||||
};
|
||||
|
||||
#if !defined (_USE_CUSTOM_PRIVATE_HEAP)
|
||||
using CPrivateHeap = CGlobalHeapImpl;
|
||||
#endif
|
||||
|
||||
template<class T> class CPrivateHeapBuffer
|
||||
{
|
||||
public:
|
||||
CPrivateHeapBuffer(CPrivateHeap& hpPrivate, SIZE_T dwSize = 0)
|
||||
: m_hpPrivate (hpPrivate)
|
||||
, m_pvMemory (nullptr)
|
||||
{
|
||||
ASSERT(m_hpPrivate.IsValid());
|
||||
Alloc(dwSize);
|
||||
}
|
||||
|
||||
~CPrivateHeapBuffer() {Free();}
|
||||
|
||||
public:
|
||||
|
||||
T* Alloc(SIZE_T dwSize, DWORD dwFlags = 0)
|
||||
{
|
||||
if(IsValid())
|
||||
Free();
|
||||
|
||||
if(dwSize > 0)
|
||||
m_pvMemory = (T*)m_hpPrivate.Alloc(dwSize * sizeof(T), dwFlags);
|
||||
|
||||
return m_pvMemory;
|
||||
}
|
||||
|
||||
T* ReAlloc(SIZE_T dwSize, DWORD dwFlags = 0)
|
||||
{return m_pvMemory = (T*)m_hpPrivate.ReAlloc(m_pvMemory, dwSize * sizeof(T), dwFlags);}
|
||||
|
||||
SIZE_T Size(DWORD dwFlags = 0)
|
||||
{return m_hpPrivate.Size(m_pvMemory, dwFlags) / sizeof(T);}
|
||||
|
||||
BOOL Free(DWORD dwFlags = 0)
|
||||
{
|
||||
BOOL isOK = TRUE;
|
||||
|
||||
if(IsValid())
|
||||
{
|
||||
isOK = m_hpPrivate.Free(m_pvMemory, dwFlags);
|
||||
m_pvMemory = nullptr;
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL IsValid() {return m_pvMemory != nullptr;}
|
||||
operator T* () const {return m_pvMemory;}
|
||||
T& operator [] (int i) const {return *(m_pvMemory + i);}
|
||||
|
||||
private:
|
||||
CPrivateHeap& m_hpPrivate;
|
||||
T* m_pvMemory;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CPrivateHeapBuffer)
|
||||
};
|
||||
|
||||
using CPrivateHeapByteBuffer = CPrivateHeapBuffer<BYTE>;
|
||||
using CPrivateHeapStrBuffer = CPrivateHeapBuffer<TCHAR>;
|
||||
228
common/RWLock.cpp
Normal file
228
common/RWLock.cpp
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "RWLock.h"
|
||||
|
||||
CMutexRWLock::CMutexRWLock()
|
||||
: m_nActive (0)
|
||||
, m_dwWriterTID (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CMutexRWLock::~CMutexRWLock()
|
||||
{
|
||||
ASSERT(m_nActive == 0);
|
||||
ASSERT(m_dwWriterTID == 0);
|
||||
}
|
||||
|
||||
VOID CMutexRWLock::WaitToRead()
|
||||
{
|
||||
BOOL bWait = FALSE;
|
||||
|
||||
{
|
||||
CSpinLock locallock(m_cs);
|
||||
|
||||
if(m_nActive > 0)
|
||||
++m_nActive;
|
||||
else if(m_nActive == 0)
|
||||
{
|
||||
if(m_mtx.try_lock_shared())
|
||||
++m_nActive;
|
||||
else
|
||||
bWait = TRUE;
|
||||
}
|
||||
else if(!IsOwner())
|
||||
bWait = TRUE;
|
||||
}
|
||||
|
||||
if(bWait)
|
||||
{
|
||||
m_mtx.lock_shared();
|
||||
|
||||
CSpinLock locallock(m_cs);
|
||||
++m_nActive;
|
||||
}
|
||||
}
|
||||
|
||||
VOID CMutexRWLock::WaitToWrite()
|
||||
{
|
||||
BOOL bWait = FALSE;
|
||||
|
||||
{
|
||||
CSpinLock locallock(m_cs);
|
||||
|
||||
if(m_nActive > 0)
|
||||
bWait = TRUE;
|
||||
else if(m_nActive == 0)
|
||||
{
|
||||
if(m_mtx.try_lock())
|
||||
{
|
||||
SetOwner();
|
||||
--m_nActive;
|
||||
}
|
||||
else
|
||||
bWait = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(IsOwner())
|
||||
--m_nActive;
|
||||
else
|
||||
bWait = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if(bWait)
|
||||
{
|
||||
m_mtx.lock();
|
||||
|
||||
SetOwner();
|
||||
--m_nActive;
|
||||
}
|
||||
}
|
||||
|
||||
VOID CMutexRWLock::ReadDone()
|
||||
{
|
||||
ASSERT(m_nActive != 0);
|
||||
|
||||
if(m_nActive > 0)
|
||||
{
|
||||
{
|
||||
CSpinLock locallock(m_cs);
|
||||
--m_nActive;
|
||||
}
|
||||
|
||||
m_mtx.unlock_shared();
|
||||
}
|
||||
else
|
||||
ASSERT(IsOwner());
|
||||
}
|
||||
|
||||
VOID CMutexRWLock::WriteDone()
|
||||
{
|
||||
ASSERT(IsOwner());
|
||||
ASSERT(m_nActive < 0);
|
||||
|
||||
BOOL bDone;
|
||||
|
||||
{
|
||||
CSpinLock locallock(m_cs);
|
||||
bDone = (++m_nActive == 0);
|
||||
}
|
||||
|
||||
if(bDone)
|
||||
{
|
||||
DetachOwner();
|
||||
m_mtx.unlock();
|
||||
}
|
||||
else
|
||||
ASSERT(IsOwner());
|
||||
}
|
||||
|
||||
CSEMRWLock::CSEMRWLock()
|
||||
: m_nWaitingReaders (0)
|
||||
, m_nWaitingWriters (0)
|
||||
, m_nActive (0)
|
||||
, m_dwWriterTID (0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CSEMRWLock::~CSEMRWLock()
|
||||
{
|
||||
ASSERT(m_nActive == 0);
|
||||
ASSERT(m_dwWriterTID == 0);
|
||||
}
|
||||
|
||||
VOID CSEMRWLock::WaitToRead()
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
if(IsOwner())
|
||||
return;
|
||||
|
||||
++m_nWaitingReaders;
|
||||
|
||||
m_cvRead.wait(lock, [=]() -> BOOL
|
||||
{
|
||||
return m_nActive >= 0 && m_nWaitingWriters == 0;
|
||||
});
|
||||
|
||||
--m_nWaitingReaders;
|
||||
++m_nActive;
|
||||
}
|
||||
|
||||
VOID CSEMRWLock::WaitToWrite()
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
if(IsOwner())
|
||||
{
|
||||
--m_nActive;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
++m_nWaitingWriters;
|
||||
|
||||
m_cvWrite.wait(lock, [=]() -> BOOL
|
||||
{
|
||||
return m_nActive == 0;
|
||||
});
|
||||
|
||||
--m_nWaitingWriters;
|
||||
--m_nActive;
|
||||
|
||||
SetOwner();
|
||||
}
|
||||
|
||||
VOID CSEMRWLock::ReadDone()
|
||||
{
|
||||
CMutexLock2 locallock(m_mtx);
|
||||
|
||||
if(IsOwner())
|
||||
return;
|
||||
|
||||
ASSERT(m_nActive > 0);
|
||||
|
||||
if(--m_nActive == 0 && m_nWaitingWriters > 0)
|
||||
m_cvWrite.notify_one();
|
||||
}
|
||||
|
||||
VOID CSEMRWLock::WriteDone()
|
||||
{
|
||||
ASSERT(IsOwner());
|
||||
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
if(++m_nActive == 0)
|
||||
{
|
||||
DetachOwner();
|
||||
|
||||
if(m_nWaitingWriters > 0)
|
||||
m_cvWrite.notify_one();
|
||||
else if(m_nWaitingReaders > 0)
|
||||
m_cvRead.notify_all();
|
||||
}
|
||||
}
|
||||
128
common/RWLock.h
Normal file
128
common/RWLock.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "CriSec.h"
|
||||
|
||||
#include <shared_mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CMutexRWLock
|
||||
{
|
||||
public:
|
||||
VOID WaitToRead();
|
||||
VOID WaitToWrite();
|
||||
VOID ReadDone();
|
||||
VOID WriteDone();
|
||||
|
||||
private:
|
||||
BOOL IsOwner() {return ::IsSelfThread(m_dwWriterTID);}
|
||||
VOID SetOwner() {m_dwWriterTID = SELF_THREAD_ID;}
|
||||
VOID DetachOwner() {m_dwWriterTID = 0;}
|
||||
|
||||
public:
|
||||
CMutexRWLock();
|
||||
~CMutexRWLock();
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CMutexRWLock)
|
||||
|
||||
private:
|
||||
int m_nActive;
|
||||
THR_ID m_dwWriterTID;
|
||||
|
||||
CSpinGuard m_cs;
|
||||
shared_timed_mutex m_mtx;
|
||||
};
|
||||
|
||||
class CSEMRWLock
|
||||
{
|
||||
public:
|
||||
VOID WaitToRead();
|
||||
VOID WaitToWrite();
|
||||
VOID ReadDone();
|
||||
VOID WriteDone();
|
||||
|
||||
private:
|
||||
BOOL IsOwner() {BOOL bOwner = ::IsSelfThread(m_dwWriterTID); ASSERT(!bOwner || m_nActive < 0); return bOwner;}
|
||||
VOID SetOwner() {m_dwWriterTID = SELF_THREAD_ID;}
|
||||
VOID DetachOwner() {m_dwWriterTID = 0;}
|
||||
|
||||
public:
|
||||
CSEMRWLock();
|
||||
~CSEMRWLock();
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSEMRWLock)
|
||||
|
||||
private:
|
||||
int m_nWaitingReaders;
|
||||
int m_nWaitingWriters;
|
||||
int m_nActive;
|
||||
THR_ID m_dwWriterTID;
|
||||
|
||||
CMTX m_mtx;
|
||||
condition_variable m_cvRead;
|
||||
condition_variable m_cvWrite;
|
||||
};
|
||||
|
||||
template<class CLockObj> class CLocalReadLock
|
||||
{
|
||||
public:
|
||||
CLocalReadLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToRead();}
|
||||
~CLocalReadLock() {m_wait.ReadDone();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CLocalReadLock)
|
||||
|
||||
private:
|
||||
CLockObj& m_wait;
|
||||
};
|
||||
|
||||
template<class CLockObj> class CLocalWriteLock
|
||||
{
|
||||
public:
|
||||
CLocalWriteLock(CLockObj& obj) : m_wait(obj) {m_wait.WaitToWrite();}
|
||||
~CLocalWriteLock() {m_wait.WriteDone();}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CLocalWriteLock)
|
||||
|
||||
private:
|
||||
CLockObj& m_wait;
|
||||
};
|
||||
|
||||
|
||||
using CSimpleRWLock = shared_timed_mutex;
|
||||
using CReadLock = shared_lock<shared_timed_mutex>;
|
||||
using CWriteLock = lock_guard<shared_timed_mutex>;
|
||||
using CWriteLock2 = unique_lock<shared_timed_mutex>;
|
||||
|
||||
#if !defined(_USE_MUTEX_RW_LOCK)
|
||||
using CRWLock = CSEMRWLock;
|
||||
#else
|
||||
using CRWLock = CMutexRWLock;
|
||||
#endif
|
||||
|
||||
using CReentrantReadLock = CLocalReadLock<CRWLock>;
|
||||
using CReentrantWriteLock = CLocalWriteLock<CRWLock>;
|
||||
1731
common/RingBuffer.h
Normal file
1731
common/RingBuffer.h
Normal file
File diff suppressed because it is too large
Load Diff
1068
common/STLHelper.h
Normal file
1068
common/STLHelper.h
Normal file
File diff suppressed because it is too large
Load Diff
117
common/Semaphore.h
Normal file
117
common/Semaphore.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "CriSec.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class CSEM
|
||||
{
|
||||
public:
|
||||
void Wait()
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
m_cv.wait(lock);
|
||||
}
|
||||
|
||||
template<typename _Predicate>
|
||||
void Wait(_Predicate p)
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
m_cv.wait(lock, p);
|
||||
}
|
||||
|
||||
template<typename _Rep, typename _Period>
|
||||
cv_status WaitFor(const chrono::duration<_Rep, _Period>& t)
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
return m_cv.wait_for(lock, t);
|
||||
}
|
||||
|
||||
cv_status WaitFor(DWORD dwMilliseconds)
|
||||
{
|
||||
return WaitFor(chrono::milliseconds(dwMilliseconds));
|
||||
}
|
||||
|
||||
template<typename _Rep, typename _Period, typename _Predicate>
|
||||
bool WaitFor(const chrono::duration<_Rep, _Period>& t, _Predicate p)
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
return m_cv.wait_for(lock, t, p);
|
||||
}
|
||||
|
||||
template<typename _Predicate>
|
||||
bool WaitFor(DWORD dwMilliseconds, _Predicate p)
|
||||
{
|
||||
if(IS_INFINITE(dwMilliseconds))
|
||||
{
|
||||
Wait(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
return WaitFor(chrono::milliseconds(dwMilliseconds), p);
|
||||
|
||||
}
|
||||
|
||||
void NotifyOne()
|
||||
{
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void NotifyAll()
|
||||
{
|
||||
m_cv.notify_all();
|
||||
}
|
||||
|
||||
void SyncNotifyOne()
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
NotifyOne();
|
||||
}
|
||||
|
||||
void SyncNotifyAll()
|
||||
{
|
||||
CMutexLock2 lock(m_mtx);
|
||||
|
||||
NotifyAll();
|
||||
}
|
||||
|
||||
private:
|
||||
CMTX m_mtx;
|
||||
condition_variable m_cv;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSEM)
|
||||
DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(CSEM)
|
||||
};
|
||||
|
||||
using CCVLock = CSEM;
|
||||
183
common/SignalHandler.h
Normal file
183
common/SignalHandler.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<class T> class CSignalHandler
|
||||
{
|
||||
public:
|
||||
using MT = CSignalHandler<T>;
|
||||
using SI = siginfo_t;
|
||||
using SS = sigset_t;
|
||||
using CHandlerThread = CThread<MT, const SS, VOID>;
|
||||
using F = VOID (T::*)(const SI*);
|
||||
using SF = VOID (*)(const SI*);
|
||||
using SSPTR = unique_ptr<SS>;
|
||||
friend CHandlerThread;
|
||||
|
||||
public:
|
||||
|
||||
BOOL Setup(SF pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
|
||||
{
|
||||
return Setup((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pSigSet, bRestorOnCancel);
|
||||
}
|
||||
|
||||
BOOL Setup(T* pRunner, F pFunc, const SS* pSigSet, BOOL bRestorOnCancel = TRUE)
|
||||
{
|
||||
ASSERT_CHECK_EINVAL(pSigSet != nullptr);
|
||||
|
||||
m_pssPre = make_unique<SS>();
|
||||
int rs = pthread_sigmask(SIG_BLOCK, pSigSet, m_pssPre.get());
|
||||
|
||||
if(rs != NO_ERROR)
|
||||
{
|
||||
m_pssPre = nullptr;
|
||||
::SetLastError(rs);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
m_pRunner = pRunner;
|
||||
m_pFunc = pFunc;
|
||||
m_pssCur = make_unique<SS>();
|
||||
|
||||
::CopyPlainObject(m_pssCur.get(), pSigSet);
|
||||
|
||||
BOOL isOK = m_thHandler.Start(this, &MT::ThreadFunc, m_pssCur.get());
|
||||
|
||||
if(isOK && !bRestorOnCancel)
|
||||
m_pssPre = nullptr;
|
||||
else if(!isOK)
|
||||
EXECUTE_RESTORE_ERROR(Reset());
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL Cancel()
|
||||
{
|
||||
BOOL isOK = m_thHandler.IsRunning();
|
||||
|
||||
if(isOK)
|
||||
{
|
||||
isOK = m_thHandler.Interrupt();
|
||||
isOK &= m_thHandler.Join();
|
||||
}
|
||||
|
||||
isOK &= Reset();
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
BOOL IsRunning () {return m_thHandler.IsRunning();}
|
||||
T* GetRunner () {return m_pRunner;}
|
||||
F GetFunc () {return m_pFunc;}
|
||||
SF GetSFunc () {return *(SF*)&m_pFunc;}
|
||||
THR_ID GetThreadID () {return m_thHandler.GetThreadID();}
|
||||
NTHR_ID GetNativeID () {return m_thHandler.GetNativeID();}
|
||||
|
||||
|
||||
private:
|
||||
VOID ThreadFunc(const SS* pSigSet)
|
||||
{
|
||||
ASSERT(pSigSet == m_pssCur.get());
|
||||
|
||||
SI si;
|
||||
|
||||
ZeroObject(si);
|
||||
|
||||
while(!::IsThreadInterrupted())
|
||||
{
|
||||
#if !defined(__ANDROID__)
|
||||
int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwaitinfo(pSigSet, &si));
|
||||
#else
|
||||
int rs = NO_EINTR_EXCEPT_THR_INTR_INT(sigwait(pSigSet, &si.si_signo));
|
||||
#endif
|
||||
|
||||
if(IS_HAS_ERROR(rs))
|
||||
{
|
||||
if(IS_ERROR(EINTR))
|
||||
{
|
||||
ASSERT(::IsThreadInterrupted());
|
||||
break;
|
||||
}
|
||||
|
||||
ERROR_ABORT();
|
||||
}
|
||||
|
||||
Run((T*)nullptr, &si);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
|
||||
VOID Run(T_*, const SI* pSigSet) {(m_pRunner->*m_pFunc)(pSigSet);}
|
||||
|
||||
VOID Run(__CFakeRunnerClass_*, const SI* pSigSet) {(*(SF*)&m_pFunc)(pSigSet);}
|
||||
|
||||
|
||||
BOOL Reset()
|
||||
{
|
||||
BOOL isOK = TRUE;
|
||||
|
||||
if(m_pssPre)
|
||||
{
|
||||
isOK = (pthread_sigmask(SIG_SETMASK, m_pssPre.get(), nullptr) == NO_ERROR);
|
||||
m_pssPre = nullptr;
|
||||
}
|
||||
|
||||
m_pssCur = nullptr;
|
||||
m_pRunner = nullptr;
|
||||
m_pFunc = nullptr;
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
public:
|
||||
CSignalHandler()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~CSignalHandler()
|
||||
{
|
||||
Cancel();
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CSignalHandler)
|
||||
|
||||
private:
|
||||
T* m_pRunner;
|
||||
F m_pFunc;
|
||||
SSPTR m_pssCur;
|
||||
SSPTR m_pssPre;
|
||||
CHandlerThread m_thHandler;
|
||||
};
|
||||
|
||||
using CStaticSignalHandler = CSignalHandler<__CFakeRunnerClass_>;
|
||||
117
common/Singleton.h
Normal file
117
common/Singleton.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
|
||||
#define SINGLETON_THIS(ClassName) ClassName::GetThis()
|
||||
#define SINGLETON_INSTANCE(ClassName) ClassName::GetInstance()
|
||||
#define SINGLETON_OBJECT(ObjName) SINGLETON_INSTANCE(C##ObjName)
|
||||
|
||||
#define DEFINE_SINGLETON(ClassName) \
|
||||
ClassName* ClassName::m_pThis = nullptr;
|
||||
|
||||
#define DEFINE_P_THIS(ClassName) \
|
||||
DEFINE_SINGLETON(ClassName)
|
||||
|
||||
#define DECLARE_SINGLETON_INTERFACE(ClassName) \
|
||||
public: \
|
||||
static ClassName* GetThis() {return m_pThis;} \
|
||||
static ClassName& GetInstance() {return *m_pThis;} \
|
||||
protected: \
|
||||
static ClassName* m_pThis;
|
||||
|
||||
#define DECLARE_SINGLETON_CREATE_INSTANCE(ClassName) \
|
||||
public: \
|
||||
static BOOL CreateInstance() \
|
||||
{ \
|
||||
if(!m_pThis) \
|
||||
m_pThis = new ClassName; \
|
||||
\
|
||||
return m_pThis != nullptr; \
|
||||
} \
|
||||
\
|
||||
static BOOL DeleteInstance() \
|
||||
{ \
|
||||
if(m_pThis) \
|
||||
{ \
|
||||
delete m_pThis; \
|
||||
m_pThis = nullptr; \
|
||||
} \
|
||||
\
|
||||
return m_pThis == nullptr; \
|
||||
}
|
||||
|
||||
#define DECLARE_PUBLIC_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
public: \
|
||||
ClassName() = default;
|
||||
|
||||
#define DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
private: \
|
||||
ClassName() = default;
|
||||
|
||||
#define DECLARE_PRIVATE_COPY_CONSTRUCTOR(ClassName) \
|
||||
private: \
|
||||
ClassName(const ClassName&); \
|
||||
ClassName& operator = (const ClassName&);
|
||||
|
||||
#define DECLARE_NO_COPY_CLASS(ClassName) \
|
||||
private: \
|
||||
ClassName(const ClassName&) = delete; \
|
||||
ClassName& operator = (const ClassName&) = delete;
|
||||
|
||||
|
||||
#define DECLARE_SINGLETON_IMPLEMENT_NO_CREATE_INSTANCE(ClassName) \
|
||||
DECLARE_SINGLETON_INTERFACE(ClassName) \
|
||||
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
DECLARE_NO_COPY_CLASS(ClassName)
|
||||
|
||||
#define DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
DECLARE_SINGLETON_CREATE_INSTANCE(ClassName) \
|
||||
DECLARE_NO_COPY_CLASS(ClassName)
|
||||
|
||||
#define DECLARE_SINGLETON_IMPLEMENT(ClassName) \
|
||||
DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
|
||||
|
||||
#define DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
DECLARE_SINGLETON_INTERFACE(ClassName) \
|
||||
DECLARE_SINGLETON_IMPLEMENT_NO_DEFAULT_CONSTRUCTOR(ClassName)
|
||||
|
||||
#define DECLARE_SINGLETON(ClassName) \
|
||||
DECLARE_SINGLETON_NO_DEFAULT_CONSTRUCTOR(ClassName) \
|
||||
DECLARE_PRIVATE_DEFAULT_CONSTRUCTOR(ClassName)
|
||||
|
||||
|
||||
template<class T> class CSingleObject
|
||||
{
|
||||
public:
|
||||
CSingleObject () {T::CreateInstance();}
|
||||
~CSingleObject () {T::DeleteInstance();}
|
||||
T* GetPointer () {return T::GetThis();}
|
||||
T& GetObject () {return T::GetInstance();}
|
||||
BOOL IsValid () {return GetPointer() != nullptr;}
|
||||
};
|
||||
|
||||
#define DECLARE_SINGLE_OBJECT(ClassName) CSingleObject<ClassName> _##ClassName##_Single_Object_;
|
||||
930
common/StringT.h
Normal file
930
common/StringT.h
Normal file
@@ -0,0 +1,930 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "FuncHelper.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<typename _CharT, typename _Traits = char_traits<_CharT>, typename _Alloc = allocator<_CharT>>
|
||||
class CStringT : public basic_string<_CharT, _Traits, _Alloc>
|
||||
{
|
||||
public:
|
||||
|
||||
using __super = basic_string<_CharT, _Traits, _Alloc>;
|
||||
|
||||
using XCHAR = _CharT;
|
||||
using PXSTR = _CharT*;
|
||||
using PCXSTR = const _CharT*;
|
||||
|
||||
using traits_type = typename __super::traits_type;
|
||||
using value_type = typename __super::value_type;
|
||||
using allocator_type = typename __super::allocator_type;
|
||||
using size_type = typename __super::size_type;
|
||||
using difference_type = typename __super::difference_type;
|
||||
using reference = typename __super::reference;
|
||||
using const_reference = typename __super::const_reference;
|
||||
using pointer = typename __super::pointer;
|
||||
using const_pointer = typename __super::const_pointer;
|
||||
using iterator = typename __super::iterator;
|
||||
using const_iterator = typename __super::const_iterator;
|
||||
using const_reverse_iterator = typename __super::const_reverse_iterator;
|
||||
using reverse_iterator = typename __super::reverse_iterator;
|
||||
|
||||
using __super::clear;
|
||||
using __super::empty;
|
||||
using __super::size;
|
||||
using __super::resize;
|
||||
using __super::data;
|
||||
using __super::c_str;
|
||||
|
||||
private:
|
||||
|
||||
constexpr static PCXSTR SPACE_CHARS = _T(" \t\r\n\f\v");
|
||||
|
||||
public:
|
||||
|
||||
void Empty() {clear();}
|
||||
bool IsEmpty() const {return empty();}
|
||||
int GetLength() const {return (int)size();}
|
||||
const _CharT* GetString() const {return c_str();}
|
||||
|
||||
operator const _CharT* () const {return __super::c_str();}
|
||||
|
||||
_CharT* GetBuffer(int length)
|
||||
{
|
||||
resize((size_type)length);
|
||||
return (_CharT*)data();
|
||||
}
|
||||
|
||||
void ReleaseBuffer(int length = -1)
|
||||
{
|
||||
if(length == -1)
|
||||
length = lstrlen(data());
|
||||
|
||||
resize(length);
|
||||
}
|
||||
|
||||
void ReleaseBufferSetLength(int length)
|
||||
{
|
||||
ASSERT(length >=0);
|
||||
ReleaseBuffer(length);
|
||||
}
|
||||
|
||||
void Truncate(int length)
|
||||
{
|
||||
if(length >= GetLength())
|
||||
return;
|
||||
|
||||
ReleaseBuffer(length);
|
||||
}
|
||||
|
||||
int Format(const _CharT* format, ...)
|
||||
{
|
||||
int rs;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
rs = VASprintf(0, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
int AppendFormat(const _CharT* format, ...)
|
||||
{
|
||||
int rs;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
rs = VASprintf(GetLength(), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return rs;
|
||||
}
|
||||
|
||||
int VASprintf(int offset, const _CharT* format, va_list ap)
|
||||
{
|
||||
va_list ap_cpy;
|
||||
va_copy(ap_cpy, ap);
|
||||
|
||||
int count = vsnprintf(nullptr, 0, format, ap);
|
||||
|
||||
if(count >= 0)
|
||||
{
|
||||
_CharT* p = GetBuffer(count + offset);
|
||||
vsnprintf(p + offset, count + 1, format, ap_cpy);
|
||||
}
|
||||
|
||||
va_end(ap_cpy);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
CStringT& Append(const _CharT* __s)
|
||||
{
|
||||
append(__s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& Append(const _CharT* __s, int __n)
|
||||
{
|
||||
append(__s, __n);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& AppendChar(_CharT __c)
|
||||
{
|
||||
push_back(__c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
int Compare(const _CharT* __s) const
|
||||
{
|
||||
return lstrcmp(c_str(), __s);
|
||||
}
|
||||
|
||||
int CompareNoCase(const _CharT* __s) const
|
||||
{
|
||||
return lstricmp(c_str(), __s);
|
||||
}
|
||||
|
||||
bool Equals(const _CharT* __s) const
|
||||
{
|
||||
return (Compare(__s) == 0);
|
||||
}
|
||||
|
||||
bool EqualsNoCase(const _CharT* __s) const
|
||||
{
|
||||
return (CompareNoCase(__s) == 0);
|
||||
}
|
||||
|
||||
CStringT& MakeLower()
|
||||
{
|
||||
size_type s = size();
|
||||
_CharT* p = (_CharT*)c_str();
|
||||
_CharT c;
|
||||
|
||||
for(size_type i = 0; i < s; i++)
|
||||
{
|
||||
c = p[i];
|
||||
|
||||
if(c >= 'A' && c <= 'Z')
|
||||
p[i] = (_CharT)(c + 32);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& MakeUpper()
|
||||
{
|
||||
size_type s = size();
|
||||
_CharT* p = (_CharT*)c_str();
|
||||
_CharT c;
|
||||
|
||||
for(size_type i = 0; i < s; i++)
|
||||
{
|
||||
c = p[i];
|
||||
|
||||
if(c >= 'a' && c <= 'z')
|
||||
p[i] = (_CharT)(c - 32);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT Mid(int iFirst, int nCount = (int)__super::npos) const
|
||||
{
|
||||
return substr(iFirst, nCount);
|
||||
}
|
||||
|
||||
CStringT Left(int nCount) const
|
||||
{
|
||||
return Mid(0, nCount);
|
||||
}
|
||||
|
||||
CStringT Right(int nCount) const
|
||||
{
|
||||
int nLength = GetLength();
|
||||
|
||||
if(nCount >= nLength)
|
||||
return *this;
|
||||
|
||||
return Mid(nLength - nCount, nCount);
|
||||
}
|
||||
|
||||
CStringT Tokenize(PCXSTR lpszTokens, int& iStart) const
|
||||
{
|
||||
ASSERT(iStart >= 0);
|
||||
|
||||
if((lpszTokens == nullptr) || (*lpszTokens == (_CharT)0))
|
||||
{
|
||||
if(iStart < GetLength())
|
||||
return CStringT(GetString() + iStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
PCXSTR pszPlace = GetString() + iStart;
|
||||
PCXSTR pszEnd = GetString() + GetLength();
|
||||
|
||||
if(pszPlace < pszEnd)
|
||||
{
|
||||
int nIncluding = lstrspn(pszPlace, lpszTokens);
|
||||
|
||||
if((pszPlace + nIncluding) < pszEnd)
|
||||
{
|
||||
pszPlace += nIncluding;
|
||||
int nExcluding = lstrcspn(pszPlace, lpszTokens);
|
||||
|
||||
int iFrom = iStart + nIncluding;
|
||||
int nUntil = nExcluding;
|
||||
iStart = iFrom + nUntil + 1;
|
||||
|
||||
return Mid(iFrom, nUntil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iStart = -1;
|
||||
|
||||
return CStringT();
|
||||
}
|
||||
|
||||
CStringT& Trim()
|
||||
{
|
||||
return Trim(SPACE_CHARS);
|
||||
}
|
||||
|
||||
CStringT& TrimRight()
|
||||
{
|
||||
return TrimRight(SPACE_CHARS);
|
||||
}
|
||||
|
||||
CStringT& TrimLeft()
|
||||
{
|
||||
return TrimLeft(SPACE_CHARS);
|
||||
}
|
||||
|
||||
CStringT& Trim(XCHAR c)
|
||||
{
|
||||
return(TrimRight(c).TrimLeft(c));
|
||||
}
|
||||
|
||||
CStringT& TrimRight(XCHAR c)
|
||||
{
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return *this;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszEnd = lpszBegin + iLength;
|
||||
|
||||
while(lpszEnd > lpszBegin)
|
||||
{
|
||||
if(*(lpszEnd - 1) != c)
|
||||
break;
|
||||
|
||||
--lpszEnd;
|
||||
}
|
||||
|
||||
int iNewLength = (int)(lpszEnd - lpszBegin);
|
||||
|
||||
if(iNewLength < iLength)
|
||||
Truncate(iNewLength);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& TrimLeft(XCHAR c)
|
||||
{
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return *this;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszEnd = lpszBegin;
|
||||
int iOffset = 0;
|
||||
|
||||
while(*lpszEnd == c)
|
||||
{
|
||||
++lpszEnd;
|
||||
++iOffset;
|
||||
|
||||
if(iOffset == iLength)
|
||||
break;
|
||||
}
|
||||
|
||||
if(iOffset != 0)
|
||||
{
|
||||
int iNewLength = iLength - iOffset;
|
||||
|
||||
if(iNewLength > 0)
|
||||
memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
|
||||
|
||||
ReleaseBufferSetLength(iNewLength);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& Trim(PCXSTR lpszChars)
|
||||
{
|
||||
return(TrimRight(lpszChars).TrimLeft(lpszChars));
|
||||
}
|
||||
|
||||
CStringT& TrimRight(PCXSTR lpszChars)
|
||||
{
|
||||
ASSERT(!::IsStrEmpty(lpszChars));
|
||||
|
||||
if(::IsStrEmpty(lpszChars))
|
||||
return *this;
|
||||
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return *this;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszEnd = lpszBegin + iLength;
|
||||
|
||||
while(lpszEnd > lpszBegin)
|
||||
{
|
||||
if(::StrChr(lpszChars, *(lpszEnd - 1)) == nullptr)
|
||||
break;
|
||||
|
||||
--lpszEnd;
|
||||
}
|
||||
|
||||
int iNewLength = (int)(lpszEnd - lpszBegin);
|
||||
|
||||
if(iNewLength < iLength)
|
||||
Truncate(iNewLength);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
CStringT& TrimLeft(PCXSTR lpszChars)
|
||||
{
|
||||
ASSERT(!::IsStrEmpty(lpszChars));
|
||||
|
||||
if(::IsStrEmpty(lpszChars))
|
||||
return *this;
|
||||
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return *this;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszEnd = lpszBegin;
|
||||
int iOffset = 0;
|
||||
|
||||
while(::StrChr(lpszChars, *lpszEnd) != nullptr)
|
||||
{
|
||||
++lpszEnd;
|
||||
++iOffset;
|
||||
|
||||
if(iOffset == iLength)
|
||||
break;
|
||||
}
|
||||
|
||||
if(iOffset != 0)
|
||||
{
|
||||
int iNewLength = iLength - iOffset;
|
||||
|
||||
if(iNewLength > 0)
|
||||
memcpy((PXSTR)lpszBegin, lpszEnd, (iLength - iOffset) * sizeof(XCHAR));
|
||||
|
||||
ReleaseBufferSetLength(iNewLength);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int Find(XCHAR c, int iStart = 0) const
|
||||
{
|
||||
ASSERT(iStart >= 0);
|
||||
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iStart < 0 || iStart >= iLength)
|
||||
return -1;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszFind = ::StrChr(lpszBegin + iStart, c);
|
||||
|
||||
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
|
||||
}
|
||||
|
||||
int Find(PCXSTR lpszSub, int iStart = 0) const
|
||||
{
|
||||
ASSERT(iStart >= 0 && !::IsStrEmpty(lpszSub));
|
||||
|
||||
int iLength = GetLength();
|
||||
|
||||
if(lpszSub == nullptr || iStart < 0 || iStart > iLength)
|
||||
return -1;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszFind = ::StrStr(lpszBegin + iStart, lpszSub);
|
||||
|
||||
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
|
||||
}
|
||||
|
||||
int FindOneOf(PCXSTR lpszChars) const
|
||||
{
|
||||
ASSERT(!::IsStrEmpty(lpszChars));
|
||||
|
||||
if(lpszChars == nullptr)
|
||||
return -1;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszFind = ::StrPBrk(lpszBegin, lpszChars);
|
||||
|
||||
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
|
||||
}
|
||||
|
||||
int ReverseFind(XCHAR c) const
|
||||
{
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PCXSTR lpszFind = ::StrRChr(lpszBegin, c);
|
||||
|
||||
return ((lpszFind == nullptr) ? -1 : (int)(lpszFind - lpszBegin));
|
||||
}
|
||||
|
||||
int Remove(XCHAR c)
|
||||
{
|
||||
int iLength = GetLength();
|
||||
|
||||
if(iLength == 0)
|
||||
return 0;
|
||||
|
||||
PCXSTR lpszBegin = GetString();
|
||||
PXSTR lpszCur = (PXSTR)lpszBegin;
|
||||
PCXSTR lpszEnd = lpszBegin + iLength;
|
||||
int iRemoved = 0;
|
||||
|
||||
while(lpszCur < lpszEnd)
|
||||
{
|
||||
if(*lpszCur == c)
|
||||
++iRemoved;
|
||||
else if(iRemoved > 0)
|
||||
*(lpszCur - iRemoved) = *lpszCur;
|
||||
|
||||
++lpszCur;
|
||||
}
|
||||
|
||||
if(iRemoved > 0)
|
||||
ReleaseBufferSetLength(iLength - iRemoved);
|
||||
|
||||
return iRemoved;
|
||||
}
|
||||
|
||||
XCHAR GetAt(int i) const
|
||||
{
|
||||
return (*this)[i];
|
||||
}
|
||||
|
||||
void SetAt(int i, XCHAR c)
|
||||
{
|
||||
(*this)[i] = c;
|
||||
}
|
||||
|
||||
XCHAR operator[](int i) const
|
||||
{
|
||||
ASSERT(i >= 0 && i < GetLength());
|
||||
|
||||
return *(GetString() + i);
|
||||
}
|
||||
|
||||
XCHAR& operator[](int i)
|
||||
{
|
||||
ASSERT(i >= 0 && i < GetLength());
|
||||
|
||||
return *(PXSTR)(GetString() + i);
|
||||
}
|
||||
|
||||
CStringT& Insert(int i, XCHAR c)
|
||||
{
|
||||
return insert((size_type)i, 1, c);
|
||||
}
|
||||
|
||||
CStringT& Insert(int i, PCXSTR lpszChars)
|
||||
{
|
||||
return insert((size_type)i, lpszChars);
|
||||
}
|
||||
|
||||
CStringT& SetString(PCXSTR lpszStr)
|
||||
{
|
||||
return assign(lpszStr);
|
||||
}
|
||||
|
||||
CStringT& SetString(PCXSTR lpszStr, int iLength)
|
||||
{
|
||||
return assign(lpszStr, iLength);
|
||||
}
|
||||
|
||||
friend bool operator==(const CStringT& str1, const CStringT& str2)
|
||||
{
|
||||
return (str1.Compare(str2) == 0);
|
||||
}
|
||||
|
||||
friend bool operator==(const CStringT& str1, const _CharT* psz2)
|
||||
{
|
||||
return (str1.Compare(psz2) == 0);
|
||||
}
|
||||
|
||||
friend bool operator==(const _CharT* psz1, const CStringT& str2)
|
||||
{
|
||||
return (str2.Compare(psz1) == 0);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CStringT& str1, const CStringT& str2)
|
||||
{
|
||||
return !(str1 == str2);
|
||||
}
|
||||
|
||||
friend bool operator!=(const CStringT& str1, const _CharT* psz2)
|
||||
{
|
||||
return !(str1 == psz2);
|
||||
}
|
||||
|
||||
friend bool operator!=(const _CharT* psz1, const CStringT& str2)
|
||||
{
|
||||
return !(psz1 == str2);
|
||||
}
|
||||
|
||||
public:
|
||||
CStringT() : __super() {};
|
||||
|
||||
explicit CStringT(const _Alloc& __a)
|
||||
: __super(__a) {}
|
||||
|
||||
CStringT(const __super& __str)
|
||||
: __super(__str) {}
|
||||
|
||||
CStringT(const CStringT& __str)
|
||||
: __super(__str) {}
|
||||
|
||||
CStringT(const __super& __str, size_type __pos, size_type __n = __super::npos)
|
||||
: __super(__str, __pos, __n) {}
|
||||
|
||||
CStringT(const __super& __str, size_type __pos, size_type __n, const _Alloc& __a)
|
||||
: __super(__str, __pos, __n, __a) {}
|
||||
|
||||
CStringT(const _CharT* __s, size_type __n, const _Alloc& __a = _Alloc())
|
||||
: __super(::SafeStr(__s), __n, __a) {}
|
||||
|
||||
CStringT(const _CharT* __s, const _Alloc& __a = _Alloc())
|
||||
: __super(::SafeStr(__s), __a) {}
|
||||
|
||||
CStringT(size_type __n, _CharT __c, const _Alloc& __a = _Alloc())
|
||||
: __super(__n, __c, __a) {}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT(__super&& __str)
|
||||
: __super(__str) {}
|
||||
|
||||
CStringT(CStringT&& __str)
|
||||
: __super(__str) {}
|
||||
|
||||
CStringT(initializer_list<_CharT> __l, const _Alloc& __a = _Alloc())
|
||||
: __super(__l, __a) {}
|
||||
#endif // C++11
|
||||
|
||||
template<class _InputIterator>
|
||||
CStringT(_InputIterator __beg, _InputIterator __end, const _Alloc& __a = _Alloc())
|
||||
: __super(__beg, __end, __a) {}
|
||||
|
||||
~CStringT() = default;
|
||||
|
||||
CStringT& operator=(const __super& __str)
|
||||
{__super::operator=(__str); return *this;}
|
||||
|
||||
CStringT& operator=(const CStringT& __str)
|
||||
{__super::operator=(__str); return *this;}
|
||||
|
||||
CStringT& operator=(const _CharT* __s)
|
||||
{__super::operator=(::SafeStr(__s)); return *this;}
|
||||
|
||||
CStringT& operator=(_CharT __c)
|
||||
{__super::operator=(__c); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& operator=(__super&& __str)
|
||||
{__super::operator=(__str); return *this;}
|
||||
|
||||
CStringT& operator=(CStringT&& __str)
|
||||
{__super::operator=(__str); return *this;}
|
||||
|
||||
CStringT& operator=(initializer_list<_CharT> __l)
|
||||
{__super::operator=(__l); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
public:
|
||||
CStringT& operator+=(const __super& __str)
|
||||
{__super::operator+=(__str); return *this;}
|
||||
|
||||
CStringT& operator+=(const _CharT* __s)
|
||||
{__super::operator+=(::SafeStr(__s)); return *this;}
|
||||
|
||||
CStringT& operator+=(_CharT __c)
|
||||
{__super::operator+=(__c); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& operator+=(initializer_list<_CharT> __l)
|
||||
{__super::operator+=(__l); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
CStringT& append(const __super& __str)
|
||||
{__super::append(__str); return *this;}
|
||||
|
||||
CStringT& append(const __super& __str, size_type __pos, size_type __n)
|
||||
{__super::append(__str, __pos, __n); return *this;}
|
||||
|
||||
CStringT& append(const _CharT* __s, size_type __n)
|
||||
{__super::append(::SafeStr(__s), __n); return *this;}
|
||||
|
||||
CStringT& append(const _CharT* __s)
|
||||
{__super::append(::SafeStr(__s)); return *this;}
|
||||
|
||||
CStringT& append(size_type __n, _CharT __c)
|
||||
{__super::append(__n, __c); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& append(initializer_list<_CharT> __l)
|
||||
{__super::append(__l); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
template<class _InputIterator>
|
||||
CStringT& append(_InputIterator __first, _InputIterator __last)
|
||||
{__super::append(__first, __last); return *this;}
|
||||
|
||||
void push_back(_CharT __c)
|
||||
{__super::push_back(__c);}
|
||||
|
||||
CStringT& assign(const __super& __str)
|
||||
{__super::assign(__str); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& assign(__super&& __str)
|
||||
{__super::assign(__str); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
CStringT& assign(const __super& __str, size_type __pos, size_type __n)
|
||||
{__super::assign(__str, __pos, __n); return *this;}
|
||||
|
||||
CStringT& assign(const _CharT* __s, size_type __n)
|
||||
{__super::assign(::SafeStr(__s), __n); return *this;}
|
||||
|
||||
CStringT& assign(const _CharT* __s)
|
||||
{__super::assign(::SafeStr(__s)); return *this;}
|
||||
|
||||
CStringT& assign(size_type __n, _CharT __c)
|
||||
{__super::assign(__n, __c); return *this;}
|
||||
|
||||
template<class _InputIterator>
|
||||
CStringT& assign(_InputIterator __first, _InputIterator __last)
|
||||
{__super::assign(__first, __last); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& assign(initializer_list<_CharT> __l)
|
||||
{__super::assign(__l); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
CStringT& insert(size_type __pos1, const __super& __str)
|
||||
{__super::insert(__pos1, __str); return *this;}
|
||||
|
||||
CStringT& insert(size_type __pos1, const __super& __str, size_type __pos2, size_type __n)
|
||||
{__super::insert(__pos1, __str, __pos2, __n); return *this;}
|
||||
|
||||
CStringT& insert(size_type __pos, const _CharT* __s, size_type __n)
|
||||
{__super::insert(__pos, __s, __n); return *this;}
|
||||
|
||||
CStringT& insert(size_type __pos, const _CharT* __s)
|
||||
{__super::insert(__pos, __s); return *this;}
|
||||
|
||||
CStringT& insert(size_type __pos, size_type __n, _CharT __c)
|
||||
{__super::insert(__pos, __n, __c); return *this;}
|
||||
|
||||
CStringT& erase(size_type __pos = 0, size_type __n = __super::npos)
|
||||
{__super::erase(__pos, __n); return *this;}
|
||||
|
||||
CStringT& replace(size_type __pos, size_type __n, const __super& __str)
|
||||
{__super::replace(__pos, __n, __str); return *this;}
|
||||
|
||||
CStringT& replace(size_type __pos1, size_type __n1, const __super& __str, size_type __pos2, size_type __n2)
|
||||
{__super::replace(__pos1, __n1, __str, __pos2, __n2); return *this;}
|
||||
|
||||
CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s, size_type __n2)
|
||||
{__super::replace(__pos, __n1, __s, __n2); return *this;}
|
||||
|
||||
CStringT& replace(size_type __pos, size_type __n1, const _CharT* __s)
|
||||
{__super::replace(__pos, __n1, __s); return *this;}
|
||||
|
||||
CStringT& replace(size_type __pos, size_type __n1, size_type __n2, _CharT __c)
|
||||
{__super::replace(__pos, __n1, __n2, __c); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, const __super& __str)
|
||||
{__super::replace(__i1, __i2, __str); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s, size_type __n)
|
||||
{__super::replace(__i1, __i2, __s, __n); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __s)
|
||||
{__super::replace(__i1, __i2, __s); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, size_type __n, _CharT __c)
|
||||
{__super::replace(__i1, __i2, __n, __c); return *this;}
|
||||
|
||||
template<class _InputIterator>
|
||||
CStringT& replace(iterator __i1, iterator __i2, _InputIterator __k1, _InputIterator __k2)
|
||||
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, _CharT* __k1, _CharT* __k2)
|
||||
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, const _CharT* __k1, const _CharT* __k2)
|
||||
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, iterator __k1, iterator __k2)
|
||||
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
|
||||
|
||||
CStringT& replace(iterator __i1, iterator __i2, const_iterator __k1, const_iterator __k2)
|
||||
{__super::replace(__i1, __i2, __k1, __k2); return *this;}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
CStringT& replace(iterator __i1, iterator __i2, initializer_list<_CharT> __l)
|
||||
{__super::replace(__i1, __i2, __l); return *this;}
|
||||
#endif // C++11
|
||||
|
||||
CStringT substr(size_type __pos = 0, size_type __n = __super::npos) const
|
||||
{return __super::substr(__pos, __n);}
|
||||
|
||||
};
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
|
||||
{
|
||||
CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
|
||||
__str.append(__rhs);
|
||||
return __str;
|
||||
}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
CStringT<_CharT,_Traits,_Alloc>
|
||||
operator+(const _CharT* __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
CStringT<_CharT,_Traits,_Alloc>
|
||||
operator+(_CharT __lhs, const CStringT<_CharT,_Traits,_Alloc>& __rhs);
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, const _CharT* __rhs)
|
||||
{
|
||||
CStringT<_CharT, _Traits, _Alloc> __str(__lhs);
|
||||
__str.append(__rhs);
|
||||
return __str;
|
||||
}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, _CharT __rhs)
|
||||
{
|
||||
typedef CStringT<_CharT, _Traits, _Alloc> __string_type;
|
||||
typedef typename __string_type::size_type __size_type;
|
||||
__string_type __str(__lhs);
|
||||
__str.append(__size_type(1), __rhs);
|
||||
return __str;
|
||||
}
|
||||
|
||||
#if __cplusplus >= 201103L
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const CStringT<_CharT, _Traits, _Alloc>& __rhs)
|
||||
{return std::move(__lhs.append(__rhs));}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(const CStringT<_CharT, _Traits, _Alloc>& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
|
||||
{return std::move(__rhs.insert(0, __lhs));}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
|
||||
{
|
||||
const auto __size = __lhs.size() + __rhs.size();
|
||||
const bool __cond = (__size > __lhs.capacity()
|
||||
&& __size <= __rhs.capacity());
|
||||
return __cond ? std::move(__rhs.insert(0, __lhs))
|
||||
: std::move(__lhs.append(__rhs));
|
||||
}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(const _CharT* __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
|
||||
{return std::move(__rhs.insert(0, __lhs));}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(_CharT __lhs, CStringT<_CharT, _Traits, _Alloc>&& __rhs)
|
||||
{return std::move(__rhs.insert(0, 1, __lhs));}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, const _CharT* __rhs)
|
||||
{return std::move(__lhs.append(__rhs));}
|
||||
|
||||
template<typename _CharT, typename _Traits, typename _Alloc>
|
||||
inline CStringT<_CharT, _Traits, _Alloc>
|
||||
operator+(CStringT<_CharT, _Traits, _Alloc>&& __lhs, _CharT __rhs)
|
||||
{return std::move(__lhs.append(1, __rhs));}
|
||||
#endif
|
||||
|
||||
using CStringA = CStringT<char>;
|
||||
using CStringW = CStringT<wchar_t>;
|
||||
|
||||
using CStdStringA = string;
|
||||
using CStdStringW = wstring;
|
||||
|
||||
#ifdef _UNICODE
|
||||
using CString = CStringW;
|
||||
using CStdString = CStdStringW;
|
||||
#else
|
||||
using CString = CStringA;
|
||||
using CStdString = CStdStringA;
|
||||
#endif
|
||||
|
||||
#define _HASH_SEED (size_t)0xdeadbeef
|
||||
|
||||
template<class _Kty>
|
||||
inline size_t hash_value(const _Kty& _Keyval)
|
||||
{
|
||||
return ((size_t)_Keyval ^ _HASH_SEED);
|
||||
}
|
||||
|
||||
template <class _InIt>
|
||||
inline size_t _Hash_value(_InIt _Begin, _InIt _End)
|
||||
{
|
||||
size_t _Val = 2166136261U;
|
||||
|
||||
while(_Begin != _End)
|
||||
_Val = 16777619U * _Val ^ (size_t)*_Begin++;
|
||||
|
||||
return (_Val);
|
||||
}
|
||||
|
||||
template<class _Elem, class _Traits, class _Alloc>
|
||||
inline size_t hash_value(const basic_string<_Elem, _Traits, _Alloc>& _Str)
|
||||
{
|
||||
const _Elem *_Ptr = _Str.c_str();
|
||||
|
||||
return (_Hash_value(_Ptr, _Ptr + _Str.size()));
|
||||
}
|
||||
|
||||
template<class _Elem>
|
||||
inline size_t hash_value(const CStringT<_Elem>& _Str)
|
||||
{
|
||||
const _Elem *_Ptr = _Str.c_str();
|
||||
|
||||
return (_Hash_value(_Ptr, _Ptr + _Str.size()));
|
||||
}
|
||||
|
||||
inline size_t hash_value(const char *_Str)
|
||||
{
|
||||
return (_Hash_value(_Str, _Str + strlen(_Str)));
|
||||
}
|
||||
|
||||
inline size_t hash_value(const wchar_t *_Str)
|
||||
{
|
||||
return (_Hash_value(_Str, _Str + wcslen(_Str)));
|
||||
}
|
||||
66
common/SysHelper.cpp
Normal file
66
common/SysHelper.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SysHelper.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
DWORD _GetKernelVersion()
|
||||
{
|
||||
utsname uts;
|
||||
|
||||
if(uname(&uts) == RS_FAIL)
|
||||
return 0;
|
||||
|
||||
char c;
|
||||
int major, minor, revise;
|
||||
|
||||
if(sscanf(uts.release, "%d.%d.%d%c", &major, &minor, &revise, &c) < 3)
|
||||
return 0;
|
||||
|
||||
return (DWORD)((major << 16) | (minor << 8) | revise);
|
||||
}
|
||||
|
||||
DWORD GetSysPageSize()
|
||||
{
|
||||
static const DWORD _s_page_size = (DWORD)SysGetPageSize();
|
||||
return _s_page_size;
|
||||
}
|
||||
|
||||
DWORD GetKernelVersion()
|
||||
{
|
||||
static const DWORD _s_kernel_version = _GetKernelVersion();
|
||||
return _s_kernel_version;
|
||||
}
|
||||
|
||||
BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise)
|
||||
{
|
||||
return GetKernelVersion() >= (DWORD)((major << 16) | (minor << 8) | revise);
|
||||
}
|
||||
|
||||
DWORD GetDefaultWorkerThreadCount()
|
||||
{
|
||||
static const DWORD _s_dwtc = MIN((PROCESSOR_COUNT * 2 + 2), MAX_WORKER_THREAD_COUNT);
|
||||
return _s_dwtc;
|
||||
}
|
||||
186
common/SysHelper.h
Normal file
186
common/SysHelper.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* 最大工作线程数 */
|
||||
#define MAX_WORKER_THREAD_COUNT 512
|
||||
/* 默认对象缓存锁定时间 */
|
||||
#define DEFAULT_OBJECT_CACHE_LOCK_TIME (30 * 1000)
|
||||
/* 默认对象缓存池大小 */
|
||||
#define DEFAULT_OBJECT_CACHE_POOL_SIZE 600
|
||||
/* 默认对象缓存池回收阀值 */
|
||||
#define DEFAULT_OBJECT_CACHE_POOL_HOLD 600
|
||||
/* 默认内存块缓存容量 */
|
||||
#define DEFAULT_BUFFER_CACHE_CAPACITY 4096
|
||||
/* 默认内存块缓存池大小 */
|
||||
#define DEFAULT_BUFFER_CACHE_POOL_SIZE 1024
|
||||
/* 默认内存块缓存池回收阀值 */
|
||||
#define DEFAULT_BUFFER_CACHE_POOL_HOLD 1024
|
||||
|
||||
/* 使用外部垃圾回收 */
|
||||
#define USE_EXTERNAL_GC 1
|
||||
|
||||
|
||||
#define SysGetSystemConfig sysconf
|
||||
#define SysGetSystemInfo sysinfo
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#define SysGetPageSize getpagesize
|
||||
#define SysGetNumberOfProcessors get_nprocs
|
||||
#else
|
||||
#define SysGetPageSize() sysconf(_SC_PAGESIZE)
|
||||
#define SysGetNumberOfProcessors() sysconf(_SC_NPROCESSORS_ONLN)
|
||||
#endif
|
||||
|
||||
#define SYS_PAGE_SIZE GetSysPageSize()
|
||||
|
||||
#define PROCESSOR_COUNT (::SysGetNumberOfProcessors())
|
||||
#define GetCurrentProcessId getpid
|
||||
#define SELF_PROCESS_ID (::GetCurrentProcessId())
|
||||
#define gettid() syscall(__NR_gettid)
|
||||
#define GetCurrentNativeThreadId() gettid()
|
||||
#define SELF_NATIVE_THREAD_ID (::GetCurrentNativeThreadId())
|
||||
#define GetCurrentThreadId pthread_self
|
||||
#define SELF_THREAD_ID (::GetCurrentThreadId())
|
||||
#define IsSameThread(tid1, tid2) pthread_equal((tid1), (tid2))
|
||||
#define IsSelfThread(tid) IsSameThread((tid), SELF_THREAD_ID)
|
||||
inline BOOL IsSameNativeThread(pid_t pid1, pid_t pid2)
|
||||
{return (pid1 == pid2);}
|
||||
#define IsSelfNativeThread(pid) IsSameNativeThread((pid), SELF_PROCESS_ID)
|
||||
#define DEFAULT_WORKER_THREAD_COUNT GetDefaultWorkerThreadCount()
|
||||
|
||||
// Yield
|
||||
#if defined(__cplusplus)
|
||||
#include <thread>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
std::this_thread::yield();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
YieldProcessor();
|
||||
}
|
||||
#elif defined(__SSE2__)
|
||||
#include <emmintrin.h>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
_mm_pause();
|
||||
}
|
||||
#elif (defined(__GNUC__) || defined(__clang__)) && \
|
||||
(defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__armel__) || defined(__ARMEL__) || \
|
||||
defined(__aarch64__) || defined(__powerpc__) || defined(__ppc__) || defined(__PPC__))
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
__asm__ volatile ("pause" ::: "memory");
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
__asm__ volatile("wfe");
|
||||
}
|
||||
#elif (defined(__arm__) && __ARM_ARCH__ >= 7)
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
__asm__ volatile("yield" ::: "memory");
|
||||
}
|
||||
#elif defined(__powerpc__) || defined(__ppc__) || defined(__PPC__)
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
__asm__ __volatile__ ("or 27,27,27" ::: "memory");
|
||||
}
|
||||
#elif defined(__armel__) || defined(__ARMEL__)
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
__asm__ volatile ("nop" ::: "memory");
|
||||
}
|
||||
#endif
|
||||
#elif defined(__sun)
|
||||
// Fallback for other archs
|
||||
#include <synch.h>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
smt_pause();
|
||||
}
|
||||
#elif defined(__wasi__)
|
||||
#include <sched.h>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
sched_yield();
|
||||
}
|
||||
#else
|
||||
#include <unistd.h>
|
||||
static inline void __atomic_yield()
|
||||
{
|
||||
sleep(0);
|
||||
}
|
||||
#endif // Yield
|
||||
|
||||
#define YieldProcessor __atomic_yield
|
||||
#define SwitchToThread sched_yield
|
||||
|
||||
inline void __asm_nop() {__asm__ __volatile__("nop" : : : "memory");}
|
||||
inline void __asm_rep_nop() {__asm__ __volatile__("rep; nop" : : : "memory");}
|
||||
|
||||
DWORD GetSysPageSize();
|
||||
DWORD GetKernelVersion();
|
||||
BOOL IsKernelVersionAbove(BYTE major, BYTE minor, BYTE revise);
|
||||
DWORD GetDefaultWorkerThreadCount();
|
||||
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
#include<android/api-level.h>
|
||||
|
||||
#if !defined(EFD_SEMAPHORE)
|
||||
#define EFD_SEMAPHORE 00000001
|
||||
#endif
|
||||
|
||||
#define pthread_cancel(t)
|
||||
|
||||
#if defined(__ANDROID_API__)
|
||||
#if (__ANDROID_API__ < 21)
|
||||
|
||||
#define ppoll(fd, nfds, ptmspec, sig) poll((fd), (nfds), ((ptmspec) == nullptr) ? -1 : ((ptmspec)->tv_sec * 1000 + (ptmspec)->tv_nsec / 1000000))
|
||||
#define epoll_create1(flag) epoll_create(32)
|
||||
#define epoll_pwait(epfd, events, maxevents, timeout, sigmask) epoll_wait((epfd), (events), (maxevents), (timeout))
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
50
common/Thread.cpp
Normal file
50
common/Thread.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
static __thread BOOL s_tlsInterrupt = FALSE;
|
||||
BOOL __CThread_Interrupt_::sm_bInitFlag = __CThread_Interrupt_::InitSigAction();
|
||||
|
||||
__CThread_Interrupt_::~__CThread_Interrupt_ () {s_tlsInterrupt = FALSE;}
|
||||
BOOL __CThread_Interrupt_::IsInterrupted () {return s_tlsInterrupt;}
|
||||
|
||||
BOOL __CThread_Interrupt_::InitSigAction()
|
||||
{
|
||||
struct sigaction act;
|
||||
sigemptyset(&act.sa_mask);
|
||||
|
||||
act.sa_handler = SignalHandler;
|
||||
act.sa_flags = 0;
|
||||
|
||||
if(IS_HAS_ERROR(sigaction(SIG_NO_INTERRUPT, &act, nullptr)))
|
||||
ERROR_ABORT();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void __CThread_Interrupt_::SignalHandler(int sig)
|
||||
{
|
||||
if(sig == SIG_NO_INTERRUPT)
|
||||
s_tlsInterrupt = TRUE;
|
||||
}
|
||||
612
common/Thread.h
Normal file
612
common/Thread.h
Normal file
@@ -0,0 +1,612 @@
|
||||
/*
|
||||
* Copyright: JessMA Open Source (ldcsaa@gmail.com)
|
||||
*
|
||||
* Author : Bruce Liang
|
||||
* Website : https://github.com/ldcsaa
|
||||
* Project : https://github.com/ldcsaa/HP-Socket
|
||||
* Blog : http://www.cnblogs.com/ldcsaa
|
||||
* Wiki : http://www.oschina.net/p/hp-socket
|
||||
* QQ Group : 44636872, 75375912
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hpsocket/GlobalDef.h"
|
||||
#include "hpsocket/GlobalErrno.h"
|
||||
#include "RWLock.h"
|
||||
#include "STLHelper.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <utility>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/* Used to retry syscalls that can return EINTR. */
|
||||
#define NO_EINTR_EXCEPT_THR_INTR(exp) ({ \
|
||||
long int _rc; \
|
||||
do {_rc = (long int)(exp);} \
|
||||
while (IS_HAS_ERROR(_rc) && IS_INTR_ERROR() \
|
||||
&& !::IsThreadInterrupted()); \
|
||||
_rc; })
|
||||
|
||||
#define NO_EINTR_EXCEPT_THR_INTR_INT(exp) ((int)NO_EINTR_EXCEPT_THR_INTR(exp))
|
||||
|
||||
class __CThread_Interrupt_
|
||||
{
|
||||
public:
|
||||
static const int SIG_NO_INTERRUPT = (_NSIG - 5);
|
||||
|
||||
private:
|
||||
friend BOOL IsThreadInterrupted();
|
||||
template<typename T, typename P, typename R> friend class CThread;
|
||||
|
||||
private:
|
||||
static BOOL IsInterrupted();
|
||||
static BOOL InitSigAction();
|
||||
static void SignalHandler(int sig);
|
||||
|
||||
private:
|
||||
~__CThread_Interrupt_();
|
||||
|
||||
private:
|
||||
static BOOL sm_bInitFlag;
|
||||
};
|
||||
|
||||
inline BOOL IsThreadInterrupted() {return __CThread_Interrupt_::IsInterrupted();}
|
||||
|
||||
class __CFakeRunnerClass_ {};
|
||||
|
||||
template<class T, class P = VOID, class R = UINT_PTR> class CThread
|
||||
{
|
||||
public:
|
||||
|
||||
using F = R (T::*)(P*);
|
||||
using SF = R (*)(P*);
|
||||
|
||||
struct TWorker
|
||||
{
|
||||
CThread* m_pThread;
|
||||
BOOL m_bDetach;
|
||||
|
||||
T* m_pRunner;
|
||||
F m_pFunc;
|
||||
P* m_pArg;
|
||||
|
||||
public:
|
||||
|
||||
TWorker(CThread* pThread, BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
|
||||
: m_pThread(pThread)
|
||||
{
|
||||
Reset(bDetach, pRunner, pFunc, pArg);
|
||||
}
|
||||
|
||||
void Reset(BOOL bDetach = FALSE, T* pRunner = nullptr, F pFunc = nullptr, P* pArg = nullptr)
|
||||
{
|
||||
m_bDetach = bDetach;
|
||||
m_pRunner = pRunner;
|
||||
m_pFunc = pFunc;
|
||||
m_pArg = pArg;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
template<typename T_, typename R_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value && !is_void<R_>::value>>
|
||||
PVOID Run(T_*, R_*)
|
||||
{
|
||||
return (PVOID)(UINT_PTR)((m_pRunner->*m_pFunc)(m_pArg));
|
||||
}
|
||||
|
||||
template<typename T_, typename = enable_if_t<!is_same<T_, __CFakeRunnerClass_>::value>>
|
||||
PVOID Run(T_*, PVOID)
|
||||
{
|
||||
(m_pRunner->*m_pFunc)(m_pArg);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename R_, typename = enable_if_t<!is_void<R_>::value>>
|
||||
PVOID Run(__CFakeRunnerClass_*, R_*)
|
||||
{
|
||||
return (PVOID)(UINT_PTR)(*(SF*)&m_pFunc)(m_pArg);
|
||||
}
|
||||
|
||||
PVOID Run(__CFakeRunnerClass_*, VOID*)
|
||||
{
|
||||
(*(SF*)&m_pFunc)(m_pArg);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
friend struct TWorker;
|
||||
|
||||
public:
|
||||
|
||||
BOOL Start(SF pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
|
||||
{
|
||||
return Start((__CFakeRunnerClass_*)nullptr, *(F*)&pFunc, pArg, bDetach, pAttr);
|
||||
}
|
||||
|
||||
BOOL Start(T* pRunner, F pFunc, P* pArg = nullptr, BOOL bDetach = FALSE, const pthread_attr_t* pAttr = nullptr)
|
||||
{
|
||||
int rs = ERROR_INVALID_STATE;
|
||||
|
||||
if(IsRunning())
|
||||
::SetLastError(rs);
|
||||
else
|
||||
{
|
||||
m_Worker.Reset(bDetach, pRunner, pFunc, pArg);
|
||||
|
||||
SetRunning(TRUE);
|
||||
|
||||
rs = pthread_create(&m_ulThreadID, pAttr, ThreadProc, (PVOID)(&m_Worker));
|
||||
|
||||
if(rs != NO_ERROR)
|
||||
{
|
||||
Reset();
|
||||
::SetLastError(rs);
|
||||
}
|
||||
}
|
||||
|
||||
return (rs == NO_ERROR);
|
||||
}
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
|
||||
BOOL Cancel()
|
||||
{
|
||||
int rs = NO_ERROR;
|
||||
|
||||
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
|
||||
rs = ERROR_INVALID_STATE;
|
||||
else
|
||||
rs = pthread_cancel(m_ulThreadID);
|
||||
|
||||
if(rs != NO_ERROR)
|
||||
::SetLastError(rs);
|
||||
|
||||
return (rs == NO_ERROR);
|
||||
}
|
||||
|
||||
BOOL Join(R* pResult = nullptr, BOOL bWait = TRUE, LONG lWaitMillsec = INFINITE)
|
||||
{
|
||||
int rs = NO_ERROR;
|
||||
|
||||
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
|
||||
rs = ERROR_INVALID_STATE;
|
||||
else
|
||||
{
|
||||
if(!bWait)
|
||||
rs = pthread_tryjoin_np(m_ulThreadID, (PVOID*)pResult);
|
||||
else if(IS_INFINITE(lWaitMillsec))
|
||||
rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
|
||||
else
|
||||
{
|
||||
timespec ts;
|
||||
::GetFutureTimespec(lWaitMillsec, ts, CLOCK_REALTIME);
|
||||
|
||||
rs = pthread_timedjoin_np(m_ulThreadID, (PVOID*)pResult, &ts);
|
||||
}
|
||||
}
|
||||
|
||||
if(rs == NO_ERROR)
|
||||
SetRunning(FALSE);
|
||||
else
|
||||
::SetLastError(rs);
|
||||
|
||||
return (rs == NO_ERROR);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
BOOL Cancel()
|
||||
{
|
||||
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL Join(R* pResult = nullptr)
|
||||
{
|
||||
int rs = NO_ERROR;
|
||||
|
||||
if(!IsRunning() || ::IsSelfThread(m_ulThreadID))
|
||||
rs = ERROR_INVALID_STATE;
|
||||
else
|
||||
rs = pthread_join(m_ulThreadID, (PVOID*)pResult);
|
||||
|
||||
if(rs == NO_ERROR)
|
||||
SetRunning(FALSE);
|
||||
else
|
||||
::SetLastError(rs);
|
||||
|
||||
return (rs == NO_ERROR);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOL Detach()
|
||||
{
|
||||
int rs = NO_ERROR;
|
||||
|
||||
if(!IsRunning())
|
||||
rs = ERROR_INVALID_STATE;
|
||||
else
|
||||
rs = pthread_detach(m_ulThreadID);
|
||||
|
||||
if(rs == NO_ERROR)
|
||||
Reset();
|
||||
else
|
||||
::SetLastError(rs);
|
||||
|
||||
return (rs == NO_ERROR);
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
SetRunning(FALSE);
|
||||
|
||||
m_ulThreadID = 0;
|
||||
m_lNativeID = 0;
|
||||
|
||||
m_Worker.Reset();
|
||||
}
|
||||
|
||||
BOOL Interrupt()
|
||||
{
|
||||
if(!IsRunning())
|
||||
{
|
||||
SetLastError(ERROR_INVALID_STATE);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return (IS_NO_ERROR(pthread_kill(m_ulThreadID, __CThread_Interrupt_::SIG_NO_INTERRUPT)));
|
||||
}
|
||||
|
||||
void SetRunning(BOOL bRunning) {m_bRunning = bRunning;}
|
||||
|
||||
BOOL IsRunning () const {return m_bRunning;}
|
||||
T* GetRunner () const {return m_Worker.m_pRunner;}
|
||||
F GetFunc () const {return m_Worker.m_pFunc;}
|
||||
SF GetSFunc () const {return *(SF*)&m_Worker.m_pFunc;}
|
||||
P* GetArg () const {return m_Worker.m_pArg;}
|
||||
THR_ID GetThreadID () const {return m_ulThreadID;}
|
||||
NTHR_ID GetNativeID () const {return m_lNativeID;}
|
||||
|
||||
BOOL IsInMyThread () const {return IsMyThreadID(SELF_THREAD_ID);}
|
||||
BOOL IsMyThreadID (THR_ID ulThreadID) const {return ::IsSameThread(ulThreadID, m_ulThreadID);}
|
||||
BOOL IsMyNativeThreadID (NTHR_ID lNativeID) const {return ::IsSameNativeThread(lNativeID, m_lNativeID);}
|
||||
|
||||
private:
|
||||
|
||||
static PVOID ThreadProc(LPVOID pv)
|
||||
{
|
||||
UnmaskInterruptSignal();
|
||||
|
||||
__CThread_Interrupt_ tlsInterrupt;
|
||||
|
||||
TWorker* pWorker = (TWorker*)pv;
|
||||
|
||||
if(pWorker->m_bDetach)
|
||||
pWorker->m_pThread->Detach();
|
||||
else
|
||||
pWorker->m_pThread->m_lNativeID = SELF_NATIVE_THREAD_ID;
|
||||
|
||||
PVOID pResult = pWorker->Run((T*)nullptr, (R*)nullptr);
|
||||
|
||||
return pResult;
|
||||
}
|
||||
|
||||
static void UnmaskInterruptSignal()
|
||||
{
|
||||
sigset_t ss;
|
||||
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, __CThread_Interrupt_::SIG_NO_INTERRUPT);
|
||||
|
||||
pthread_sigmask(SIG_UNBLOCK, &ss, nullptr);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CThread()
|
||||
: m_Worker(this)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
virtual ~CThread()
|
||||
{
|
||||
if(IsRunning())
|
||||
{
|
||||
Interrupt();
|
||||
Join(nullptr);
|
||||
}
|
||||
|
||||
ASSERT(!IsRunning());
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CThread)
|
||||
|
||||
private:
|
||||
|
||||
THR_ID m_ulThreadID;
|
||||
NTHR_ID m_lNativeID;
|
||||
BOOL m_bRunning;
|
||||
|
||||
TWorker m_Worker;
|
||||
};
|
||||
|
||||
template<class P = VOID, class R = UINT_PTR> using CStaticThread = CThread<__CFakeRunnerClass_, P, R>;
|
||||
|
||||
template<class T> class CTlsObj
|
||||
{
|
||||
using TLocalMap = unordered_map<THR_ID, T*>;
|
||||
|
||||
public:
|
||||
|
||||
T* TryGet()
|
||||
{
|
||||
T* pValue = nullptr;
|
||||
|
||||
{
|
||||
CReadLock locallock(m_lock);
|
||||
auto it = m_map.find(SELF_THREAD_ID);
|
||||
|
||||
if(it != m_map.end())
|
||||
pValue = it->second;
|
||||
}
|
||||
|
||||
return pValue;
|
||||
}
|
||||
|
||||
template<typename ... _Con_Param> T* Get(_Con_Param&& ... construct_args)
|
||||
{
|
||||
T* pValue = TryGet();
|
||||
|
||||
if(pValue == nullptr)
|
||||
{
|
||||
pValue = Construct(forward<_Con_Param>(construct_args) ...);
|
||||
|
||||
CWriteLock locallock(m_lock);
|
||||
m_map[SELF_THREAD_ID] = pValue;
|
||||
}
|
||||
|
||||
return pValue;
|
||||
}
|
||||
|
||||
template<typename ... _Con_Param> T& GetRef(_Con_Param&& ... construct_args)
|
||||
{
|
||||
return *Get(forward<_Con_Param>(construct_args) ...);
|
||||
}
|
||||
|
||||
T* SetNewAndGetOld(T* pValue)
|
||||
{
|
||||
T* pOldValue = TryGet();
|
||||
|
||||
if(pValue != pOldValue)
|
||||
{
|
||||
if(pValue == nullptr)
|
||||
DoRemove();
|
||||
else
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
m_map[SELF_THREAD_ID] = pValue;
|
||||
}
|
||||
}
|
||||
|
||||
return pOldValue;
|
||||
}
|
||||
|
||||
void Set(T* pValue)
|
||||
{
|
||||
T* pOldValue = SetNewAndGetOld(pValue);
|
||||
|
||||
if(pValue != pOldValue)
|
||||
DoDelete(pOldValue);
|
||||
}
|
||||
|
||||
void Remove()
|
||||
{
|
||||
T* pValue = TryGet();
|
||||
|
||||
if(pValue != nullptr)
|
||||
{
|
||||
DoDelete(pValue);
|
||||
DoRemove();
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
|
||||
if(!IsEmpty())
|
||||
{
|
||||
for(auto it = m_map.begin(), end = m_map.end(); it != end; ++it)
|
||||
DoDelete(it->second);
|
||||
|
||||
m_map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
TLocalMap& GetLocalMap() {return m_map;}
|
||||
const TLocalMap& GetLocalMap() const {return m_map;}
|
||||
|
||||
CTlsObj& operator = (T* p) {Set(p); return *this;}
|
||||
T* operator -> () {return Get();}
|
||||
const T* operator -> () const {return Get();}
|
||||
T& operator * () {return GetRef();}
|
||||
const T& operator * () const {return GetRef();}
|
||||
size_t Size () const {return m_map.size();}
|
||||
bool IsEmpty() const {return m_map.empty();}
|
||||
|
||||
private:
|
||||
|
||||
inline void DoRemove()
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
m_map.erase(SELF_THREAD_ID);
|
||||
}
|
||||
|
||||
static inline void DoDelete(T* pValue)
|
||||
{
|
||||
if(pValue != nullptr)
|
||||
delete pValue;
|
||||
}
|
||||
|
||||
template<typename ... _Con_Param> static inline T* Construct(_Con_Param&& ... construct_args)
|
||||
{
|
||||
return new T(forward<_Con_Param>(construct_args) ...);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CTlsObj()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CTlsObj(T* pValue)
|
||||
{
|
||||
Set(pValue);
|
||||
}
|
||||
|
||||
~CTlsObj()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CSimpleRWLock m_lock;
|
||||
TLocalMap m_map;
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CTlsObj)
|
||||
};
|
||||
|
||||
template<class T> class CTlsSimple
|
||||
{
|
||||
using TLocalMap = unordered_map<THR_ID, T>;
|
||||
|
||||
static const T DEFAULT = (T)(0);
|
||||
|
||||
public:
|
||||
|
||||
BOOL TryGet(T& tValue)
|
||||
{
|
||||
BOOL isOK = FALSE;
|
||||
|
||||
{
|
||||
CReadLock locallock(m_lock);
|
||||
auto it = m_map.find(SELF_THREAD_ID);
|
||||
|
||||
if(it != m_map.end())
|
||||
{
|
||||
tValue = it->second;
|
||||
isOK = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return isOK;
|
||||
}
|
||||
|
||||
T Get(T tDefault = DEFAULT)
|
||||
{
|
||||
T tValue;
|
||||
|
||||
if(TryGet(tValue))
|
||||
return tValue;
|
||||
|
||||
Set(tDefault);
|
||||
|
||||
return tDefault;
|
||||
}
|
||||
|
||||
T SetNewAndGetOld(T tValue)
|
||||
{
|
||||
T tOldValue;
|
||||
|
||||
if(!TryGet(tOldValue))
|
||||
tOldValue = DEFAULT;
|
||||
else if(tValue != tOldValue)
|
||||
Set(tValue);
|
||||
|
||||
return tOldValue;
|
||||
}
|
||||
|
||||
void Set(T tValue)
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
m_map[SELF_THREAD_ID] = tValue;
|
||||
}
|
||||
|
||||
void Remove()
|
||||
{
|
||||
T tValue;
|
||||
|
||||
if(TryGet(tValue))
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
m_map.erase(SELF_THREAD_ID);
|
||||
}
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
CWriteLock locallock(m_lock);
|
||||
|
||||
if(!IsEmpty())
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
TLocalMap& GetLocalMap() {return m_map;}
|
||||
const TLocalMap& GetLocalMap() const {return m_map;}
|
||||
|
||||
CTlsSimple& operator = (T t) {Set(t); return *this;}
|
||||
BOOL operator == (T t) {return Get() == t;}
|
||||
BOOL operator != (T t) {return Get() != t;}
|
||||
BOOL operator >= (T t) {return Get() >= t;}
|
||||
BOOL operator <= (T t) {return Get() <= t;}
|
||||
BOOL operator > (T t) {return Get() > t;}
|
||||
BOOL operator < (T t) {return Get() < t;}
|
||||
|
||||
size_t Size () const {return m_map.size();}
|
||||
bool IsEmpty() const {return m_map.empty();}
|
||||
|
||||
public:
|
||||
|
||||
CTlsSimple()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
CTlsSimple(T tValue)
|
||||
{
|
||||
Set(tValue);
|
||||
}
|
||||
|
||||
~CTlsSimple()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
DECLARE_NO_COPY_CLASS(CTlsSimple)
|
||||
|
||||
private:
|
||||
|
||||
CSimpleRWLock m_lock;
|
||||
TLocalMap m_map;
|
||||
};
|
||||
11
common/http/Readme.txt
Normal file
11
common/http/Readme.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
http_parser Modifications
|
||||
--------------------
|
||||
1. move 'enum state' from http_parser.c to http_parser.h
|
||||
3. http_parser.c ignore warning: "-Wconversion", "-Wsign-conversion"
|
||||
|
||||
llhttp Modifications
|
||||
--------------------
|
||||
1. llhttp_api.c ignore warning: "-Wconversion", "-Wsign-conversion"
|
||||
1. llhttp_url.c ignore warning: "-Wconversion", "-Wsign-conversion"
|
||||
2. llhttp_internal.c ignore warning: "-Wconversion", "-Wsign-conversion" "-Wunused-variable" "-Wunreachable-code"
|
||||
3. llhttp.h, llhttp_url.h : LLHTTP_STRICT_MODE set default value 1
|
||||
903
common/http/llhttp.h
Normal file
903
common/http/llhttp.h
Normal file
@@ -0,0 +1,903 @@
|
||||
|
||||
#ifndef INCLUDE_LLHTTP_H_
|
||||
#define INCLUDE_LLHTTP_H_
|
||||
|
||||
#define LLHTTP_VERSION_MAJOR 9
|
||||
#define LLHTTP_VERSION_MINOR 2
|
||||
#define LLHTTP_VERSION_PATCH 1
|
||||
|
||||
#ifndef INCLUDE_LLHTTP_ITSELF_H_
|
||||
#define INCLUDE_LLHTTP_ITSELF_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct llhttp__internal_s llhttp__internal_t;
|
||||
struct llhttp__internal_s {
|
||||
int32_t _index;
|
||||
void* _span_pos0;
|
||||
void* _span_cb0;
|
||||
int32_t error;
|
||||
const char* reason;
|
||||
const char* error_pos;
|
||||
void* data;
|
||||
void* _current;
|
||||
uint64_t content_length;
|
||||
uint8_t type;
|
||||
uint8_t method;
|
||||
uint8_t http_major;
|
||||
uint8_t http_minor;
|
||||
uint8_t header_state;
|
||||
uint16_t lenient_flags;
|
||||
uint8_t upgrade;
|
||||
uint8_t finish;
|
||||
uint16_t flags;
|
||||
uint16_t status_code;
|
||||
uint8_t initial_message_completed;
|
||||
void* settings;
|
||||
};
|
||||
|
||||
int llhttp__internal_init(llhttp__internal_t* s);
|
||||
int llhttp__internal_execute(llhttp__internal_t* s, const char* p, const char* endp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* INCLUDE_LLHTTP_ITSELF_H_ */
|
||||
|
||||
|
||||
#ifndef LLLLHTTP_C_HEADERS_
|
||||
#define LLLLHTTP_C_HEADERS_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum llhttp_errno {
|
||||
HPE_OK = 0,
|
||||
HPE_INTERNAL = 1,
|
||||
HPE_STRICT = 2,
|
||||
HPE_CR_EXPECTED = 25,
|
||||
HPE_LF_EXPECTED = 3,
|
||||
HPE_UNEXPECTED_CONTENT_LENGTH = 4,
|
||||
HPE_UNEXPECTED_SPACE = 30,
|
||||
HPE_CLOSED_CONNECTION = 5,
|
||||
HPE_INVALID_METHOD = 6,
|
||||
HPE_INVALID_URL = 7,
|
||||
HPE_INVALID_CONSTANT = 8,
|
||||
HPE_INVALID_VERSION = 9,
|
||||
HPE_INVALID_HEADER_TOKEN = 10,
|
||||
HPE_INVALID_CONTENT_LENGTH = 11,
|
||||
HPE_INVALID_CHUNK_SIZE = 12,
|
||||
HPE_INVALID_STATUS = 13,
|
||||
HPE_INVALID_EOF_STATE = 14,
|
||||
HPE_INVALID_TRANSFER_ENCODING = 15,
|
||||
HPE_CB_MESSAGE_BEGIN = 16,
|
||||
HPE_CB_HEADERS_COMPLETE = 17,
|
||||
HPE_CB_MESSAGE_COMPLETE = 18,
|
||||
HPE_CB_CHUNK_HEADER = 19,
|
||||
HPE_CB_CHUNK_COMPLETE = 20,
|
||||
HPE_PAUSED = 21,
|
||||
HPE_PAUSED_UPGRADE = 22,
|
||||
HPE_PAUSED_H2_UPGRADE = 23,
|
||||
HPE_USER = 24,
|
||||
HPE_CB_URL_COMPLETE = 26,
|
||||
HPE_CB_STATUS_COMPLETE = 27,
|
||||
HPE_CB_METHOD_COMPLETE = 32,
|
||||
HPE_CB_VERSION_COMPLETE = 33,
|
||||
HPE_CB_HEADER_FIELD_COMPLETE = 28,
|
||||
HPE_CB_HEADER_VALUE_COMPLETE = 29,
|
||||
HPE_CB_CHUNK_EXTENSION_NAME_COMPLETE = 34,
|
||||
HPE_CB_CHUNK_EXTENSION_VALUE_COMPLETE = 35,
|
||||
HPE_CB_RESET = 31
|
||||
};
|
||||
typedef enum llhttp_errno llhttp_errno_t;
|
||||
|
||||
enum llhttp_flags {
|
||||
F_CONNECTION_KEEP_ALIVE = 0x1,
|
||||
F_CONNECTION_CLOSE = 0x2,
|
||||
F_CONNECTION_UPGRADE = 0x4,
|
||||
F_CHUNKED = 0x8,
|
||||
F_UPGRADE = 0x10,
|
||||
F_CONTENT_LENGTH = 0x20,
|
||||
F_SKIPBODY = 0x40,
|
||||
F_TRAILING = 0x80,
|
||||
F_TRANSFER_ENCODING = 0x200
|
||||
};
|
||||
typedef enum llhttp_flags llhttp_flags_t;
|
||||
|
||||
enum llhttp_lenient_flags {
|
||||
LENIENT_HEADERS = 0x1,
|
||||
LENIENT_CHUNKED_LENGTH = 0x2,
|
||||
LENIENT_KEEP_ALIVE = 0x4,
|
||||
LENIENT_TRANSFER_ENCODING = 0x8,
|
||||
LENIENT_VERSION = 0x10,
|
||||
LENIENT_DATA_AFTER_CLOSE = 0x20,
|
||||
LENIENT_OPTIONAL_LF_AFTER_CR = 0x40,
|
||||
LENIENT_OPTIONAL_CRLF_AFTER_CHUNK = 0x80,
|
||||
LENIENT_OPTIONAL_CR_BEFORE_LF = 0x100,
|
||||
LENIENT_SPACES_AFTER_CHUNK_SIZE = 0x200
|
||||
};
|
||||
typedef enum llhttp_lenient_flags llhttp_lenient_flags_t;
|
||||
|
||||
enum llhttp_type {
|
||||
HTTP_BOTH = 0,
|
||||
HTTP_REQUEST = 1,
|
||||
HTTP_RESPONSE = 2
|
||||
};
|
||||
typedef enum llhttp_type llhttp_type_t;
|
||||
|
||||
enum llhttp_finish {
|
||||
HTTP_FINISH_SAFE = 0,
|
||||
HTTP_FINISH_SAFE_WITH_CB = 1,
|
||||
HTTP_FINISH_UNSAFE = 2
|
||||
};
|
||||
typedef enum llhttp_finish llhttp_finish_t;
|
||||
|
||||
enum llhttp_method {
|
||||
HTTP_DELETE = 0,
|
||||
HTTP_GET = 1,
|
||||
HTTP_HEAD = 2,
|
||||
HTTP_POST = 3,
|
||||
HTTP_PUT = 4,
|
||||
HTTP_CONNECT = 5,
|
||||
HTTP_OPTIONS = 6,
|
||||
HTTP_TRACE = 7,
|
||||
HTTP_COPY = 8,
|
||||
HTTP_LOCK = 9,
|
||||
HTTP_MKCOL = 10,
|
||||
HTTP_MOVE = 11,
|
||||
HTTP_PROPFIND = 12,
|
||||
HTTP_PROPPATCH = 13,
|
||||
HTTP_SEARCH = 14,
|
||||
HTTP_UNLOCK = 15,
|
||||
HTTP_BIND = 16,
|
||||
HTTP_REBIND = 17,
|
||||
HTTP_UNBIND = 18,
|
||||
HTTP_ACL = 19,
|
||||
HTTP_REPORT = 20,
|
||||
HTTP_MKACTIVITY = 21,
|
||||
HTTP_CHECKOUT = 22,
|
||||
HTTP_MERGE = 23,
|
||||
HTTP_MSEARCH = 24,
|
||||
HTTP_NOTIFY = 25,
|
||||
HTTP_SUBSCRIBE = 26,
|
||||
HTTP_UNSUBSCRIBE = 27,
|
||||
HTTP_PATCH = 28,
|
||||
HTTP_PURGE = 29,
|
||||
HTTP_MKCALENDAR = 30,
|
||||
HTTP_LINK = 31,
|
||||
HTTP_UNLINK = 32,
|
||||
HTTP_SOURCE = 33,
|
||||
HTTP_PRI = 34,
|
||||
HTTP_DESCRIBE = 35,
|
||||
HTTP_ANNOUNCE = 36,
|
||||
HTTP_SETUP = 37,
|
||||
HTTP_PLAY = 38,
|
||||
HTTP_PAUSE = 39,
|
||||
HTTP_TEARDOWN = 40,
|
||||
HTTP_GET_PARAMETER = 41,
|
||||
HTTP_SET_PARAMETER = 42,
|
||||
HTTP_REDIRECT = 43,
|
||||
HTTP_RECORD = 44,
|
||||
HTTP_FLUSH = 45,
|
||||
HTTP_QUERY = 46
|
||||
};
|
||||
typedef enum llhttp_method llhttp_method_t;
|
||||
|
||||
enum llhttp_status {
|
||||
HTTP_STATUS_CONTINUE = 100,
|
||||
HTTP_STATUS_SWITCHING_PROTOCOLS = 101,
|
||||
HTTP_STATUS_PROCESSING = 102,
|
||||
HTTP_STATUS_EARLY_HINTS = 103,
|
||||
HTTP_STATUS_RESPONSE_IS_STALE = 110,
|
||||
HTTP_STATUS_REVALIDATION_FAILED = 111,
|
||||
HTTP_STATUS_DISCONNECTED_OPERATION = 112,
|
||||
HTTP_STATUS_HEURISTIC_EXPIRATION = 113,
|
||||
HTTP_STATUS_MISCELLANEOUS_WARNING = 199,
|
||||
HTTP_STATUS_OK = 200,
|
||||
HTTP_STATUS_CREATED = 201,
|
||||
HTTP_STATUS_ACCEPTED = 202,
|
||||
HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
HTTP_STATUS_NO_CONTENT = 204,
|
||||
HTTP_STATUS_RESET_CONTENT = 205,
|
||||
HTTP_STATUS_PARTIAL_CONTENT = 206,
|
||||
HTTP_STATUS_MULTI_STATUS = 207,
|
||||
HTTP_STATUS_ALREADY_REPORTED = 208,
|
||||
HTTP_STATUS_TRANSFORMATION_APPLIED = 214,
|
||||
HTTP_STATUS_IM_USED = 226,
|
||||
HTTP_STATUS_MISCELLANEOUS_PERSISTENT_WARNING = 299,
|
||||
HTTP_STATUS_MULTIPLE_CHOICES = 300,
|
||||
HTTP_STATUS_MOVED_PERMANENTLY = 301,
|
||||
HTTP_STATUS_FOUND = 302,
|
||||
HTTP_STATUS_SEE_OTHER = 303,
|
||||
HTTP_STATUS_NOT_MODIFIED = 304,
|
||||
HTTP_STATUS_USE_PROXY = 305,
|
||||
HTTP_STATUS_SWITCH_PROXY = 306,
|
||||
HTTP_STATUS_TEMPORARY_REDIRECT = 307,
|
||||
HTTP_STATUS_PERMANENT_REDIRECT = 308,
|
||||
HTTP_STATUS_BAD_REQUEST = 400,
|
||||
HTTP_STATUS_UNAUTHORIZED = 401,
|
||||
HTTP_STATUS_PAYMENT_REQUIRED = 402,
|
||||
HTTP_STATUS_FORBIDDEN = 403,
|
||||
HTTP_STATUS_NOT_FOUND = 404,
|
||||
HTTP_STATUS_METHOD_NOT_ALLOWED = 405,
|
||||
HTTP_STATUS_NOT_ACCEPTABLE = 406,
|
||||
HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
HTTP_STATUS_REQUEST_TIMEOUT = 408,
|
||||
HTTP_STATUS_CONFLICT = 409,
|
||||
HTTP_STATUS_GONE = 410,
|
||||
HTTP_STATUS_LENGTH_REQUIRED = 411,
|
||||
HTTP_STATUS_PRECONDITION_FAILED = 412,
|
||||
HTTP_STATUS_PAYLOAD_TOO_LARGE = 413,
|
||||
HTTP_STATUS_URI_TOO_LONG = 414,
|
||||
HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416,
|
||||
HTTP_STATUS_EXPECTATION_FAILED = 417,
|
||||
HTTP_STATUS_IM_A_TEAPOT = 418,
|
||||
HTTP_STATUS_PAGE_EXPIRED = 419,
|
||||
HTTP_STATUS_ENHANCE_YOUR_CALM = 420,
|
||||
HTTP_STATUS_MISDIRECTED_REQUEST = 421,
|
||||
HTTP_STATUS_UNPROCESSABLE_ENTITY = 422,
|
||||
HTTP_STATUS_LOCKED = 423,
|
||||
HTTP_STATUS_FAILED_DEPENDENCY = 424,
|
||||
HTTP_STATUS_TOO_EARLY = 425,
|
||||
HTTP_STATUS_UPGRADE_REQUIRED = 426,
|
||||
HTTP_STATUS_PRECONDITION_REQUIRED = 428,
|
||||
HTTP_STATUS_TOO_MANY_REQUESTS = 429,
|
||||
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL = 430,
|
||||
HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
HTTP_STATUS_LOGIN_TIMEOUT = 440,
|
||||
HTTP_STATUS_NO_RESPONSE = 444,
|
||||
HTTP_STATUS_RETRY_WITH = 449,
|
||||
HTTP_STATUS_BLOCKED_BY_PARENTAL_CONTROL = 450,
|
||||
HTTP_STATUS_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||
HTTP_STATUS_CLIENT_CLOSED_LOAD_BALANCED_REQUEST = 460,
|
||||
HTTP_STATUS_INVALID_X_FORWARDED_FOR = 463,
|
||||
HTTP_STATUS_REQUEST_HEADER_TOO_LARGE = 494,
|
||||
HTTP_STATUS_SSL_CERTIFICATE_ERROR = 495,
|
||||
HTTP_STATUS_SSL_CERTIFICATE_REQUIRED = 496,
|
||||
HTTP_STATUS_HTTP_REQUEST_SENT_TO_HTTPS_PORT = 497,
|
||||
HTTP_STATUS_INVALID_TOKEN = 498,
|
||||
HTTP_STATUS_CLIENT_CLOSED_REQUEST = 499,
|
||||
HTTP_STATUS_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_STATUS_NOT_IMPLEMENTED = 501,
|
||||
HTTP_STATUS_BAD_GATEWAY = 502,
|
||||
HTTP_STATUS_SERVICE_UNAVAILABLE = 503,
|
||||
HTTP_STATUS_GATEWAY_TIMEOUT = 504,
|
||||
HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
HTTP_STATUS_VARIANT_ALSO_NEGOTIATES = 506,
|
||||
HTTP_STATUS_INSUFFICIENT_STORAGE = 507,
|
||||
HTTP_STATUS_LOOP_DETECTED = 508,
|
||||
HTTP_STATUS_BANDWIDTH_LIMIT_EXCEEDED = 509,
|
||||
HTTP_STATUS_NOT_EXTENDED = 510,
|
||||
HTTP_STATUS_NETWORK_AUTHENTICATION_REQUIRED = 511,
|
||||
HTTP_STATUS_WEB_SERVER_UNKNOWN_ERROR = 520,
|
||||
HTTP_STATUS_WEB_SERVER_IS_DOWN = 521,
|
||||
HTTP_STATUS_CONNECTION_TIMEOUT = 522,
|
||||
HTTP_STATUS_ORIGIN_IS_UNREACHABLE = 523,
|
||||
HTTP_STATUS_TIMEOUT_OCCURED = 524,
|
||||
HTTP_STATUS_SSL_HANDSHAKE_FAILED = 525,
|
||||
HTTP_STATUS_INVALID_SSL_CERTIFICATE = 526,
|
||||
HTTP_STATUS_RAILGUN_ERROR = 527,
|
||||
HTTP_STATUS_SITE_IS_OVERLOADED = 529,
|
||||
HTTP_STATUS_SITE_IS_FROZEN = 530,
|
||||
HTTP_STATUS_IDENTITY_PROVIDER_AUTHENTICATION_ERROR = 561,
|
||||
HTTP_STATUS_NETWORK_READ_TIMEOUT = 598,
|
||||
HTTP_STATUS_NETWORK_CONNECT_TIMEOUT = 599
|
||||
};
|
||||
typedef enum llhttp_status llhttp_status_t;
|
||||
|
||||
#define HTTP_ERRNO_MAP(XX) \
|
||||
XX(0, OK, OK) \
|
||||
XX(1, INTERNAL, INTERNAL) \
|
||||
XX(2, STRICT, STRICT) \
|
||||
XX(25, CR_EXPECTED, CR_EXPECTED) \
|
||||
XX(3, LF_EXPECTED, LF_EXPECTED) \
|
||||
XX(4, UNEXPECTED_CONTENT_LENGTH, UNEXPECTED_CONTENT_LENGTH) \
|
||||
XX(30, UNEXPECTED_SPACE, UNEXPECTED_SPACE) \
|
||||
XX(5, CLOSED_CONNECTION, CLOSED_CONNECTION) \
|
||||
XX(6, INVALID_METHOD, INVALID_METHOD) \
|
||||
XX(7, INVALID_URL, INVALID_URL) \
|
||||
XX(8, INVALID_CONSTANT, INVALID_CONSTANT) \
|
||||
XX(9, INVALID_VERSION, INVALID_VERSION) \
|
||||
XX(10, INVALID_HEADER_TOKEN, INVALID_HEADER_TOKEN) \
|
||||
XX(11, INVALID_CONTENT_LENGTH, INVALID_CONTENT_LENGTH) \
|
||||
XX(12, INVALID_CHUNK_SIZE, INVALID_CHUNK_SIZE) \
|
||||
XX(13, INVALID_STATUS, INVALID_STATUS) \
|
||||
XX(14, INVALID_EOF_STATE, INVALID_EOF_STATE) \
|
||||
XX(15, INVALID_TRANSFER_ENCODING, INVALID_TRANSFER_ENCODING) \
|
||||
XX(16, CB_MESSAGE_BEGIN, CB_MESSAGE_BEGIN) \
|
||||
XX(17, CB_HEADERS_COMPLETE, CB_HEADERS_COMPLETE) \
|
||||
XX(18, CB_MESSAGE_COMPLETE, CB_MESSAGE_COMPLETE) \
|
||||
XX(19, CB_CHUNK_HEADER, CB_CHUNK_HEADER) \
|
||||
XX(20, CB_CHUNK_COMPLETE, CB_CHUNK_COMPLETE) \
|
||||
XX(21, PAUSED, PAUSED) \
|
||||
XX(22, PAUSED_UPGRADE, PAUSED_UPGRADE) \
|
||||
XX(23, PAUSED_H2_UPGRADE, PAUSED_H2_UPGRADE) \
|
||||
XX(24, USER, USER) \
|
||||
XX(26, CB_URL_COMPLETE, CB_URL_COMPLETE) \
|
||||
XX(27, CB_STATUS_COMPLETE, CB_STATUS_COMPLETE) \
|
||||
XX(32, CB_METHOD_COMPLETE, CB_METHOD_COMPLETE) \
|
||||
XX(33, CB_VERSION_COMPLETE, CB_VERSION_COMPLETE) \
|
||||
XX(28, CB_HEADER_FIELD_COMPLETE, CB_HEADER_FIELD_COMPLETE) \
|
||||
XX(29, CB_HEADER_VALUE_COMPLETE, CB_HEADER_VALUE_COMPLETE) \
|
||||
XX(34, CB_CHUNK_EXTENSION_NAME_COMPLETE, CB_CHUNK_EXTENSION_NAME_COMPLETE) \
|
||||
XX(35, CB_CHUNK_EXTENSION_VALUE_COMPLETE, CB_CHUNK_EXTENSION_VALUE_COMPLETE) \
|
||||
XX(31, CB_RESET, CB_RESET) \
|
||||
|
||||
|
||||
#define HTTP_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
XX(33, SOURCE, SOURCE) \
|
||||
XX(46, QUERY, QUERY) \
|
||||
|
||||
|
||||
#define RTSP_METHOD_MAP(XX) \
|
||||
XX(1, GET, GET) \
|
||||
XX(3, POST, POST) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(35, DESCRIBE, DESCRIBE) \
|
||||
XX(36, ANNOUNCE, ANNOUNCE) \
|
||||
XX(37, SETUP, SETUP) \
|
||||
XX(38, PLAY, PLAY) \
|
||||
XX(39, PAUSE, PAUSE) \
|
||||
XX(40, TEARDOWN, TEARDOWN) \
|
||||
XX(41, GET_PARAMETER, GET_PARAMETER) \
|
||||
XX(42, SET_PARAMETER, SET_PARAMETER) \
|
||||
XX(43, REDIRECT, REDIRECT) \
|
||||
XX(44, RECORD, RECORD) \
|
||||
XX(45, FLUSH, FLUSH) \
|
||||
|
||||
|
||||
#define HTTP_ALL_METHOD_MAP(XX) \
|
||||
XX(0, DELETE, DELETE) \
|
||||
XX(1, GET, GET) \
|
||||
XX(2, HEAD, HEAD) \
|
||||
XX(3, POST, POST) \
|
||||
XX(4, PUT, PUT) \
|
||||
XX(5, CONNECT, CONNECT) \
|
||||
XX(6, OPTIONS, OPTIONS) \
|
||||
XX(7, TRACE, TRACE) \
|
||||
XX(8, COPY, COPY) \
|
||||
XX(9, LOCK, LOCK) \
|
||||
XX(10, MKCOL, MKCOL) \
|
||||
XX(11, MOVE, MOVE) \
|
||||
XX(12, PROPFIND, PROPFIND) \
|
||||
XX(13, PROPPATCH, PROPPATCH) \
|
||||
XX(14, SEARCH, SEARCH) \
|
||||
XX(15, UNLOCK, UNLOCK) \
|
||||
XX(16, BIND, BIND) \
|
||||
XX(17, REBIND, REBIND) \
|
||||
XX(18, UNBIND, UNBIND) \
|
||||
XX(19, ACL, ACL) \
|
||||
XX(20, REPORT, REPORT) \
|
||||
XX(21, MKACTIVITY, MKACTIVITY) \
|
||||
XX(22, CHECKOUT, CHECKOUT) \
|
||||
XX(23, MERGE, MERGE) \
|
||||
XX(24, MSEARCH, M-SEARCH) \
|
||||
XX(25, NOTIFY, NOTIFY) \
|
||||
XX(26, SUBSCRIBE, SUBSCRIBE) \
|
||||
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||
XX(28, PATCH, PATCH) \
|
||||
XX(29, PURGE, PURGE) \
|
||||
XX(30, MKCALENDAR, MKCALENDAR) \
|
||||
XX(31, LINK, LINK) \
|
||||
XX(32, UNLINK, UNLINK) \
|
||||
XX(33, SOURCE, SOURCE) \
|
||||
XX(34, PRI, PRI) \
|
||||
XX(35, DESCRIBE, DESCRIBE) \
|
||||
XX(36, ANNOUNCE, ANNOUNCE) \
|
||||
XX(37, SETUP, SETUP) \
|
||||
XX(38, PLAY, PLAY) \
|
||||
XX(39, PAUSE, PAUSE) \
|
||||
XX(40, TEARDOWN, TEARDOWN) \
|
||||
XX(41, GET_PARAMETER, GET_PARAMETER) \
|
||||
XX(42, SET_PARAMETER, SET_PARAMETER) \
|
||||
XX(43, REDIRECT, REDIRECT) \
|
||||
XX(44, RECORD, RECORD) \
|
||||
XX(45, FLUSH, FLUSH) \
|
||||
XX(46, QUERY, QUERY) \
|
||||
|
||||
|
||||
#define HTTP_STATUS_MAP(XX) \
|
||||
XX(100, CONTINUE, CONTINUE) \
|
||||
XX(101, SWITCHING_PROTOCOLS, SWITCHING_PROTOCOLS) \
|
||||
XX(102, PROCESSING, PROCESSING) \
|
||||
XX(103, EARLY_HINTS, EARLY_HINTS) \
|
||||
XX(110, RESPONSE_IS_STALE, RESPONSE_IS_STALE) \
|
||||
XX(111, REVALIDATION_FAILED, REVALIDATION_FAILED) \
|
||||
XX(112, DISCONNECTED_OPERATION, DISCONNECTED_OPERATION) \
|
||||
XX(113, HEURISTIC_EXPIRATION, HEURISTIC_EXPIRATION) \
|
||||
XX(199, MISCELLANEOUS_WARNING, MISCELLANEOUS_WARNING) \
|
||||
XX(200, OK, OK) \
|
||||
XX(201, CREATED, CREATED) \
|
||||
XX(202, ACCEPTED, ACCEPTED) \
|
||||
XX(203, NON_AUTHORITATIVE_INFORMATION, NON_AUTHORITATIVE_INFORMATION) \
|
||||
XX(204, NO_CONTENT, NO_CONTENT) \
|
||||
XX(205, RESET_CONTENT, RESET_CONTENT) \
|
||||
XX(206, PARTIAL_CONTENT, PARTIAL_CONTENT) \
|
||||
XX(207, MULTI_STATUS, MULTI_STATUS) \
|
||||
XX(208, ALREADY_REPORTED, ALREADY_REPORTED) \
|
||||
XX(214, TRANSFORMATION_APPLIED, TRANSFORMATION_APPLIED) \
|
||||
XX(226, IM_USED, IM_USED) \
|
||||
XX(299, MISCELLANEOUS_PERSISTENT_WARNING, MISCELLANEOUS_PERSISTENT_WARNING) \
|
||||
XX(300, MULTIPLE_CHOICES, MULTIPLE_CHOICES) \
|
||||
XX(301, MOVED_PERMANENTLY, MOVED_PERMANENTLY) \
|
||||
XX(302, FOUND, FOUND) \
|
||||
XX(303, SEE_OTHER, SEE_OTHER) \
|
||||
XX(304, NOT_MODIFIED, NOT_MODIFIED) \
|
||||
XX(305, USE_PROXY, USE_PROXY) \
|
||||
XX(306, SWITCH_PROXY, SWITCH_PROXY) \
|
||||
XX(307, TEMPORARY_REDIRECT, TEMPORARY_REDIRECT) \
|
||||
XX(308, PERMANENT_REDIRECT, PERMANENT_REDIRECT) \
|
||||
XX(400, BAD_REQUEST, BAD_REQUEST) \
|
||||
XX(401, UNAUTHORIZED, UNAUTHORIZED) \
|
||||
XX(402, PAYMENT_REQUIRED, PAYMENT_REQUIRED) \
|
||||
XX(403, FORBIDDEN, FORBIDDEN) \
|
||||
XX(404, NOT_FOUND, NOT_FOUND) \
|
||||
XX(405, METHOD_NOT_ALLOWED, METHOD_NOT_ALLOWED) \
|
||||
XX(406, NOT_ACCEPTABLE, NOT_ACCEPTABLE) \
|
||||
XX(407, PROXY_AUTHENTICATION_REQUIRED, PROXY_AUTHENTICATION_REQUIRED) \
|
||||
XX(408, REQUEST_TIMEOUT, REQUEST_TIMEOUT) \
|
||||
XX(409, CONFLICT, CONFLICT) \
|
||||
XX(410, GONE, GONE) \
|
||||
XX(411, LENGTH_REQUIRED, LENGTH_REQUIRED) \
|
||||
XX(412, PRECONDITION_FAILED, PRECONDITION_FAILED) \
|
||||
XX(413, PAYLOAD_TOO_LARGE, PAYLOAD_TOO_LARGE) \
|
||||
XX(414, URI_TOO_LONG, URI_TOO_LONG) \
|
||||
XX(415, UNSUPPORTED_MEDIA_TYPE, UNSUPPORTED_MEDIA_TYPE) \
|
||||
XX(416, RANGE_NOT_SATISFIABLE, RANGE_NOT_SATISFIABLE) \
|
||||
XX(417, EXPECTATION_FAILED, EXPECTATION_FAILED) \
|
||||
XX(418, IM_A_TEAPOT, IM_A_TEAPOT) \
|
||||
XX(419, PAGE_EXPIRED, PAGE_EXPIRED) \
|
||||
XX(420, ENHANCE_YOUR_CALM, ENHANCE_YOUR_CALM) \
|
||||
XX(421, MISDIRECTED_REQUEST, MISDIRECTED_REQUEST) \
|
||||
XX(422, UNPROCESSABLE_ENTITY, UNPROCESSABLE_ENTITY) \
|
||||
XX(423, LOCKED, LOCKED) \
|
||||
XX(424, FAILED_DEPENDENCY, FAILED_DEPENDENCY) \
|
||||
XX(425, TOO_EARLY, TOO_EARLY) \
|
||||
XX(426, UPGRADE_REQUIRED, UPGRADE_REQUIRED) \
|
||||
XX(428, PRECONDITION_REQUIRED, PRECONDITION_REQUIRED) \
|
||||
XX(429, TOO_MANY_REQUESTS, TOO_MANY_REQUESTS) \
|
||||
XX(430, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL, REQUEST_HEADER_FIELDS_TOO_LARGE_UNOFFICIAL) \
|
||||
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, REQUEST_HEADER_FIELDS_TOO_LARGE) \
|
||||
XX(440, LOGIN_TIMEOUT, LOGIN_TIMEOUT) \
|
||||
XX(444, NO_RESPONSE, NO_RESPONSE) \
|
||||
XX(449, RETRY_WITH, RETRY_WITH) \
|
||||
XX(450, BLOCKED_BY_PARENTAL_CONTROL, BLOCKED_BY_PARENTAL_CONTROL) \
|
||||
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, UNAVAILABLE_FOR_LEGAL_REASONS) \
|
||||
XX(460, CLIENT_CLOSED_LOAD_BALANCED_REQUEST, CLIENT_CLOSED_LOAD_BALANCED_REQUEST) \
|
||||
XX(463, INVALID_X_FORWARDED_FOR, INVALID_X_FORWARDED_FOR) \
|
||||
XX(494, REQUEST_HEADER_TOO_LARGE, REQUEST_HEADER_TOO_LARGE) \
|
||||
XX(495, SSL_CERTIFICATE_ERROR, SSL_CERTIFICATE_ERROR) \
|
||||
XX(496, SSL_CERTIFICATE_REQUIRED, SSL_CERTIFICATE_REQUIRED) \
|
||||
XX(497, HTTP_REQUEST_SENT_TO_HTTPS_PORT, HTTP_REQUEST_SENT_TO_HTTPS_PORT) \
|
||||
XX(498, INVALID_TOKEN, INVALID_TOKEN) \
|
||||
XX(499, CLIENT_CLOSED_REQUEST, CLIENT_CLOSED_REQUEST) \
|
||||
XX(500, INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR) \
|
||||
XX(501, NOT_IMPLEMENTED, NOT_IMPLEMENTED) \
|
||||
XX(502, BAD_GATEWAY, BAD_GATEWAY) \
|
||||
XX(503, SERVICE_UNAVAILABLE, SERVICE_UNAVAILABLE) \
|
||||
XX(504, GATEWAY_TIMEOUT, GATEWAY_TIMEOUT) \
|
||||
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP_VERSION_NOT_SUPPORTED) \
|
||||
XX(506, VARIANT_ALSO_NEGOTIATES, VARIANT_ALSO_NEGOTIATES) \
|
||||
XX(507, INSUFFICIENT_STORAGE, INSUFFICIENT_STORAGE) \
|
||||
XX(508, LOOP_DETECTED, LOOP_DETECTED) \
|
||||
XX(509, BANDWIDTH_LIMIT_EXCEEDED, BANDWIDTH_LIMIT_EXCEEDED) \
|
||||
XX(510, NOT_EXTENDED, NOT_EXTENDED) \
|
||||
XX(511, NETWORK_AUTHENTICATION_REQUIRED, NETWORK_AUTHENTICATION_REQUIRED) \
|
||||
XX(520, WEB_SERVER_UNKNOWN_ERROR, WEB_SERVER_UNKNOWN_ERROR) \
|
||||
XX(521, WEB_SERVER_IS_DOWN, WEB_SERVER_IS_DOWN) \
|
||||
XX(522, CONNECTION_TIMEOUT, CONNECTION_TIMEOUT) \
|
||||
XX(523, ORIGIN_IS_UNREACHABLE, ORIGIN_IS_UNREACHABLE) \
|
||||
XX(524, TIMEOUT_OCCURED, TIMEOUT_OCCURED) \
|
||||
XX(525, SSL_HANDSHAKE_FAILED, SSL_HANDSHAKE_FAILED) \
|
||||
XX(526, INVALID_SSL_CERTIFICATE, INVALID_SSL_CERTIFICATE) \
|
||||
XX(527, RAILGUN_ERROR, RAILGUN_ERROR) \
|
||||
XX(529, SITE_IS_OVERLOADED, SITE_IS_OVERLOADED) \
|
||||
XX(530, SITE_IS_FROZEN, SITE_IS_FROZEN) \
|
||||
XX(561, IDENTITY_PROVIDER_AUTHENTICATION_ERROR, IDENTITY_PROVIDER_AUTHENTICATION_ERROR) \
|
||||
XX(598, NETWORK_READ_TIMEOUT, NETWORK_READ_TIMEOUT) \
|
||||
XX(599, NETWORK_CONNECT_TIMEOUT, NETWORK_CONNECT_TIMEOUT) \
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* LLLLHTTP_C_HEADERS_ */
|
||||
|
||||
|
||||
#ifndef INCLUDE_LLHTTP_API_H_
|
||||
#define INCLUDE_LLHTTP_API_H_
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__wasm__)
|
||||
#define LLHTTP_EXPORT __attribute__((visibility("default")))
|
||||
//#elif defined(_WIN32)
|
||||
//#define LLHTTP_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define LLHTTP_EXPORT
|
||||
#endif
|
||||
|
||||
typedef llhttp__internal_t llhttp_t;
|
||||
typedef struct llhttp_settings_s llhttp_settings_t;
|
||||
|
||||
typedef int (*llhttp_data_cb)(llhttp_t*, const char *at, size_t length);
|
||||
typedef int (*llhttp_cb)(llhttp_t*);
|
||||
|
||||
struct llhttp_settings_s {
|
||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||
llhttp_cb on_message_begin;
|
||||
|
||||
/* Possible return values 0, -1, HPE_USER */
|
||||
llhttp_data_cb on_url;
|
||||
llhttp_data_cb on_status;
|
||||
llhttp_data_cb on_method;
|
||||
llhttp_data_cb on_version;
|
||||
llhttp_data_cb on_header_field;
|
||||
llhttp_data_cb on_header_value;
|
||||
llhttp_data_cb on_chunk_extension_name;
|
||||
llhttp_data_cb on_chunk_extension_value;
|
||||
|
||||
/* Possible return values:
|
||||
* 0 - Proceed normally
|
||||
* 1 - Assume that request/response has no body, and proceed to parsing the
|
||||
* next message
|
||||
* 2 - Assume absence of body (as above) and make `llhttp_execute()` return
|
||||
* `HPE_PAUSED_UPGRADE`
|
||||
* -1 - Error
|
||||
* `HPE_PAUSED`
|
||||
*/
|
||||
llhttp_cb on_headers_complete;
|
||||
|
||||
/* Possible return values 0, -1, HPE_USER */
|
||||
llhttp_data_cb on_body;
|
||||
|
||||
/* Possible return values 0, -1, `HPE_PAUSED` */
|
||||
llhttp_cb on_message_complete;
|
||||
llhttp_cb on_url_complete;
|
||||
llhttp_cb on_status_complete;
|
||||
llhttp_cb on_method_complete;
|
||||
llhttp_cb on_version_complete;
|
||||
llhttp_cb on_header_field_complete;
|
||||
llhttp_cb on_header_value_complete;
|
||||
llhttp_cb on_chunk_extension_name_complete;
|
||||
llhttp_cb on_chunk_extension_value_complete;
|
||||
|
||||
/* When on_chunk_header is called, the current chunk length is stored
|
||||
* in parser->content_length.
|
||||
* Possible return values 0, -1, `HPE_PAUSED`
|
||||
*/
|
||||
llhttp_cb on_chunk_header;
|
||||
llhttp_cb on_chunk_complete;
|
||||
llhttp_cb on_reset;
|
||||
};
|
||||
|
||||
/* Initialize the parser with specific type and user settings.
|
||||
*
|
||||
* NOTE: lifetime of `settings` has to be at least the same as the lifetime of
|
||||
* the `parser` here. In practice, `settings` has to be either a static
|
||||
* variable or be allocated with `malloc`, `new`, etc.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||
const llhttp_settings_t* settings);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
llhttp_t* llhttp_alloc(llhttp_type_t type);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_free(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
uint8_t llhttp_get_type(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
uint8_t llhttp_get_http_major(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
uint8_t llhttp_get_http_minor(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
uint8_t llhttp_get_method(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
int llhttp_get_status_code(llhttp_t* parser);
|
||||
|
||||
LLHTTP_EXPORT
|
||||
uint8_t llhttp_get_upgrade(llhttp_t* parser);
|
||||
|
||||
/* Reset an already initialized parser back to the start state, preserving the
|
||||
* existing parser type, callback settings, user data, and lenient flags.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_reset(llhttp_t* parser);
|
||||
|
||||
/* Initialize the settings object */
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_settings_init(llhttp_settings_t* settings);
|
||||
|
||||
/* Parse full or partial request/response, invoking user callbacks along the
|
||||
* way.
|
||||
*
|
||||
* If any of `llhttp_data_cb` returns errno not equal to `HPE_OK` - the parsing
|
||||
* interrupts, and such errno is returned from `llhttp_execute()`. If
|
||||
* `HPE_PAUSED` was used as a errno, the execution can be resumed with
|
||||
* `llhttp_resume()` call.
|
||||
*
|
||||
* In a special case of CONNECT/Upgrade request/response `HPE_PAUSED_UPGRADE`
|
||||
* is returned after fully parsing the request/response. If the user wishes to
|
||||
* continue parsing, they need to invoke `llhttp_resume_after_upgrade()`.
|
||||
*
|
||||
* NOTE: if this function ever returns a non-pause type error, it will continue
|
||||
* to return the same error upon each successive call up until `llhttp_init()`
|
||||
* is called.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len);
|
||||
|
||||
/* This method should be called when the other side has no further bytes to
|
||||
* send (e.g. shutdown of readable side of the TCP connection.)
|
||||
*
|
||||
* Requests without `Content-Length` and other messages might require treating
|
||||
* all incoming bytes as the part of the body, up to the last byte of the
|
||||
* connection. This method will invoke `on_message_complete()` callback if the
|
||||
* request was terminated safely. Otherwise a error code would be returned.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
llhttp_errno_t llhttp_finish(llhttp_t* parser);
|
||||
|
||||
/* Returns `1` if the incoming message is parsed until the last byte, and has
|
||||
* to be completed by calling `llhttp_finish()` on EOF
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
int llhttp_message_needs_eof(const llhttp_t* parser);
|
||||
|
||||
/* Returns `1` if there might be any other messages following the last that was
|
||||
* successfully parsed.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
int llhttp_should_keep_alive(const llhttp_t* parser);
|
||||
|
||||
/* Make further calls of `llhttp_execute()` return `HPE_PAUSED` and set
|
||||
* appropriate error reason.
|
||||
*
|
||||
* Important: do not call this from user callbacks! User callbacks must return
|
||||
* `HPE_PAUSED` if pausing is required.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_pause(llhttp_t* parser);
|
||||
|
||||
/* Might be called to resume the execution after the pause in user's callback.
|
||||
* See `llhttp_execute()` above for details.
|
||||
*
|
||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED`.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_resume(llhttp_t* parser);
|
||||
|
||||
/* Might be called to resume the execution after the pause in user's callback.
|
||||
* See `llhttp_execute()` above for details.
|
||||
*
|
||||
* Call this only if `llhttp_execute()` returns `HPE_PAUSED_UPGRADE`
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_resume_after_upgrade(llhttp_t* parser);
|
||||
|
||||
/* Returns the latest return error */
|
||||
LLHTTP_EXPORT
|
||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser);
|
||||
|
||||
/* Returns the verbal explanation of the latest returned error.
|
||||
*
|
||||
* Note: User callback should set error reason when returning the error. See
|
||||
* `llhttp_set_error_reason()` for details.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
const char* llhttp_get_error_reason(const llhttp_t* parser);
|
||||
|
||||
/* Assign verbal description to the returned error. Must be called in user
|
||||
* callbacks right before returning the errno.
|
||||
*
|
||||
* Note: `HPE_USER` error code might be useful in user callbacks.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason);
|
||||
|
||||
/* Returns the pointer to the last parsed byte before the returned error. The
|
||||
* pointer is relative to the `data` argument of `llhttp_execute()`.
|
||||
*
|
||||
* Note: this method might be useful for counting the number of parsed bytes.
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
const char* llhttp_get_error_pos(const llhttp_t* parser);
|
||||
|
||||
/* Returns textual name of error code */
|
||||
LLHTTP_EXPORT
|
||||
const char* llhttp_errno_name(llhttp_errno_t err);
|
||||
|
||||
/* Returns textual name of HTTP method */
|
||||
LLHTTP_EXPORT
|
||||
const char* llhttp_method_name(llhttp_method_t method);
|
||||
|
||||
/* Returns textual name of HTTP status */
|
||||
LLHTTP_EXPORT
|
||||
const char* llhttp_status_name(llhttp_status_t status);
|
||||
|
||||
/* Enables/disables lenient header value parsing (disabled by default).
|
||||
*
|
||||
* Lenient parsing disables header value token checks, extending llhttp's
|
||||
* protocol support to highly non-compliant clients/server. No
|
||||
* `HPE_INVALID_HEADER_TOKEN` will be raised for incorrect header values when
|
||||
* lenient parsing is "on".
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled);
|
||||
|
||||
|
||||
/* Enables/disables lenient handling of conflicting `Transfer-Encoding` and
|
||||
* `Content-Length` headers (disabled by default).
|
||||
*
|
||||
* Normally `llhttp` would error when `Transfer-Encoding` is present in
|
||||
* conjunction with `Content-Length`. This error is important to prevent HTTP
|
||||
* request smuggling, but may be less desirable for small number of cases
|
||||
* involving legacy servers.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled);
|
||||
|
||||
|
||||
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0
|
||||
* requests responses.
|
||||
*
|
||||
* Normally `llhttp` would error on (in strict mode) or discard (in loose mode)
|
||||
* the HTTP request/response after the request/response with `Connection: close`
|
||||
* and `Content-Length`. This is important to prevent cache poisoning attacks,
|
||||
* but might interact badly with outdated and insecure clients. With this flag
|
||||
* the extra request/response will be parsed normally.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* poisoning attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of `Transfer-Encoding` header.
|
||||
*
|
||||
* Normally `llhttp` would error when a `Transfer-Encoding` has `chunked` value
|
||||
* and another value after it (either in a single header or in multiple
|
||||
* headers whose value are internally joined using `, `).
|
||||
* This is mandated by the spec to reliably determine request body size and thus
|
||||
* avoid request smuggling.
|
||||
* With this flag the extra value will be parsed normally.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of HTTP version.
|
||||
*
|
||||
* Normally `llhttp` would error when the HTTP version in the request or status line
|
||||
* is not `0.9`, `1.0`, `1.1` or `2.0`.
|
||||
* With this flag the invalid value will be parsed normally.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will allow unsupported
|
||||
* HTTP versions. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_version(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of additional data received after a message ends
|
||||
* and keep-alive is disabled.
|
||||
*
|
||||
* Normally `llhttp` would error when additional unexpected data is received if the message
|
||||
* contains the `Connection` header with `close` value.
|
||||
* With this flag the extra data will discarded without throwing an error.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* poisoning attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of incomplete CRLF sequences.
|
||||
*
|
||||
* Normally `llhttp` would error when a CR is not followed by LF when terminating the
|
||||
* request line, the status line, the headers or a chunk header.
|
||||
* With this flag only a CR is required to terminate such sections.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled);
|
||||
|
||||
/*
|
||||
* Enables/disables lenient handling of line separators.
|
||||
*
|
||||
* Normally `llhttp` would error when a LF is not preceded by CR when terminating the
|
||||
* request line, the status line, the headers, a chunk header or a chunk data.
|
||||
* With this flag only a LF is required to terminate such sections.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of chunks not separated via CRLF.
|
||||
*
|
||||
* Normally `llhttp` would error when after a chunk data a CRLF is missing before
|
||||
* starting a new chunk.
|
||||
* With this flag the new chunk can start immediately after the previous one.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled);
|
||||
|
||||
/* Enables/disables lenient handling of spaces after chunk size.
|
||||
*
|
||||
* Normally `llhttp` would error when after a chunk size is followed by one or more
|
||||
* spaces are present instead of a CRLF or `;`.
|
||||
* With this flag this check is disabled.
|
||||
*
|
||||
* **Enabling this flag can pose a security issue since you will be exposed to
|
||||
* request smuggling attacks. USE WITH CAUTION!**
|
||||
*/
|
||||
LLHTTP_EXPORT
|
||||
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
#endif /* INCLUDE_LLHTTP_API_H_ */
|
||||
|
||||
|
||||
#endif /* INCLUDE_LLHTTP_H_ */
|
||||
520
common/http/llhttp_api.c
Normal file
520
common/http/llhttp_api.c
Normal file
@@ -0,0 +1,520 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "llhttp.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#endif
|
||||
|
||||
#define CALLBACK_MAYBE(PARSER, NAME) \
|
||||
do { \
|
||||
const llhttp_settings_t* settings; \
|
||||
settings = (const llhttp_settings_t*) (PARSER)->settings; \
|
||||
if (settings == NULL || settings->NAME == NULL) { \
|
||||
err = 0; \
|
||||
break; \
|
||||
} \
|
||||
err = settings->NAME((PARSER)); \
|
||||
} while (0)
|
||||
|
||||
#define SPAN_CALLBACK_MAYBE(PARSER, NAME, START, LEN) \
|
||||
do { \
|
||||
const llhttp_settings_t* settings; \
|
||||
settings = (const llhttp_settings_t*) (PARSER)->settings; \
|
||||
if (settings == NULL || settings->NAME == NULL) { \
|
||||
err = 0; \
|
||||
break; \
|
||||
} \
|
||||
err = settings->NAME((PARSER), (START), (LEN)); \
|
||||
if (err == -1) { \
|
||||
err = HPE_USER; \
|
||||
llhttp_set_error_reason((PARSER), "Span callback error in " #NAME); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void llhttp_init(llhttp_t* parser, llhttp_type_t type,
|
||||
const llhttp_settings_t* settings) {
|
||||
llhttp__internal_init(parser);
|
||||
|
||||
parser->type = type;
|
||||
parser->settings = (void*) settings;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__wasm__)
|
||||
|
||||
extern int wasm_on_message_begin(llhttp_t * p);
|
||||
extern int wasm_on_url(llhttp_t* p, const char* at, size_t length);
|
||||
extern int wasm_on_status(llhttp_t* p, const char* at, size_t length);
|
||||
extern int wasm_on_header_field(llhttp_t* p, const char* at, size_t length);
|
||||
extern int wasm_on_header_value(llhttp_t* p, const char* at, size_t length);
|
||||
extern int wasm_on_headers_complete(llhttp_t * p, int status_code,
|
||||
uint8_t upgrade, int should_keep_alive);
|
||||
extern int wasm_on_body(llhttp_t* p, const char* at, size_t length);
|
||||
extern int wasm_on_message_complete(llhttp_t * p);
|
||||
|
||||
static int wasm_on_headers_complete_wrap(llhttp_t* p) {
|
||||
return wasm_on_headers_complete(p, p->status_code, p->upgrade,
|
||||
llhttp_should_keep_alive(p));
|
||||
}
|
||||
|
||||
const llhttp_settings_t wasm_settings = {
|
||||
wasm_on_message_begin,
|
||||
wasm_on_url,
|
||||
wasm_on_status,
|
||||
NULL,
|
||||
NULL,
|
||||
wasm_on_header_field,
|
||||
wasm_on_header_value,
|
||||
NULL,
|
||||
NULL,
|
||||
wasm_on_headers_complete_wrap,
|
||||
wasm_on_body,
|
||||
wasm_on_message_complete,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
llhttp_t* llhttp_alloc(llhttp_type_t type) {
|
||||
llhttp_t* parser = malloc(sizeof(llhttp_t));
|
||||
llhttp_init(parser, type, &wasm_settings);
|
||||
return parser;
|
||||
}
|
||||
|
||||
void llhttp_free(llhttp_t* parser) {
|
||||
free(parser);
|
||||
}
|
||||
|
||||
#endif // defined(__wasm__)
|
||||
|
||||
/* Some getters required to get stuff from the parser */
|
||||
|
||||
uint8_t llhttp_get_type(llhttp_t* parser) {
|
||||
return parser->type;
|
||||
}
|
||||
|
||||
uint8_t llhttp_get_http_major(llhttp_t* parser) {
|
||||
return parser->http_major;
|
||||
}
|
||||
|
||||
uint8_t llhttp_get_http_minor(llhttp_t* parser) {
|
||||
return parser->http_minor;
|
||||
}
|
||||
|
||||
uint8_t llhttp_get_method(llhttp_t* parser) {
|
||||
return parser->method;
|
||||
}
|
||||
|
||||
int llhttp_get_status_code(llhttp_t* parser) {
|
||||
return parser->status_code;
|
||||
}
|
||||
|
||||
uint8_t llhttp_get_upgrade(llhttp_t* parser) {
|
||||
return parser->upgrade;
|
||||
}
|
||||
|
||||
|
||||
void llhttp_reset(llhttp_t* parser) {
|
||||
llhttp_type_t type = parser->type;
|
||||
const llhttp_settings_t* settings = parser->settings;
|
||||
void* data = parser->data;
|
||||
uint16_t lenient_flags = parser->lenient_flags;
|
||||
|
||||
llhttp__internal_init(parser);
|
||||
|
||||
parser->type = type;
|
||||
parser->settings = (void*) settings;
|
||||
parser->data = data;
|
||||
parser->lenient_flags = lenient_flags;
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_execute(llhttp_t* parser, const char* data, size_t len) {
|
||||
return llhttp__internal_execute(parser, data, data + len);
|
||||
}
|
||||
|
||||
|
||||
void llhttp_settings_init(llhttp_settings_t* settings) {
|
||||
memset(settings, 0, sizeof(*settings));
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_finish(llhttp_t* parser) {
|
||||
int err;
|
||||
|
||||
/* We're in an error state. Don't bother doing anything. */
|
||||
if (parser->error != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (parser->finish) {
|
||||
case HTTP_FINISH_SAFE_WITH_CB:
|
||||
CALLBACK_MAYBE(parser, on_message_complete);
|
||||
if (err != HPE_OK) return err;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case HTTP_FINISH_SAFE:
|
||||
return HPE_OK;
|
||||
case HTTP_FINISH_UNSAFE:
|
||||
parser->reason = "Invalid EOF state";
|
||||
return HPE_INVALID_EOF_STATE;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void llhttp_pause(llhttp_t* parser) {
|
||||
if (parser->error != HPE_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = HPE_PAUSED;
|
||||
parser->reason = "Paused";
|
||||
}
|
||||
|
||||
|
||||
void llhttp_resume(llhttp_t* parser) {
|
||||
if (parser->error != HPE_PAUSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = 0;
|
||||
}
|
||||
|
||||
|
||||
void llhttp_resume_after_upgrade(llhttp_t* parser) {
|
||||
if (parser->error != HPE_PAUSED_UPGRADE) {
|
||||
return;
|
||||
}
|
||||
|
||||
parser->error = 0;
|
||||
}
|
||||
|
||||
|
||||
llhttp_errno_t llhttp_get_errno(const llhttp_t* parser) {
|
||||
return parser->error;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_get_error_reason(const llhttp_t* parser) {
|
||||
return parser->reason;
|
||||
}
|
||||
|
||||
|
||||
void llhttp_set_error_reason(llhttp_t* parser, const char* reason) {
|
||||
parser->reason = reason;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_get_error_pos(const llhttp_t* parser) {
|
||||
return parser->error_pos;
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_errno_name(llhttp_errno_t err) {
|
||||
#define HTTP_ERRNO_GEN(CODE, NAME, _) case HPE_##NAME: return "HPE_" #NAME;
|
||||
switch (err) {
|
||||
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
|
||||
default: abort();
|
||||
}
|
||||
#undef HTTP_ERRNO_GEN
|
||||
}
|
||||
|
||||
|
||||
const char* llhttp_method_name(llhttp_method_t method) {
|
||||
#define HTTP_METHOD_GEN(NUM, NAME, STRING) case HTTP_##NAME: return #STRING;
|
||||
switch (method) {
|
||||
HTTP_ALL_METHOD_MAP(HTTP_METHOD_GEN)
|
||||
default: abort();
|
||||
}
|
||||
#undef HTTP_METHOD_GEN
|
||||
}
|
||||
|
||||
const char* llhttp_status_name(llhttp_status_t status) {
|
||||
#define HTTP_STATUS_GEN(NUM, NAME, STRING) case HTTP_STATUS_##NAME: return #STRING;
|
||||
switch (status) {
|
||||
HTTP_STATUS_MAP(HTTP_STATUS_GEN)
|
||||
default: abort();
|
||||
}
|
||||
#undef HTTP_STATUS_GEN
|
||||
}
|
||||
|
||||
|
||||
void llhttp_set_lenient_headers(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_HEADERS;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_HEADERS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void llhttp_set_lenient_chunked_length(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_CHUNKED_LENGTH;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_CHUNKED_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void llhttp_set_lenient_keep_alive(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_KEEP_ALIVE;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_KEEP_ALIVE;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_transfer_encoding(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_TRANSFER_ENCODING;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_TRANSFER_ENCODING;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_version(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_VERSION;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_data_after_close(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_DATA_AFTER_CLOSE;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_DATA_AFTER_CLOSE;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_optional_lf_after_cr(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_OPTIONAL_LF_AFTER_CR;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_OPTIONAL_LF_AFTER_CR;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_optional_crlf_after_chunk(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_OPTIONAL_CRLF_AFTER_CHUNK;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_optional_cr_before_lf(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_OPTIONAL_CR_BEFORE_LF;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_OPTIONAL_CR_BEFORE_LF;
|
||||
}
|
||||
}
|
||||
|
||||
void llhttp_set_lenient_spaces_after_chunk_size(llhttp_t* parser, int enabled) {
|
||||
if (enabled) {
|
||||
parser->lenient_flags |= LENIENT_SPACES_AFTER_CHUNK_SIZE;
|
||||
} else {
|
||||
parser->lenient_flags &= ~LENIENT_SPACES_AFTER_CHUNK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
|
||||
int llhttp__on_message_begin(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_message_begin);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_url(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_url, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_url_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_url_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_status(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_status, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_status_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_status_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_method(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_method, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_method_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_method_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_version(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_version, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_version_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_version_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_field(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_header_field, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_field_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_header_field_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_value(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_header_value, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_header_value_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_header_value_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_headers_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_headers_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_message_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_message_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_body(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_body, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_header(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_header);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_extension_name(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_name, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_extension_name_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_extension_name_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_extension_value(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
SPAN_CALLBACK_MAYBE(s, on_chunk_extension_value, p, endp - p);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_extension_value_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_extension_value_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_chunk_complete(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_chunk_complete);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int llhttp__on_reset(llhttp_t* s, const char* p, const char* endp) {
|
||||
int err;
|
||||
CALLBACK_MAYBE(s, on_reset);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* Private */
|
||||
|
||||
|
||||
void llhttp__debug(llhttp_t* s, const char* p, const char* endp,
|
||||
const char* msg) {
|
||||
if (p == endp) {
|
||||
fprintf(stderr, "p=%p type=%d flags=%02x next=null debug=%s\n", s, s->type,
|
||||
s->flags, msg);
|
||||
} else {
|
||||
fprintf(stderr, "p=%p type=%d flags=%02x next=%02x debug=%s\n", s,
|
||||
s->type, s->flags, *p, msg);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user