This commit is contained in:
NH
2025-04-17 20:38:35 +08:00
commit 40504ab11f
117 changed files with 62822 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@@ -0,0 +1,6 @@
################################################################################
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
################################################################################
/.vs
/out/build/x64-Debug

40
ArqHelper.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

496
HPThreadPool.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

615
HttpServer.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

496
SSLHelper.h Normal file
View 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 -- 线程 ID0当前线程
*
* 返回值:无
*/
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
View 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
View 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

File diff suppressed because it is too large Load Diff

959
SocketHelper.h Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

306
TcpAgent.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

306
TcpServer.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

301
UdpServer.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1068
common/STLHelper.h Normal file

File diff suppressed because it is too large Load Diff

117
common/Semaphore.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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